From eafb26d4623b80e7ba7f313614443c264b28c269 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Wed, 20 Mar 2024 15:26:38 +0530 Subject: [PATCH 01/26] feat: restrict route if user subscription is expired --- apps/OpenSign/src/layout/HomeLayout.js | 60 ++++++++++++++++++--- apps/OpenSign/src/pages/Login.js | 73 +++++++++++++++++++++++--- 2 files changed, 118 insertions(+), 15 deletions(-) diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index d3cfd8188..84cd51f0f 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -18,6 +18,12 @@ const HomeLayout = () => { const arr = useSelector((state) => state.TourSteps); const [isUserValid, setIsUserValid] = useState(true); const [isLoader, setIsLoader] = useState(true); + // reactour state + const [isCloseBtn, setIsCloseBtn] = useState(true); + const [isTour, setIsTour] = useState(false); + const [tourStatusArr, setTourStatusArr] = useState([]); + const [tourConfigs, setTourConfigs] = useState([]); + useEffect(() => { (async () => { try { @@ -27,7 +33,7 @@ const HomeLayout = () => { sessionToken: localStorage.getItem("accesstoken") }); if (user) { - setIsUserValid(true); + checkIsSubscribed(); setIsLoader(false); } else { setIsUserValid(false); @@ -37,14 +43,54 @@ const HomeLayout = () => { setIsUserValid(false); } })(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // reactour state - const [isCloseBtn, setIsCloseBtn] = useState(true); - const [isTour, setIsTour] = useState(false); - const [tourStatusArr, setTourStatusArr] = useState([]); - const [tourConfigs, setTourConfigs] = useState([]); - + async function checkIsSubscribed() { + const currentUser = Parse.User.current(); + // const userSettings = appInfo.settings; + // const setting = userSettings.find((x) => x.role === _currentRole); + // const redirectUrl = + // location?.state?.from || `/${setting.pageType}/${setting.pageId}`; + const user = await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }); + const _user = user.toJSON(); + if (_user.TenantId) { + localStorage.setItem("TenetId", _user.TenantId.objectId); + } + // localStorage.setItem("PageLanding", setting.pageId); + // localStorage.setItem("defaultmenuid", setting.menuId); + // localStorage.setItem("pageType", setting.pageType); + if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + // const LocalUserDetails = { + // name: _user.Name, + // email: _user.Email, + // phone: _user.Phone, + // company: _user.Company + // }; + // localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails)); + const billingDate = _user.Next_billing_date && _user.Next_billing_date; + if (billingDate) { + if (billingDate > new Date()) { + // localStorage.removeItem("userDetails"); + // Redirect to the appropriate URL after successful login + // navigate(redirectUrl); + setIsUserValid(true); + return true; + } else { + navigate(`/subscription`); + } + } else { + navigate(`/subscription`); + } + } else { + setIsUserValid(true); + return true; + // Redirect to the appropriate URL after successful login + // navigate(redirectUrl); + } + } const showSidebar = () => { setIsOpen((value) => !value); }; diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 7ed464696..8bbbce23f 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -745,6 +745,13 @@ function Login() { // Get TenentID from Extendend Class localStorage.setItem("extended_class", item.extended_class); const currentUser = Parse.User.current(); + const userSettings = appInfo.settings; + const setting = userSettings.find( + (x) => x.role === _currentRole + ); + const redirectUrl = + location?.state?.from || + `/${setting.pageType}/${setting.pageId}`; await Parse.Cloud.run("getUserDetails", { email: currentUser.get("email") }).then( @@ -783,16 +790,66 @@ function Login() { tenentInfo.push(obj); } }); - localStorage.setItem("PageLanding", item.pageId); - localStorage.setItem("defaultmenuid", item.menuId); - localStorage.setItem("pageType", item.pageType); - navigate(`/${item.pageType}/${item.pageId}`); + localStorage.setItem("PageLanding", setting.pageId); + localStorage.setItem( + "defaultmenuid", + setting.menuId + ); + localStorage.setItem("pageType", setting.pageType); + if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + const LocalUserDetails = { + name: results[0].get("Name"), + email: results[0].get("Email"), + phone: results[0].get("Phone"), + company: results[0].get("Company") + }; + localStorage.setItem( + "userDetails", + JSON.stringify(LocalUserDetails) + ); + const billingDate = + results[0].get("Next_billing_date") && + results[0].get("Next_billing_date"); + if (billingDate) { + if (billingDate > new Date()) { + localStorage.removeItem("userDetails"); + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } else { + navigate(`/subscription`); + } + } else { + navigate(`/subscription`); + } + } else { + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } } } else { - localStorage.setItem("PageLanding", item.pageId); - localStorage.setItem("defaultmenuid", item.menuId); - localStorage.setItem("pageType", item.pageType); - navigate(`/${item.pageType}/${item.pageId}`); + localStorage.setItem("PageLanding", setting.pageId); + localStorage.setItem("defaultmenuid", setting.menuId); + localStorage.setItem("pageType", setting.pageType); + setState({ ...state, loading: false }); + if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + const LocalUserDetails = { + name: results[0].get("Name"), + email: results[0].get("Email"), + phone: results[0].get("Phone") + // company: results.get("Company"), + }; + localStorage.setItem( + "userDetails", + JSON.stringify(LocalUserDetails) + ); + const billingDate = ""; + if (billingDate) { + navigate(`/subscription`); + } + } else { + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } } }, (error) => { From 3115ff3e2a978401e1dee4f1ff67549c15119a5f Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Wed, 27 Mar 2024 13:11:20 +0530 Subject: [PATCH 02/26] fix: restrict user from logging in when next renewal date is expired even when route is entered directly --- apps/OpenSign/src/layout/HomeLayout.js | 25 +++-------------- apps/OpenSign/src/pages/PlanSubscriptions.js | 16 +++++------ apps/OpenSign/src/primitives/Validate.js | 28 +++++++++++++++++++- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index b13a51a6a..43565a80f 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -34,7 +34,6 @@ const HomeLayout = () => { }); if (user) { checkIsSubscribed(); - setIsLoader(false); } else { setIsUserValid(false); } @@ -48,10 +47,6 @@ const HomeLayout = () => { async function checkIsSubscribed() { const currentUser = Parse.User.current(); - // const userSettings = appInfo.settings; - // const setting = userSettings.find((x) => x.role === _currentRole); - // const redirectUrl = - // location?.state?.from || `/${setting.pageType}/${setting.pageId}`; const user = await Parse.Cloud.run("getUserDetails", { email: currentUser.get("email") }); @@ -59,24 +54,12 @@ const HomeLayout = () => { if (_user.TenantId) { localStorage.setItem("TenetId", _user.TenantId.objectId); } - // localStorage.setItem("PageLanding", setting.pageId); - // localStorage.setItem("defaultmenuid", setting.menuId); - // localStorage.setItem("pageType", setting.pageType); if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { - // const LocalUserDetails = { - // name: _user.Name, - // email: _user.Email, - // phone: _user.Phone, - // company: _user.Company - // }; - // localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails)); const billingDate = _user.Next_billing_date && _user.Next_billing_date; if (billingDate) { if (billingDate > new Date()) { - // localStorage.removeItem("userDetails"); - // Redirect to the appropriate URL after successful login - // navigate(redirectUrl); setIsUserValid(true); + setIsLoader(false); return true; } else { navigate(`/subscription`); @@ -86,9 +69,8 @@ const HomeLayout = () => { } } else { setIsUserValid(true); + setIsLoader(false); return true; - // Redirect to the appropriate URL after successful login - // navigate(redirectUrl); } } const showSidebar = () => { @@ -244,7 +226,7 @@ const HomeLayout = () => { return (
-
+ {!isLoader &&
}
{isUserValid ? ( <> @@ -269,7 +251,6 @@ const HomeLayout = () => { <>
-
{ - const navigate = useNavigate(); + // const navigate = useNavigate(); const [yearlyVisible, setYearlyVisible] = useState(false); const [isLoader, setIsLoader] = useState(true); @@ -35,12 +35,12 @@ const PlanSubscriptions = () => { company + phone; useEffect(() => { - if (localStorage.getItem("accesstoken")) { + // if (localStorage.getItem("accesstoken")) { setIsLoader(false); setYearlyVisible(false); - } else { - navigate("/", { replace: true }); - } + // } else { + // navigate("/", { replace: true }); + // } // eslint-disable-next-line }, []); @@ -108,7 +108,7 @@ const PlanSubscriptions = () => { : "" } text-sm text-center my-2`} > -

{ ) : ( {item.subtitle} )} -

+
diff --git a/apps/OpenSign/src/primitives/Validate.js b/apps/OpenSign/src/primitives/Validate.js index 86993d8b3..21fd208c5 100644 --- a/apps/OpenSign/src/primitives/Validate.js +++ b/apps/OpenSign/src/primitives/Validate.js @@ -15,7 +15,7 @@ const Validate = () => { sessionToken: localStorage.getItem("accesstoken") }); if (user) { - setIsUserValid(true); + checkIsSubscribed(); } else { setIsUserValid(false); } @@ -24,7 +24,33 @@ const Validate = () => { setIsUserValid(false); } })(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + + async function checkIsSubscribed() { + const currentUser = Parse.User.current(); + console.log("currentUser ", currentUser) + const user = await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }); + const _user = user?.toJSON(); + + if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + const billingDate = _user.Next_billing_date && _user.Next_billing_date; + if (billingDate) { + if (billingDate > new Date()) { + setIsUserValid(true); + return true; + } else { + // navigate(`/subscription`); + } + } else { + // navigate(`/subscription`); + } + } else { + setIsUserValid(true); + } + } const handleLoginBtn = () => { try { Parse.User.logOut(); From 74bfd39abc25501e0d7c7ca05445a3f728572668 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Wed, 27 Mar 2024 16:07:26 +0530 Subject: [PATCH 03/26] feat: restrict guest route --- apps/OpenSign/src/App.js | 3 +- apps/OpenSign/src/constant/const.js | 1 + apps/OpenSign/src/layout/HomeLayout.js | 11 +- apps/OpenSign/src/pages/Login.js | 23 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 923 ++++++++++-------- apps/OpenSign/src/pages/PlaceHolderSign.js | 22 +- apps/OpenSign/src/pages/Signup.js | 13 +- .../OpenSign/src/pages/TemplatePlaceholder.js | 23 +- apps/OpenSign/src/primitives/Validate.js | 26 +- apps/OpenSignServer/index.js | 8 + 10 files changed, 564 insertions(+), 489 deletions(-) diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 7f4f587b1..929008576 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -15,6 +15,7 @@ import DraftDocument from "./components/pdf/DraftDocument"; import PlaceHolderSign from "./pages/PlaceHolderSign"; import PdfRequestFiles from "./pages/PdfRequestFiles"; import LazyPage from "./primitives/LazyPage"; +import { isEnableSubscription } from "./constant/const"; const DebugPdf = lazy(() => import("./pages/DebugPdf")); const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const GuestLogin = lazy(() => import("./pages/GuestLogin")); @@ -113,7 +114,7 @@ function App() { path="/forgetpassword" element={} /> - {process.env.REACT_APP_ENABLE_SUBSCRIPTION && ( + {isEnableSubscription && ( <> { const navigate = useNavigate(); @@ -50,12 +51,10 @@ const HomeLayout = () => { const user = await Parse.Cloud.run("getUserDetails", { email: currentUser.get("email") }); - const _user = user.toJSON(); - if (_user.TenantId) { - localStorage.setItem("TenetId", _user.TenantId.objectId); - } - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { - const billingDate = _user.Next_billing_date && _user.Next_billing_date; + if (isEnableSubscription) { + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (billingDate) { if (billingDate > new Date()) { setIsUserValid(true); diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 8bbbce23f..8ddccaa1c 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -10,7 +10,11 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom"; import login_img from "../assets/images/login_img.svg"; import { useWindowSize } from "../hook/useWindowSize"; import ModalUi from "../primitives/ModalUi"; -import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const"; +import { + isEnableSubscription, + modalCancelBtnColor, + modalSubmitBtnColor +} from "../constant/const"; import Alert from "../primitives/Alert"; import { appInfo } from "../constant/appinfo"; import { fetchAppInfo } from "../redux/reducers/infoReducer"; @@ -239,10 +243,7 @@ function Login() { element.pageType ); setState({ ...state, loading: false }); - if ( - process.env - .REACT_APP_ENABLE_SUBSCRIPTION - ) { + if (isEnableSubscription) { const LocalUserDetails = { name: results[0].get("Name"), email: results[0].get("Email"), @@ -292,9 +293,7 @@ function Login() { element.pageType ); setState({ ...state, loading: false }); - if ( - process.env.REACT_APP_ENABLE_SUBSCRIPTION - ) { + if (isEnableSubscription) { const LocalUserDetails = { name: _user.name, email: email, @@ -571,7 +570,7 @@ function Login() { ); setThirdpartyLoader(false); setState({ ...state, loading: false }); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { if (billingDate) { if (billingDate > new Date()) { localStorage.removeItem("userDetails"); @@ -597,7 +596,7 @@ function Login() { localStorage.setItem("pageType", element.pageType); setState({ ...state, loading: false }); setThirdpartyLoader(false); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { if (billingDate) { if (billingDate > new Date()) { localStorage.removeItem("userDetails"); @@ -796,7 +795,7 @@ function Login() { setting.menuId ); localStorage.setItem("pageType", setting.pageType); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { const LocalUserDetails = { name: results[0].get("Name"), email: results[0].get("Email"), @@ -831,7 +830,7 @@ function Login() { localStorage.setItem("defaultmenuid", setting.menuId); localStorage.setItem("pageType", setting.pageType); setState({ ...state, loading: false }); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { const LocalUserDetails = { name: results[0].get("Name"), email: results[0].get("Email"), diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 7f2bf551d..d063a3c35 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1,12 +1,12 @@ import React, { useState, useRef, useEffect } from "react"; -import { themeColor } from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import { PDFDocument } from "pdf-lib"; import "../styles/signature.css"; import axios from "axios"; import loader from "../assets/images/loader2.gif"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; -import { useParams } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; import SignPad from "../components/pdf/SignPad"; import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import Tour from "reactour"; @@ -30,9 +30,12 @@ import PdfDeclineModal from "../primitives/PdfDeclineModal"; import Title from "../components/Title"; import DefaultSignature from "../components/pdf/DefaultSignature"; import ModalUi from "../primitives/ModalUi"; +import Parse from "parse"; function PdfRequestFiles() { const { docId } = useParams(); + const navigate = useNavigate(); + const location = useLocation(); const [pdfDetails, setPdfDetails] = useState([]); const [signedSigners, setSignedSigners] = useState([]); const [unsignedSigners, setUnSignedSigners] = useState([]); @@ -87,6 +90,7 @@ function PdfRequestFiles() { const [widgetsTour, setWidgetsTour] = useState(false); const [minRequiredCount, setminRequiredCount] = useState(); const [sendInOrder, setSendInOrder] = useState(false); + const [isSubscribed, setIsSubscribed] = useState(false); const divRef = useRef(null); const isMobile = window.innerWidth < 767; const rowLevel = @@ -126,12 +130,39 @@ function PdfRequestFiles() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [divRef.current]); + async function checkIsSubscribed(email) { + const user = await Parse.Cloud.run("getUserDetails", { + email: email + }); + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (billingDate) { + if (billingDate > new Date()) { + return true; + } else { + if (location.pathname.includes("/load/")) { + setIsSubscribed(true); + } else { + navigate(`/subscription`); + } + } + } else { + if (location.pathname.includes("/load/")) { + setIsSubscribed(true); + } else { + navigate(`/subscription`); + } + } + } //function for get document details for perticular signer with signer'object id const getDocumentDetails = async () => { let currUserId; //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { + if (isEnableSubscription) { + await checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); + } const isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted; const expireDate = documentData[0].ExpiryDate.iso; @@ -307,7 +338,7 @@ function PdfRequestFiles() { `${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( "_appName" )}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${ - jsonSender.objectId + jsonSender?.objectId }"}}`, { headers: { @@ -894,152 +925,80 @@ function PdfRequestFiles() { return ( - {isLoading.isLoad ? ( - <Loader isLoading={isLoading} /> - ) : handleError ? ( - <HandleError handleError={handleError} /> + {isSubscribed ? ( + <ModalUi + title={"Subscription Expired"} + isOpen={isSubscribed} + showClose={false} + > + <div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5"> + <p className="text-sm md:text-lg font-normal"> + Owner's subscription has expired. + </p> + </div> + </ModalUi> ) : ( - <div> - {isUiLoading && ( - <div - style={{ - position: "absolute", - height: "100vh", - width: "100%", - display: "flex", - justifyContent: "center", - flexDirection: "column", - alignItems: "center", - zIndex: "999", - backgroundColor: "#e6f2f2", - opacity: 0.8 - }} - > - <img - alt="no img" - src={loader} - style={{ width: "100px", height: "100px" }} - /> - <span style={{ fontSize: "13px", fontWeight: "bold" }}> - This might take some time - </span> - </div> - )} - - <div className="signatureContainer" ref={divRef}> - <ModalUi - headerColor={"#dc3545"} - isOpen={isAlert.isShow} - title={"Alert message"} - handleClose={() => { - setIsAlert({ - isShow: false, - alertMessage: "" - }); - }} - > - <div style={{ height: "100%", padding: 20 }}> - <p>{isAlert.alertMessage}</p> - + <> + {isLoading.isLoad ? ( + <Loader isLoading={isLoading} /> + ) : handleError ? ( + <HandleError handleError={handleError} /> + ) : ( + <div> + {isUiLoading && ( <div style={{ - height: "1px", - backgroundColor: "#9f9f9f", + position: "absolute", + height: "100vh", width: "100%", - marginTop: "15px", - marginBottom: "15px" + display: "flex", + justifyContent: "center", + flexDirection: "column", + alignItems: "center", + zIndex: "999", + backgroundColor: "#e6f2f2", + opacity: 0.8 }} - ></div> + > + <img + alt="no img" + src={loader} + style={{ width: "100px", height: "100px" }} + /> + <span style={{ fontSize: "13px", fontWeight: "bold" }}> + This might take some time + </span> + </div> + )} - <button - onClick={() => { + <div className="signatureContainer" ref={divRef}> + <ModalUi + headerColor={"#dc3545"} + isOpen={isAlert.isShow} + title={"Alert message"} + handleClose={() => { setIsAlert({ isShow: false, alertMessage: "" }); }} - type="button" - className="finishBtn cancelBtn" > - Ok - </button> - </div> - </ModalUi> + <div style={{ height: "100%", padding: 20 }}> + <p>{isAlert.alertMessage}</p> - <Tour - showNumber={false} - showNavigation={false} - showNavigationNumber={false} - onRequestClose={closeTour} - steps={tourConfig} - isOpen={widgetsTour} - rounded={5} - closeWithMask={false} - /> - - {/* this modal is used to show decline alert */} - <PdfDeclineModal - containerWH={containerWH} - show={isDecline.isDeclined} - headMsg="Document Declined" - bodyMssg={ - isDecline.currnt === "Sure" - ? "Are you sure want to decline this document ?" - : isDecline.currnt === "YouDeclined" - ? "You have declined this document!" - : isDecline.currnt === "another" && - "You cannot sign this document as it has been declined by one or more recipient(s)." - } - footerMessage={isDecline.currnt === "Sure"} - declineDoc={declineDoc} - setIsDecline={setIsDecline} - /> - {/* this modal is used for show expired alert */} - <PdfDeclineModal - containerWH={containerWH} - show={isExpired} - headMsg="Document Expired!" - bodyMssg="This Document is no longer available." - /> - - <ModalUi - headerColor={defaultSignImg ? themeColor : "#dc3545"} - isOpen={defaultSignAlert.isShow} - title={"Auto sign"} - handleClose={() => { - setDefaultSignAlert({ - isShow: false, - alertMessage: "" - }); - }} - > - <div style={{ height: "100%", padding: 20 }}> - <p>{defaultSignAlert.alertMessage}</p> - - <div - style={{ - height: "1px", - backgroundColor: "#9f9f9f", - width: "100%", - marginTop: "15px", - marginBottom: "15px" - }} - ></div> - {defaultSignImg ? ( - <> - <button - onClick={() => addDefaultSignature()} + <div style={{ - background: themeColor + height: "1px", + backgroundColor: "#9f9f9f", + width: "100%", + marginTop: "15px", + marginBottom: "15px" }} - type="button" - className="finishBtn" - > - Yes - </button> + ></div> + <button onClick={() => { - setDefaultSignAlert({ + setIsAlert({ isShow: false, alertMessage: "" }); @@ -1047,324 +1006,420 @@ function PdfRequestFiles() { type="button" className="finishBtn cancelBtn" > - Close + Ok </button> - </> - ) : ( - <button - onClick={() => { - setIsAlert({ - isShow: false, - alertMessage: "" - }); - }} - type="button" - className="finishBtn cancelBtn" - > - Ok - </button> - )} - </div> - </ModalUi> - {/* this component used to render all pdf pages in left side */} - <RenderAllPdfPage - signPdfUrl={pdfDetails[0].URL} - allPages={allPages} - setAllPages={setAllPages} - setPageNumber={setPageNumber} - pageNumber={pageNumber} - /> + </div> + </ModalUi> - {/* pdf render view */} - <div - style={{ - marginLeft: !isMobile && pdfOriginalWidth > 500 && "20px", - marginRight: !isMobile && pdfOriginalWidth > 500 && "20px" - }} - > - {/* this modal is used show this document is already sign */} - <ModalUi - isOpen={isCompleted.isModal} - title={"Sign Documents"} - handleClose={() => { - setIsCompleted({ isModal: false, isCertificate: true }); - }} - > - <div style={{ height: "100%", padding: 20 }}> - <p>This document has been signed by all Signers.</p> - - <div - style={{ - height: "1px", - backgroundColor: "#9f9f9f", - width: "100%", - marginTop: "15px", - marginBottom: "15px" - }} - ></div> - <button - type="button" - className="finishBtn cancelBtn" - onClick={() => - setIsCompleted({ isModal: false, isCertificate: true }) - } - > - Close - </button> - </div> - </ModalUi> - - {/* this component is used for signature pad modal */} - <SignPad - isSignPad={isSignPad} - isStamp={isStamp} - setIsImageSelect={setIsImageSelect} - setIsSignPad={setIsSignPad} - setImage={setImage} - isImageSelect={isImageSelect} - imageRef={imageRef} - onImageChange={onImageChange} - setSignature={setSignature} - image={image} - onSaveImage={saveImage} - onSaveSign={saveSign} - defaultSign={defaultSignImg} - myInitial={myInitial} - isInitial={isInitial} - setIsInitial={setIsInitial} - setIsStamp={setIsStamp} - /> - {/* pdf header which contain funish back button */} - <Header - isPdfRequestFiles={true} - pageNumber={pageNumber} - allPages={allPages} - changePage={changePage} - pdfDetails={pdfDetails} - signerPos={signerPos} - isSigned={isSigned} - isCompleted={isCompleted} - embedWidgetsData={embedWidgetsData} - isShowHeader={true} - setIsDecline={setIsDecline} - decline={true} - currentSigner={currentSigner} - pdfUrl={pdfUrl} - alreadySign={alreadySign} - /> - {containerWH && ( - <RenderPdf - pageNumber={pageNumber} - pdfOriginalWidth={pdfOriginalWidth} - pdfNewWidth={pdfNewWidth} - setIsSignPad={setIsSignPad} - setIsStamp={setIsStamp} - setSignKey={setSignKey} - pdfDetails={pdfDetails} - signerPos={signerPos} - successEmail={false} - pdfUrl={pdfUrl} - numPages={numPages} - pageDetails={pageDetails} - pdfRequest={true} - signerObjectId={signerObjectId} - signedSigners={signedSigners} - setCurrentSigner={setCurrentSigner} - setPdfLoadFail={setPdfLoadFail} - pdfLoadFail={pdfLoadFail} - setSignerPos={setSignerPos} - containerWH={containerWH} - setIsInitial={setIsInitial} - setValidateAlert={setValidateAlert} - unSignedWidgetId={unSignedWidgetId} - setSelectWidgetId={setSelectWidgetId} - selectWidgetId={selectWidgetId} + <Tour + showNumber={false} + showNavigation={false} + showNavigationNumber={false} + onRequestClose={closeTour} + steps={tourConfig} + isOpen={widgetsTour} + rounded={5} + closeWithMask={false} /> - )} - </div> - <div> - <div className="signerComponent"> - <div - style={{ maxHeight: window.innerHeight - 70 + "px" }} - className="autoSignScroll" - > - {signedSigners.length > 0 && ( - <> - <div - style={{ background: themeColor }} - className="signedStyle" - > - Signed by - </div> - <div style={{ marginTop: "2px" }}> - {signedSigners.map((obj, ind) => { - return ( - <div - style={{ - display: "flex", - flexDirection: "row", - alignItems: "center", - padding: "10px 0", - background: checkSignerBackColor(obj) - }} - key={ind} - > - <div - className="signerStyle" - style={{ - background: "#abd1d0", - width: 30, - height: 30, - display: "flex", - borderRadius: 30 / 2, - justifyContent: "center", - alignItems: "center", - margin: "0 10px 0 5px" - }} - > - <span - style={{ - fontSize: "12px", - textAlign: "center", - fontWeight: "bold", - color: "black", - textTransform: "uppercase" - }} - > - {getFirstLetter(obj.Name)} - </span> - </div> - <div - style={{ - display: "flex", - flexDirection: "column" - }} - > - <span className="userName">{obj.Name}</span> - <span className="useEmail">{obj.Email}</span> - </div> - </div> - ); - })} - </div> - </> - )} - {unsignedSigners.length > 0 && ( - <> + {/* this modal is used to show decline alert */} + <PdfDeclineModal + containerWH={containerWH} + show={isDecline.isDeclined} + headMsg="Document Declined" + bodyMssg={ + isDecline.currnt === "Sure" + ? "Are you sure want to decline this document ?" + : isDecline.currnt === "YouDeclined" + ? "You have declined this document!" + : isDecline.currnt === "another" && + "You cannot sign this document as it has been declined by one or more recipient(s)." + } + footerMessage={isDecline.currnt === "Sure"} + declineDoc={declineDoc} + setIsDecline={setIsDecline} + /> + {/* this modal is used for show expired alert */} + <PdfDeclineModal + containerWH={containerWH} + show={isExpired} + headMsg="Document Expired!" + bodyMssg="This Document is no longer available." + /> + + <ModalUi + headerColor={defaultSignImg ? themeColor : "#dc3545"} + isOpen={defaultSignAlert.isShow} + title={"Auto sign"} + handleClose={() => { + setDefaultSignAlert({ + isShow: false, + alertMessage: "" + }); + }} + > + <div style={{ height: "100%", padding: 20 }}> + <p>{defaultSignAlert.alertMessage}</p> + + <div + style={{ + height: "1px", + backgroundColor: "#9f9f9f", + width: "100%", + marginTop: "15px", + marginBottom: "15px" + }} + ></div> + {defaultSignImg ? ( + <> + <button + onClick={() => addDefaultSignature()} + style={{ + background: themeColor + }} + type="button" + className="finishBtn" + > + Yes + </button> + <button + onClick={() => { + setDefaultSignAlert({ + isShow: false, + alertMessage: "" + }); + }} + type="button" + className="finishBtn cancelBtn" + > + Close + </button> + </> + ) : ( + <button + onClick={() => { + setIsAlert({ + isShow: false, + alertMessage: "" + }); + }} + type="button" + className="finishBtn cancelBtn" + > + Ok + </button> + )} + </div> + </ModalUi> + {/* this component used to render all pdf pages in left side */} + <RenderAllPdfPage + signPdfUrl={pdfDetails[0].URL} + allPages={allPages} + setAllPages={setAllPages} + setPageNumber={setPageNumber} + pageNumber={pageNumber} + /> + + {/* pdf render view */} + <div + style={{ + marginLeft: !isMobile && pdfOriginalWidth > 500 && "20px", + marginRight: !isMobile && pdfOriginalWidth > 500 && "20px" + }} + > + {/* this modal is used show this document is already sign */} + <ModalUi + isOpen={isCompleted.isModal} + title={"Sign Documents"} + handleClose={() => { + setIsCompleted({ isModal: false, isCertificate: true }); + }} + > + <div style={{ height: "100%", padding: 20 }}> + <p>This document has been signed by all Signers.</p> + <div style={{ - background: themeColor, - color: "white", - padding: "5px", - fontFamily: "sans-serif", - marginTop: signedSigners.length > 0 && "20px" + height: "1px", + backgroundColor: "#9f9f9f", + width: "100%", + marginTop: "15px", + marginBottom: "15px" }} + ></div> + <button + type="button" + className="finishBtn cancelBtn" + onClick={() => + setIsCompleted({ + isModal: false, + isCertificate: true + }) + } > - Yet to sign - </div> - <div style={{ marginTop: "5px" }}> - {unsignedSigners.map((obj, ind) => { - return ( - <div - style={{ - display: "flex", - flexDirection: "row", - alignItems: "center", - padding: "10px 0", - background: checkSignerBackColor(obj) - }} - key={ind} - > - <div - className="signerStyle" - style={{ - background: "#abd1d0", - width: 30, - height: 30, - display: "flex", - borderRadius: 30 / 2, - justifyContent: "center", - alignItems: "center", - margin: "0 10px 0 5px" - }} - > - <span - style={{ - fontSize: "12px", - textAlign: "center", - fontWeight: "bold", - color: "black", - textTransform: "uppercase" - }} - > - {getFirstLetter(obj.Name)} - </span> - </div> - <div - style={{ - display: "flex", - flexDirection: "column" - }} - > - <span className="userName">{obj.Name}</span> - <span className="useEmail">{obj.Email}</span> - </div> - <hr /> - </div> - ); - })} - </div> - </> - )} - {defaultSignImg && !alreadySign && ( - <DefaultSignature - defaultSignImg={defaultSignImg} - setDefaultSignImg={setDefaultSignImg} - userObjectId={signerObjectId} - setIsLoading={setIsLoading} - xyPostion={signerPos} - setDefaultSignAlert={setDefaultSignAlert} + Close + </button> + </div> + </ModalUi> + + {/* this component is used for signature pad modal */} + <SignPad + isSignPad={isSignPad} + isStamp={isStamp} + setIsImageSelect={setIsImageSelect} + setIsSignPad={setIsSignPad} + setImage={setImage} + isImageSelect={isImageSelect} + imageRef={imageRef} + onImageChange={onImageChange} + setSignature={setSignature} + image={image} + onSaveImage={saveImage} + onSaveSign={saveSign} + defaultSign={defaultSignImg} + myInitial={myInitial} + isInitial={isInitial} + setIsInitial={setIsInitial} + setIsStamp={setIsStamp} + /> + {/* pdf header which contain funish back button */} + <Header + isPdfRequestFiles={true} + pageNumber={pageNumber} + allPages={allPages} + changePage={changePage} + pdfDetails={pdfDetails} + signerPos={signerPos} + isSigned={isSigned} + isCompleted={isCompleted} + embedWidgetsData={embedWidgetsData} + isShowHeader={true} + setIsDecline={setIsDecline} + decline={true} + currentSigner={currentSigner} + pdfUrl={pdfUrl} + alreadySign={alreadySign} + /> + {containerWH && ( + <RenderPdf + pageNumber={pageNumber} + pdfOriginalWidth={pdfOriginalWidth} + pdfNewWidth={pdfNewWidth} + setIsSignPad={setIsSignPad} + setIsStamp={setIsStamp} + setSignKey={setSignKey} + pdfDetails={pdfDetails} + signerPos={signerPos} + successEmail={false} + pdfUrl={pdfUrl} + numPages={numPages} + pageDetails={pageDetails} + pdfRequest={true} + signerObjectId={signerObjectId} + signedSigners={signedSigners} + setCurrentSigner={setCurrentSigner} + setPdfLoadFail={setPdfLoadFail} + pdfLoadFail={pdfLoadFail} + setSignerPos={setSignerPos} + containerWH={containerWH} + setIsInitial={setIsInitial} + setValidateAlert={setValidateAlert} + unSignedWidgetId={unSignedWidgetId} + setSelectWidgetId={setSelectWidgetId} + selectWidgetId={selectWidgetId} /> )} </div> + <div> + <div className="signerComponent"> + <div + style={{ maxHeight: window.innerHeight - 70 + "px" }} + className="autoSignScroll" + > + {signedSigners.length > 0 && ( + <> + <div + style={{ background: themeColor }} + className="signedStyle" + > + Signed by + </div> + <div style={{ marginTop: "2px" }}> + {signedSigners.map((obj, ind) => { + return ( + <div + style={{ + display: "flex", + flexDirection: "row", + alignItems: "center", + padding: "10px 0", + background: checkSignerBackColor(obj) + }} + key={ind} + > + <div + className="signerStyle" + style={{ + background: "#abd1d0", + width: 30, + height: 30, + display: "flex", + borderRadius: 30 / 2, + justifyContent: "center", + alignItems: "center", + margin: "0 10px 0 5px" + }} + > + <span + style={{ + fontSize: "12px", + textAlign: "center", + fontWeight: "bold", + color: "black", + textTransform: "uppercase" + }} + > + {getFirstLetter(obj.Name)} + </span> + </div> + <div + style={{ + display: "flex", + flexDirection: "column" + }} + > + <span className="userName">{obj.Name}</span> + <span className="useEmail"> + {obj.Email} + </span> + </div> + </div> + ); + })} + </div> + </> + )} + + {unsignedSigners.length > 0 && ( + <> + <div + style={{ + background: themeColor, + color: "white", + padding: "5px", + fontFamily: "sans-serif", + marginTop: signedSigners.length > 0 && "20px" + }} + > + Yet to sign + </div> + <div style={{ marginTop: "5px" }}> + {unsignedSigners.map((obj, ind) => { + return ( + <div + style={{ + display: "flex", + flexDirection: "row", + alignItems: "center", + padding: "10px 0", + background: checkSignerBackColor(obj) + }} + key={ind} + > + <div + className="signerStyle" + style={{ + background: "#abd1d0", + width: 30, + height: 30, + display: "flex", + borderRadius: 30 / 2, + justifyContent: "center", + alignItems: "center", + margin: "0 10px 0 5px" + }} + > + <span + style={{ + fontSize: "12px", + textAlign: "center", + fontWeight: "bold", + color: "black", + textTransform: "uppercase" + }} + > + {getFirstLetter(obj.Name)} + </span> + </div> + <div + style={{ + display: "flex", + flexDirection: "column" + }} + > + <span className="userName">{obj.Name}</span> + <span className="useEmail"> + {obj.Email} + </span> + </div> + <hr /> + </div> + ); + })} + </div> + </> + )} + {defaultSignImg && !alreadySign && ( + <DefaultSignature + defaultSignImg={defaultSignImg} + setDefaultSignImg={setDefaultSignImg} + userObjectId={signerObjectId} + setIsLoading={setIsLoading} + xyPostion={signerPos} + setDefaultSignAlert={setDefaultSignAlert} + /> + )} + </div> + </div> + </div> </div> </div> - </div> - </div> - )} + )} - <ModalUi - headerColor={"#dc3545"} - isOpen={validateAlert} - title={"Validation alert"} - handleClose={() => { - setValidateAlert(false); - }} - > - <div style={{ height: "100%", padding: 20 }}> - <p> - The input does not meet the criteria set by the regular expression. - </p> - - <div - style={{ - height: "1px", - backgroundColor: "#9f9f9f", - width: "100%", - marginTop: "15px", - marginBottom: "15px" + <ModalUi + headerColor={"#dc3545"} + isOpen={validateAlert} + title={"Validation alert"} + handleClose={() => { + setValidateAlert(false); }} - ></div> - <button - onClick={() => setValidateAlert(false)} - type="button" - className="finishBtn cancelBtn" > - Close - </button> - </div> - </ModalUi> + <div style={{ height: "100%", padding: 20 }}> + <p> + The input does not meet the criteria set by the regular + expression. + </p> + + <div + style={{ + height: "1px", + backgroundColor: "#9f9f9f", + width: "100%", + marginTop: "15px", + marginBottom: "15px" + }} + ></div> + <button + onClick={() => setValidateAlert(false)} + type="button" + className="finishBtn cancelBtn" + > + Close + </button> + </div> + </ModalUi> + </> + )} </DndProvider> ); } diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 3228e963b..d46683711 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -3,7 +3,7 @@ import axios from "axios"; import Parse from "parse"; import "../styles/signature.css"; import { PDFDocument } from "pdf-lib"; -import { themeColor } from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { useDrag, useDrop } from "react-dnd"; @@ -108,7 +108,6 @@ function PlaceHolderSign() { status: false, message: "" }); - const isMobile = window.innerWidth < 767; const [, drop] = useDrop({ accept: "BOX", @@ -180,11 +179,30 @@ function PlaceHolderSign() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [divRef.current]); + async function checkIsSubscribed(email) { + const user = await Parse.Cloud.run("getUserDetails", { + email: email + }); + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (billingDate) { + if (billingDate > new Date()) { + return true; + } else { + navigate(`/subscription`); + } + } else { + navigate(`/subscription`); + } + } //function for get document details const getDocumentDetails = async () => { //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { + if (isEnableSubscription) { + checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); + } const alreadyPlaceholder = documentData[0]?.SignedUrl; // Check if document is sent for signing if (alreadyPlaceholder) { diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index 620db1632..ace1b1f75 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -9,7 +9,8 @@ import Alert from "../primitives/Alert"; import { appInfo } from "../constant/appinfo"; import { useDispatch } from "react-redux"; import { fetchAppInfo } from "../redux/reducers/infoReducer"; -import { showTenant } from "../redux/reducers/ShowTenant"; +import { showTenant } from "../redux/reducers/ShowTenant"; +import { isEnableSubscription } from "../constant/const"; const Signup = () => { const { width } = useWindowSize(); const navigate = useNavigate(); @@ -259,9 +260,7 @@ const Signup = () => { }); if (tenentInfo.length) { dispatch( - showTenant( - tenentInfo[0].tenentName || "" - ) + showTenant(tenentInfo[0].tenentName || "") ); localStorage.setItem( "TenantName", @@ -325,7 +324,7 @@ const Signup = () => { element.pageType ); setState({ loading: false }); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { navigate(`/subscription`, { replace: true }); } else { alert("Registered user successfully"); @@ -343,7 +342,7 @@ const Signup = () => { ); localStorage.setItem("pageType", element.pageType); setState({ loading: false }); - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { + if (isEnableSubscription) { navigate(`/subscription`, { replace: true }); } else { navigate( @@ -661,4 +660,4 @@ const Signup = () => { ); }; -export default Signup +export default Signup; diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index 82fe4d40e..93fef3bb4 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -3,7 +3,7 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import { useParams, useNavigate } from "react-router-dom"; import axios from "axios"; import "../styles/signature.css"; -import { themeColor } from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { useDrag, useDrop } from "react-dnd"; @@ -35,6 +35,7 @@ import AddRoleModal from "../components/pdf/AddRoleModal"; import PlaceholderCopy from "../components/pdf/PlaceholderCopy"; import TourContentWithBtn from "../primitives/TourContentWithBtn"; import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption"; +import Parse from "parse"; const TemplatePlaceholder = () => { const navigate = useNavigate(); const { templateId } = useParams(); @@ -168,7 +169,22 @@ const TemplatePlaceholder = () => { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [divRef.current]); - + async function checkIsSubscribed(email) { + const user = await Parse.Cloud.run("getUserDetails", { + email: email + }); + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (billingDate) { + if (billingDate > new Date()) { + return true; + } else { + navigate(`/subscription`); + } + } else { + navigate(`/subscription`); + } + } // `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present const fetchTemplate = async () => { try { @@ -190,6 +206,9 @@ const TemplatePlaceholder = () => { : []; if (documentData && documentData.length > 0) { + if (isEnableSubscription) { + checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); + } setPdfDetails(documentData); setIsSigners(true); if (documentData[0].Signers && documentData[0].Signers.length > 0) { diff --git a/apps/OpenSign/src/primitives/Validate.js b/apps/OpenSign/src/primitives/Validate.js index 21fd208c5..c2920b631 100644 --- a/apps/OpenSign/src/primitives/Validate.js +++ b/apps/OpenSign/src/primitives/Validate.js @@ -15,7 +15,7 @@ const Validate = () => { sessionToken: localStorage.getItem("accesstoken") }); if (user) { - checkIsSubscribed(); + setIsUserValid(true); } else { setIsUserValid(false); } @@ -27,30 +27,6 @@ const Validate = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - async function checkIsSubscribed() { - const currentUser = Parse.User.current(); - console.log("currentUser ", currentUser) - const user = await Parse.Cloud.run("getUserDetails", { - email: currentUser.get("email") - }); - const _user = user?.toJSON(); - - if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) { - const billingDate = _user.Next_billing_date && _user.Next_billing_date; - if (billingDate) { - if (billingDate > new Date()) { - setIsUserValid(true); - return true; - } else { - // navigate(`/subscription`); - } - } else { - // navigate(`/subscription`); - } - } else { - setIsUserValid(true); - } - } const handleLoginBtn = () => { try { Parse.User.logOut(); diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 7812b0c6d..9ae7cce6b 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -147,6 +147,14 @@ export const config = { google: { enabled: true, }, + ldap: { + enabled: true, + url: 'ldap://ldap.forumsys.com:389', + suffix: 'dc=example,dc=com', + // dn: 'ou=mathematicians, dc=example, dc=com', + groupCn: 'mathematicians', + groupFilter: '(&(uniqueMember=uid=,dc=example,dc=com)(objectClass=groupOfUniqueNames))', + }, }, }; // Client-keys like the javascript key or the .NET key are not necessary with parse-server From 2762c3e170d738a9731f2e207293e5b15ec90f07 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Thu, 28 Mar 2024 13:57:37 +0530 Subject: [PATCH 04/26] feat: add validation of free plan --- apps/OpenSign/src/json/plansArr.json | 126 +++++++++--------- apps/OpenSign/src/layout/HomeLayout.js | 9 +- apps/OpenSign/src/pages/Login.js | 15 ++- apps/OpenSign/src/pages/PdfRequestFiles.js | 5 +- apps/OpenSign/src/pages/PlaceHolderSign.js | 5 +- apps/OpenSign/src/pages/PlanSubscriptions.js | 54 +++++--- .../OpenSign/src/pages/TemplatePlaceholder.js | 5 +- apps/OpenSignServer/cloud/main.js | 2 + .../cloud/parsefunction/SubscribeFree.js | 27 ++++ 9 files changed, 156 insertions(+), 92 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js diff --git a/apps/OpenSign/src/json/plansArr.json b/apps/OpenSign/src/json/plansArr.json index 90ab47bdc..f372d7493 100644 --- a/apps/OpenSign/src/json/plansArr.json +++ b/apps/OpenSign/src/json/plansArr.json @@ -1,65 +1,63 @@ [ - { - "planName": "SELF HOSTED", - "currency": "", - "price": "Free", - "subtitle": "Free for life One click install.", - "btnText": "Visit github", - "url": "https://github.com/opensignlabs/opensign", - "target": "_blank", - "benefits": [ - "Sign Unlimited Documents", - "S3 Doc Storage", - "Unlimited Guest Signers", - "Unlimited completion certificates", - "Unique Code(OTP) verification for guest signers", - "Expiring docs & reject signature support", - "PDF Template Creation(coming soon)", - "Audit Trails", - "API support", - "Add-ons support(coming soon)", - "Self hosted cloud infrastructure", - "Free for lifetime", - "Community support" - ] - }, - { - "planName": "THANKS PLAN", - "currency": "$", - "price": "29.99", - "subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)", - "btnText": "Subscribe", - "url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks", - "target": "_self", - "benefits": [ - "Sign Unlimited Documents", - "Unlimited Secure Doc Storage with OpenSign™ Drive", - "Unlimited Guest Signers", - "Unlimited completion certificates", - "Unique Code(OTP) verification for guest signers", - "Expiring docs & reject signature support", - "PDF Template Creation(coming soon)", - "Audit Trails", - "API support", - "Add-ons support(coming soon)", - "Self hosted cloud infrastructure" - ] - }, - { - "planName": "ENTERPRISE", - "currency": "", - "price": "Request Price", - "subtitle": "Customization available Priority support.", - "btnText": "Contact us", - "url": "https://www.opensignlabs.com/contact-us", - "target": "_blank", - "benefits": [ - "All features", - "Custom domain", - "Whitelabeling", - "Uptime SLA", - "SSO support", - "24/7 support" - ] - } -] \ No newline at end of file + { + "planName": "Free Plan", + "currency": "", + "price": "Free", + "subtitle": "Free for life.", + "btnText": "Subscribe", + "url": "", + "target": "_blank", + "benefits": [ + "Sign Unlimited Documents", + "S3 Doc Storage", + "Unlimited Guest Signers", + "Unlimited completion certificates", + "Unique Code(OTP) verification for guest signers", + "Expiring docs & reject signature support", + "PDF Template Creation", + "Audit Trails", + "API support", + "Free for lifetime", + "Community support" + ] + }, + { + "planName": "THANKS PLAN", + "currency": "$", + "price": "29.99", + "subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)", + "btnText": "Subscribe", + "url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks", + "target": "_self", + "benefits": [ + "Sign Unlimited Documents", + "Unlimited Secure Doc Storage with OpenSign™ Drive", + "Unlimited Guest Signers", + "Unlimited completion certificates", + "Unique Code(OTP) verification for guest signers", + "Expiring docs & reject signature support", + "PDF Template Creation(coming soon)", + "Audit Trails", + "API support", + "Add-ons support(coming soon)", + "Self hosted cloud infrastructure" + ] + }, + { + "planName": "ENTERPRISE", + "currency": "", + "price": "Request Price", + "subtitle": "Customization available Priority support.", + "btnText": "Contact us", + "url": "https://www.opensignlabs.com/contact-us", + "target": "_blank", + "benefits": [ + "All features", + "Custom domain", + "Whitelabeling", + "Uptime SLA", + "SSO support", + "24/7 support" + ] + } +] diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index 8da57af4b..2686d5e92 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -52,14 +52,16 @@ const HomeLayout = () => { email: currentUser.get("email") }); if (isEnableSubscription) { + const freeplan = user?.get("Plan") && user?.get("Plan"); const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); - - if (billingDate) { + if (freeplan === "freeplan") { + setIsUserValid(true); + setIsLoader(false); + } else if (billingDate) { if (billingDate > new Date()) { setIsUserValid(true); setIsLoader(false); - return true; } else { navigate(`/subscription`); } @@ -69,7 +71,6 @@ const HomeLayout = () => { } else { setIsUserValid(true); setIsLoader(false); - return true; } } const showSidebar = () => { diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 8ddccaa1c..1d73c0bda 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -254,10 +254,15 @@ function Login() { "userDetails", JSON.stringify(LocalUserDetails) ); + const freeplan = + results[0].get("Plan") && + results[0].get("Plan"); const billingDate = results[0].get("Next_billing_date") && results[0].get("Next_billing_date"); - if (billingDate) { + if (freeplan === "freeplan") { + navigate(redirectUrl); + } else if (billingDate) { if (billingDate > new Date()) { localStorage.removeItem( "userDetails" @@ -809,7 +814,13 @@ function Login() { const billingDate = results[0].get("Next_billing_date") && results[0].get("Next_billing_date"); - if (billingDate) { + const freeplan = + results[0]?.get("Plan") && + results[0]?.get("Plan"); + + if (freeplan === "freeplan") { + navigate(redirectUrl); + } else if (billingDate) { if (billingDate > new Date()) { localStorage.removeItem("userDetails"); // Redirect to the appropriate URL after successful login diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index d063a3c35..efea4e25a 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -134,9 +134,12 @@ function PdfRequestFiles() { const user = await Parse.Cloud.run("getUserDetails", { email: email }); + const freeplan = user?.get("Plan") && user?.get("Plan"); const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); - if (billingDate) { + if (freeplan === "freeplan") { + return true; + } else if (billingDate) { if (billingDate > new Date()) { return true; } else { diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index d46683711..d6ac2da45 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -183,9 +183,12 @@ function PlaceHolderSign() { const user = await Parse.Cloud.run("getUserDetails", { email: email }); + const freeplan = user?.get("Plan") && user?.get("Plan"); const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); - if (billingDate) { + if (freeplan === "freeplan") { + return true; + } else if (billingDate) { if (billingDate > new Date()) { return true; } else { diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 3a2e398bc..abae7edbc 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -1,9 +1,9 @@ import React, { useState, useEffect } from "react"; -// import { useNavigate } from "react-router"; -import { NavLink } from "react-router-dom"; +import { NavLink, useNavigate } from "react-router-dom"; import checkmark from "../assets/images/checkmark.png"; import plansArr from "../json/plansArr.json"; import Title from "../components/Title"; +import Parse from "parse"; const listItemStyle = { paddingLeft: "20px", // Add padding to create space for the image backgroundImage: `url(${checkmark})`, // Set your image as the list style image @@ -13,7 +13,7 @@ const listItemStyle = { }; const PlanSubscriptions = () => { - // const navigate = useNavigate(); + const navigate = useNavigate(); const [yearlyVisible, setYearlyVisible] = useState(false); const [isLoader, setIsLoader] = useState(true); @@ -36,14 +36,21 @@ const PlanSubscriptions = () => { phone; useEffect(() => { // if (localStorage.getItem("accesstoken")) { - setIsLoader(false); - setYearlyVisible(false); + setIsLoader(false); + setYearlyVisible(false); // } else { // navigate("/", { replace: true }); // } // eslint-disable-next-line }, []); + const handleFreePlan = async () => { + const params = { userId: Parse.User.current().id }; + const res = await Parse.Cloud.run("freesubscription", params); + if (res.status === "success") { + navigate("/"); + } + }; return ( <> <Title title={"Subscriptions"} /> @@ -82,10 +89,10 @@ const PlanSubscriptions = () => { <ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300"> {plansArr.map((item) => ( <li - className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]" + className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 max-w-[260px]" key={item.planName} > - <div className="p-2 flex flex-col justify-center items-center"> + <div className="p-2 flex flex-col justify-center items-center min-h-[320px]"> <h3 className="text-[#002862] uppercase"> {item.planName} </h3> @@ -128,19 +135,28 @@ const PlanSubscriptions = () => { </div> </div> - <NavLink - to={ - item.btnText === "Subscribe" - ? item.url + details - : item.url - } - className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white" - target={item.target} - > - {item.btnText} - </NavLink> + {item.url ? ( + <NavLink + to={ + item.btnText === "Subscribe" + ? item.url + details + : item.url + } + className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" + target={item.target} + > + {item.btnText} + </NavLink> + ) : ( + <button + className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" + onClick={() => handleFreePlan()} + > + {item.btnText} + </button> + )} </div> - <hr className="w-full bg-gray-300 p-[.5px]" /> + <hr className="w-full bg-gray-300 h-[0.5px]" /> <ul className="mx-1 p-3 text-left break-words text-sm list-none"> {item.benefits.map((subitem, index) => ( <li style={listItemStyle} key={index} className="m-1"> diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index 93fef3bb4..db7e9d285 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -173,9 +173,12 @@ const TemplatePlaceholder = () => { const user = await Parse.Cloud.run("getUserDetails", { email: email }); + const freeplan = user?.get("Plan") && user?.get("Plan"); const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); - if (billingDate) { + if (freeplan === "freeplan") { + return true; + } else if (billingDate) { if (billingDate > new Date()) { return true; } else { diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index d0838e72f..6e1141cdd 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -23,6 +23,7 @@ import TemplateAfterSave from './parsefunction/TemplateAfterSave.js'; import GetTemplate from './parsefunction/GetTemplate.js'; import savewebhook from './parsefunction/saveWebhook.js'; import callWebhook from './parsefunction/callWebhook.js'; +import SubscribeFree from './parsefunction/SubscribeFree.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); @@ -45,6 +46,7 @@ Parse.Cloud.define('getapitoken', getapitoken); Parse.Cloud.define('getTemplate', GetTemplate); Parse.Cloud.define('savewebhook', savewebhook); Parse.Cloud.define('callwebhook', callWebhook); +Parse.Cloud.define('freesubscription', SubscribeFree); Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave); diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js new file mode 100644 index 000000000..2bcf48500 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js @@ -0,0 +1,27 @@ +export default async function SubscribeFree(request) { + const userId = request.params.userId; + const userPtr = { __type: 'Pointer', className: '_User', objectId: userId }; + try { + const extQuery = new Parse.Query('contracts_Users'); + extQuery.equalTo('UserId', userPtr); + const extUser = await extQuery.first({ useMasterKey: true }); + + if (extUser) { + try { + const extUpdate = new Parse.Object('contracts_Users'); + extUpdate.id = extUser.id; + extUpdate.set('Plan', { plan_code: 'freeplan' }); + const updatePlan = await extUpdate.save(null, { useMasterKey: true }); + return { status: 'success', result: 'subscribed!' }; + } catch (err) { + console.log('err ', err); + return { status: 'error', result: err.message }; + } + } else { + return { status: 'error', result: 'User not found!' }; + } + } catch (err) { + console.log('err ', err); + return { status: 'error', result: err.message }; + } +} From c57c15a4cb89e9a492971ca6df26ae0b03b69899 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Thu, 28 Mar 2024 18:57:43 +0530 Subject: [PATCH 05/26] feat: save tenant usage --- apps/OpenSign/src/components/AddSigner.js | 4 +-- apps/OpenSign/src/components/AddUser.js | 4 +-- apps/OpenSign/src/constant/const.js | 2 +- apps/OpenSign/src/constant/saveFileSize.js | 39 +++++++++++++--------- apps/OpenSign/src/pages/Form.js | 7 ++++ apps/OpenSign/src/pages/Login.js | 6 ++-- apps/OpenSign/src/pages/Pgsignup.js | 2 +- apps/OpenSign/src/pages/Signup.js | 2 +- apps/OpenSign/src/pages/UserProfile.js | 9 +++-- apps/OpenSign/src/primitives/AddUser.js | 4 +-- 10 files changed, 49 insertions(+), 30 deletions(-) diff --git a/apps/OpenSign/src/components/AddSigner.js b/apps/OpenSign/src/components/AddSigner.js index f5e070e82..a44a80026 100644 --- a/apps/OpenSign/src/components/AddSigner.js +++ b/apps/OpenSign/src/components/AddSigner.js @@ -56,11 +56,11 @@ const AddSigner = (props) => { contactQuery.set("Email", email); contactQuery.set("UserRole", "contracts_Guest"); - if (localStorage.getItem("TenetId")) { + if (localStorage.getItem("TenantId")) { contactQuery.set("TenantId", { __type: "Pointer", className: "partners_Tenant", - objectId: localStorage.getItem("TenetId") + objectId: localStorage.getItem("TenantId") }); } diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 67d6095e0..d812a1cd0 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -48,11 +48,11 @@ const AddUser = (props) => { contactQuery.set("Email", email); contactQuery.set("UserRole", "contracts_User"); - if (localStorage.getItem("TenetId")) { + if (localStorage.getItem("TenantId")) { contactQuery.set("TenantId", { __type: "Pointer", className: "partners_Tenant", - objectId: localStorage.getItem("TenetId") + objectId: localStorage.getItem("TenantId") }); } diff --git a/apps/OpenSign/src/constant/const.js b/apps/OpenSign/src/constant/const.js index a046b4ae1..43691ac98 100644 --- a/apps/OpenSign/src/constant/const.js +++ b/apps/OpenSign/src/constant/const.js @@ -9,4 +9,4 @@ export const modalSubmitBtnColor = "#17a2b8"; export const modalCancelBtnColor = "white"; export const themeColor = "#47a3ad"; export const iconColor = "#686968"; -export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION; +export const isEnableSubscription = false//process.env.REACT_APP_ENABLE_SUBSCRIPTION; diff --git a/apps/OpenSign/src/constant/saveFileSize.js b/apps/OpenSign/src/constant/saveFileSize.js index 56927ff05..51c71d31d 100644 --- a/apps/OpenSign/src/constant/saveFileSize.js +++ b/apps/OpenSign/src/constant/saveFileSize.js @@ -2,17 +2,25 @@ import axios from "axios"; const parseAppId = localStorage.getItem("parseAppId"); const serverUrl = localStorage.getItem("baseUrl"); -export const SaveFileSize = async (size, imageUrl) => { +export const SaveFileSize = async (size, imageUrl, tenantId) => { //checking server url and save file's size - + const tenantPtr = { + __type: "Pointer", + className: "partners_Tenant", + objectId: tenantId + }; + const _tenantPtr = JSON.stringify(tenantPtr); try { const response = await axios - .get(`${serverUrl}classes/partners_TenantCredits`, { - headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId + .get( + `${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId + } } - }) + ) .then((result) => { const res = result.data; // console.log("res", res); @@ -49,9 +57,7 @@ export const SaveFileSize = async (size, imageUrl) => { console.log("axois err ", err); }); } else { - data = { - usedStorage: size - }; + data = { usedStorage: size, PartnersTenant: tenantPtr }; await axios .post(`${serverUrl}classes/partners_TenantCredits`, data, { headers: { @@ -70,14 +76,15 @@ export const SaveFileSize = async (size, imageUrl) => { } catch (e) { console.log("org app error", e); } - saveDataFile(size, imageUrl); + saveDataFile(size, imageUrl, tenantPtr); }; //function for save fileUrl and file size in particular client db class partners_DataFiles -const saveDataFile = async (size, imageUrl) => { +const saveDataFile = async (size, imageUrl, tenantPtr) => { const data = { FileUrl: imageUrl, - FileSize: size + FileSize: size, + TenantPtr: tenantPtr }; // console.log("data save",file, data) @@ -88,9 +95,9 @@ const saveDataFile = async (size, imageUrl) => { "X-Parse-Application-Id": parseAppId } }) - .then(() => { - // const res = result.data; - // console.log("res", res); + .then((result) => { + const res = result.data; + console.log("res", res); }) .catch((err) => { console.log("axois err ", err); diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 4ab1a09f2..c726ad1cb 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -10,6 +10,7 @@ import SelectFolder from "../components/shared/fields/SelectFolder"; import SignersInput from "../components/shared/fields/SignersInput"; import Title from "../components/Title"; import PageNotFound from "./PageNotFound"; +import { SaveFileSize } from "../constant/saveFileSize"; // `Form` render all type of Form on this basis of their provided in path function Form() { @@ -82,6 +83,7 @@ const Forms = (props) => { const handleFileUpload = async (file) => { setfileload(true); const fileName = file.name; + const size = file.size; const name = sanitizeFileName(fileName); const pdfFile = file; const parseFile = new Parse.File(name, pdfFile); @@ -101,6 +103,8 @@ const Forms = (props) => { setFileUpload(response.url()); setfileload(false); if (response.url()) { + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(size, response.url(), tenantId); return response.url(); } } catch (error) { @@ -113,6 +117,7 @@ const Forms = (props) => { const dropboxSuccess = async (files) => { setfileload(true); const file = files[0]; + const size = file.bytes; const url = file.link; const mb = Math.round(file.bytes / Math.pow(1024, 2)); @@ -141,6 +146,8 @@ const Forms = (props) => { setfileload(false); if (response.url()) { + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(size, response.url(), tenantId); return response.url(); } } catch (error) { diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 1d73c0bda..75759aa70 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -213,7 +213,7 @@ function Login() { x.TenantId.TenantName || "" }; localStorage.setItem( - "TenetId", + "TenantId", x.TenantId.objectId ); tenentInfo.push(obj); @@ -546,7 +546,7 @@ function Login() { tenentName: x.TenantId.TenantName || "" }; localStorage.setItem( - "TenetId", + "TenantId", x.TenantId.objectId ); tenentInfo.push(obj); @@ -788,7 +788,7 @@ function Login() { tenentName: x.TenantId.TenantName || "" }; localStorage.setItem( - "TenetId", + "TenantId", x.TenantId.objectId ); tenentInfo.push(obj); diff --git a/apps/OpenSign/src/pages/Pgsignup.js b/apps/OpenSign/src/pages/Pgsignup.js index 10b270d1e..f34cf9d7b 100644 --- a/apps/OpenSign/src/pages/Pgsignup.js +++ b/apps/OpenSign/src/pages/Pgsignup.js @@ -353,7 +353,7 @@ const PgSignUp = () => { tenentName: x.TenantId.TenantName || "" }; localStorage.setItem( - "TenetId", + "TenantId", x.TenantId.objectId ); tenentInfo.push(obj); diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index ace1b1f75..1099d4a88 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -296,7 +296,7 @@ const Signup = () => { tenentName: x.TenantId.TenantName || "" }; localStorage.setItem( - "TenetId", + "TenantId", x.TenantId.objectId ); tenentInfo.push(obj); diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index b8d9da22c..45e32a393 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -137,7 +137,8 @@ function UserProfile() { setImage(response.url()); localStorage.setItem("profileImg", response.url()); setpercentage(0); - SaveFileSize(size, response.url()); + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(size, response.url(), tenantId); return response.url(); } } catch (error) { @@ -264,7 +265,11 @@ function UserProfile() { <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"} />{" "} + <Tooltip + url={ + "https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid" + } + />{" "} </span>{" "} <label className={`${ diff --git a/apps/OpenSign/src/primitives/AddUser.js b/apps/OpenSign/src/primitives/AddUser.js index 42ff14115..ee24e8ce0 100644 --- a/apps/OpenSign/src/primitives/AddUser.js +++ b/apps/OpenSign/src/primitives/AddUser.js @@ -54,11 +54,11 @@ const AddUser = (props) => { contactQuery.set("Email", email); contactQuery.set("UserRole", "contracts_Guest"); - if (localStorage.getItem("TenetId")) { + if (localStorage.getItem("TenantId")) { contactQuery.set("TenantId", { __type: "Pointer", className: "partners_Tenant", - objectId: localStorage.getItem("TenetId") + objectId: localStorage.getItem("TenantId") }); } From 46159b7214568fdb9ec19af7a24f526a123fa762 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 29 Mar 2024 13:11:13 +0530 Subject: [PATCH 06/26] fix: freeplan navigation --- apps/OpenSign/src/constant/const.js | 2 +- apps/OpenSign/src/constant/saveFileSize.js | 90 +++++++------------ apps/OpenSign/src/layout/HomeLayout.js | 2 +- apps/OpenSign/src/pages/Login.js | 4 +- apps/OpenSign/src/pages/Managesign.js | 7 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 2 +- apps/OpenSign/src/pages/PlaceHolderSign.js | 2 +- .../OpenSign/src/pages/TemplatePlaceholder.js | 2 +- apps/OpenSignServer/Utils.js | 72 +++++++++++++++ 9 files changed, 116 insertions(+), 67 deletions(-) diff --git a/apps/OpenSign/src/constant/const.js b/apps/OpenSign/src/constant/const.js index 43691ac98..a046b4ae1 100644 --- a/apps/OpenSign/src/constant/const.js +++ b/apps/OpenSign/src/constant/const.js @@ -9,4 +9,4 @@ export const modalSubmitBtnColor = "#17a2b8"; export const modalCancelBtnColor = "white"; export const themeColor = "#47a3ad"; export const iconColor = "#686968"; -export const isEnableSubscription = false//process.env.REACT_APP_ENABLE_SUBSCRIPTION; +export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION; diff --git a/apps/OpenSign/src/constant/saveFileSize.js b/apps/OpenSign/src/constant/saveFileSize.js index 51c71d31d..37151822f 100644 --- a/apps/OpenSign/src/constant/saveFileSize.js +++ b/apps/OpenSign/src/constant/saveFileSize.js @@ -11,25 +11,16 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => { }; const _tenantPtr = JSON.stringify(tenantPtr); try { - const response = await axios - .get( - `${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, - { - headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId - } + const res = await axios.get( + `${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId } - ) - .then((result) => { - const res = result.data; - // console.log("res", res); - - return res.results; - }) - .catch((err) => { - console.log("axois err ", err); - }); + } + ); + const response = res.data.results; let data; // console.log("response", response); if (response && response.length > 0) { @@ -38,43 +29,27 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => { ? response[0].usedStorage + size : size }; - await axios - .put( - `${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`, - data, - { - headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId - } - } - ) - .then(() => { - // const res = result.data; - // console.log("save res", res); - }) - .catch((err) => { - console.log("axois err ", err); - }); - } else { - data = { usedStorage: size, PartnersTenant: tenantPtr }; - await axios - .post(`${serverUrl}classes/partners_TenantCredits`, data, { + await axios.put( + `${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`, + data, + { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": parseAppId } - }) - .then(() => { - // const res = result.data; - // console.log("res", res); - }) - .catch((err) => { - console.log("axois err ", err); - }); + } + ); + } else { + data = { usedStorage: size, PartnersTenant: tenantPtr }; + await axios.post(`${serverUrl}classes/partners_TenantCredits`, data, { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId + } + }); } - } catch (e) { - console.log("org app error", e); + } catch (err) { + console.log("err in save usage", err); } saveDataFile(size, imageUrl, tenantPtr); }; @@ -88,18 +63,15 @@ const saveDataFile = async (size, imageUrl, tenantPtr) => { }; // console.log("data save",file, data) - await axios - .post(`${serverUrl}classes/partners_DataFiles`, data, { + try { + await axios.post(`${serverUrl}classes/partners_DataFiles`, data, { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": parseAppId } - }) - .then((result) => { - const res = result.data; - console.log("res", res); - }) - .catch((err) => { - console.log("axois err ", err); }); + } catch (err) { + console.log("err in save usage ", err); + } }; + \ No newline at end of file diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index 2686d5e92..12301e917 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -52,7 +52,7 @@ const HomeLayout = () => { email: currentUser.get("email") }); if (isEnableSubscription) { - const freeplan = user?.get("Plan") && user?.get("Plan"); + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); if (freeplan === "freeplan") { diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 75759aa70..2ee9d6c60 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -256,7 +256,7 @@ function Login() { ); const freeplan = results[0].get("Plan") && - results[0].get("Plan"); + results[0].get("Plan").plan_code; const billingDate = results[0].get("Next_billing_date") && results[0].get("Next_billing_date"); @@ -816,7 +816,7 @@ function Login() { results[0].get("Next_billing_date"); const freeplan = results[0]?.get("Plan") && - results[0]?.get("Plan"); + results[0]?.get("Plan").plan_code; if (freeplan === "freeplan") { navigate(redirectUrl); diff --git a/apps/OpenSign/src/pages/Managesign.js b/apps/OpenSign/src/pages/Managesign.js index e3efd39c9..7c4acd5a3 100644 --- a/apps/OpenSign/src/pages/Managesign.js +++ b/apps/OpenSign/src/pages/Managesign.js @@ -5,6 +5,7 @@ import "../styles/signature.css"; import { toDataUrl } from "../constant/Utils"; import Parse from "parse"; import { appInfo } from "../constant/appinfo"; +import { SaveFileSize } from "../constant/saveFileSize"; const ManageSign = () => { const appName = appInfo.appname; const [penColor, setPenColor] = useState("blue"); @@ -158,7 +159,11 @@ const ManageSign = () => { try { const parseFile = new Parse.File(file.name, file); const response = await parseFile.save(); - return response?.url(); + if (response?.url()) { + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(file.size, response.url(), tenantId); + return response?.url(); + } } catch (err) { console.log("sign upload err", err); setIsLoader(false); diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index e14b86fbb..8df0ad294 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -137,7 +137,7 @@ function PdfRequestFiles() { const user = await Parse.Cloud.run("getUserDetails", { email: email }); - const freeplan = user?.get("Plan") && user?.get("Plan"); + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); if (freeplan === "freeplan") { diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index d7b14b8ae..6fdf11cee 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -183,7 +183,7 @@ function PlaceHolderSign() { const user = await Parse.Cloud.run("getUserDetails", { email: email }); - const freeplan = user?.get("Plan") && user?.get("Plan"); + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); if (freeplan === "freeplan") { diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index f38b66c57..c3274128f 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -173,7 +173,7 @@ const TemplatePlaceholder = () => { const user = await Parse.Cloud.run("getUserDetails", { email: email }); - const freeplan = user?.get("Plan") && user?.get("Plan"); + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); if (freeplan === "freeplan") { diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 8fc705ceb..9684ec154 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -1,3 +1,7 @@ +import axios from 'axios'; + +const appId = process.env.APP_ID; +const serverUrl = process.env.SERVER_URL; export function customAPIurl() { const url = new URL(process.env.SERVER_URL); return url.pathname === '/api/app' ? url.origin + '/api' : url.origin; @@ -38,3 +42,71 @@ export function replaceMailVaribles(subject, body, variables) { }; return result; } + +export const saveFileUsage = async (size, imageUrl, tenantId) => { + //checking server url and save file's size + const tenantPtr = { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenantId, + }; + const _tenantPtr = JSON.stringify(tenantPtr); + try { + const res = await axios.get( + `${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, + { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + }, + } + ); + const response = res.data.results; + + let data; + // console.log("response", response); + if (response && response.length > 0) { + data = { + usedStorage: response[0].usedStorage ? response[0].usedStorage + size : size, + }; + await axios.put(`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`, data, { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + }, + }); + } else { + data = { usedStorage: size, PartnersTenant: tenantPtr }; + await axios.post(`${serverUrl}classes/partners_TenantCredits`, data, { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': parseAppId, + }, + }); + } + } catch (err) { + console.log('err in save usage', err); + } + saveDataFile(size, imageUrl, tenantPtr); +}; + +//function for save fileUrl and file size in particular client db class partners_DataFiles +const saveDataFile = async (size, imageUrl, tenantPtr) => { + const data = { + FileUrl: imageUrl, + FileSize: size, + TenantPtr: tenantPtr, + }; + + // console.log("data save",file, data) + try { + await axios.post(`${serverUrl}classes/partners_DataFiles`, data, { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': parseAppId, + }, + }); + } catch (err) { + console.log('error in save usage ', err); + } +}; From 1932dda6d6b6b0f71ca4a1e2b5d7ce7faa88215c Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Fri, 29 Mar 2024 17:06:09 +0530 Subject: [PATCH 07/26] feat: disbale validation feature, documentId,webhook,API token for free users and add upgrade button to subscribe --- .../components/pdf/DropdownWidgetOption.js | 84 ++++++++++++------- .../src/components/pdf/WidgetNameModal.js | 15 +++- apps/OpenSign/src/constant/Utils.js | 30 +++++++ apps/OpenSign/src/pages/GenerateToken.js | 42 ++++++++-- apps/OpenSign/src/pages/PlaceHolderSign.js | 6 ++ .../OpenSign/src/pages/TemplatePlaceholder.js | 6 ++ apps/OpenSign/src/pages/UserProfile.js | 35 ++++++-- apps/OpenSign/src/pages/Webhook.js | 36 ++++++-- apps/OpenSign/src/primitives/Tooltip.js | 11 ++- apps/OpenSign/src/primitives/Upgrade.js | 18 ++++ 10 files changed, 232 insertions(+), 51 deletions(-) create mode 100644 apps/OpenSign/src/primitives/Upgrade.js diff --git a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js index 7d80efd9e..08de190f7 100644 --- a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js +++ b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js @@ -3,6 +3,7 @@ import { themeColor } from "../../constant/const"; import ModalUi from "../../primitives/ModalUi"; import { radioButtonWidget } from "../../constant/Utils"; import PremiumAlertHeader from "../../primitives/PremiumAlertHeader"; +import Upgrade from "../../primitives/Upgrade"; function DropdownWidgetOption(props) { const [dropdownOptionList, setDropdownOptionList] = useState([ "option-1", @@ -241,36 +242,59 @@ function DropdownWidgetOption(props) { }} className="fa-solid fa-square-plus" ></i> - {props.type === "checkbox" && !props.isSignYourself && ( - <> - <label style={{ fontSize: "13px", fontWeight: "600" }}> - Minimun check - </label> - <input - required - defaultValue={0} - value={minCount} - onChange={(e) => { - const count = handleSetMinMax(e); - setMinCount(count); - }} - className="drodown-input" - /> - <label style={{ fontSize: "13px", fontWeight: "600" }}> - Maximum check - </label> - <input - required - defaultValue={0} - value={maxCount} - onChange={(e) => { - const count = handleSetMinMax(e); - setMaxCount(count); - }} - className="drodown-input" - /> - </> - )} + <div> + {props.type === "checkbox" && !props.isSignYourself && ( + <> + <label + style={{ + fontSize: "13px", + fontWeight: "600", + color: !props.isSubscribe && "gray" + }} + > + Minimun check + </label> + {!props.isSubscribe && <Upgrade />} + <input + required + defaultValue={0} + value={minCount} + onChange={(e) => { + const count = handleSetMinMax(e); + setMinCount(count); + }} + className={ + props.isSubscribe + ? "drodown-input" + : "disabled drodown-input" + } + /> + <label + style={{ + fontSize: "13px", + fontWeight: "600", + color: !props.isSubscribe && "gray" + }} + > + Maximum check + </label> + <input + required + defaultValue={0} + value={maxCount} + onChange={(e) => { + const count = handleSetMinMax(e); + setMaxCount(count); + }} + className={ + props.isSubscribe + ? "drodown-input" + : "disabled drodown-input" + } + /> + </> + )} + </div> </div> {["dropdown", radioButtonWidget].includes(props.type) && ( <> diff --git a/apps/OpenSign/src/components/pdf/WidgetNameModal.js b/apps/OpenSign/src/components/pdf/WidgetNameModal.js index 71c433afc..eb8bfb937 100644 --- a/apps/OpenSign/src/components/pdf/WidgetNameModal.js +++ b/apps/OpenSign/src/components/pdf/WidgetNameModal.js @@ -4,6 +4,7 @@ import "../../styles/AddUser.css"; import RegexParser from "regex-parser"; import { textInputWidget } from "../../constant/Utils"; import PremiumAlertHeader from "../../primitives/PremiumAlertHeader"; +import Upgrade from "../../primitives/Upgrade"; const WidgetNameModal = (props) => { const [formdata, setFormdata] = useState({ @@ -133,10 +134,16 @@ const WidgetNameModal = (props) => { props.widgetName === textInputWidget) && ( <> <div className="form-section"> - <label htmlFor="textvalidate" style={{ fontSize: 13 }}> + <label + htmlFor="textvalidate" + className="disabled" + style={{ fontSize: 13 }} + > Validation </label> + {!props.isSubscribe && <Upgrade />} <div + className="disabled" style={{ display: "flex", flexDirection: "row", @@ -178,6 +185,7 @@ const WidgetNameModal = (props) => { </div> </div> </div> + <div className="form-section"> <label htmlFor="name" style={{ fontSize: 13 }}> Default value @@ -196,7 +204,10 @@ const WidgetNameModal = (props) => { }} /> {isValid === false && ( - <div className="warning defaultvalueWarning" style={{ fontSize: 12 }}> + <div + className="warning defaultvalueWarning" + style={{ fontSize: 12 }} + > <i className="fas fa-exclamation-circle" style={{ color: "#fab005", fontSize: 15 }} diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 044d6c0d6..4483bfe78 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -3,6 +3,7 @@ import moment from "moment"; import { themeColor } from "./const"; import React from "react"; import { rgb } from "pdf-lib"; +import Parse from "parse"; export const isMobile = window.innerWidth < 767; export const textInputWidget = "text input"; @@ -12,6 +13,35 @@ export const openInNewTab = (url) => { window.open(url, "_blank", "noopener,noreferrer"); }; +//function to get subcripition details from Extand user class +export async function checkIsSubscribed() { + // const tenantId = localStorage.getItem("TenantId"); + // const users = new Parse.Query("partners_Tenant"); + // users.equalTo("objectId", tenantId); + // const res = await users.first(); + // const jsonRes = JSON.parse(JSON.stringify(res)); + + const extClass = localStorage.getItem("Extand_Class"); + const jsonSender = JSON.parse(extClass); + const user = await Parse.Cloud.run("getUserDetails", { + email: jsonSender[0].Email + }); + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (freeplan === "freeplan") { + return false; + } else if (billingDate) { + if (billingDate > new Date()) { + return true; + } else { + return false; + } + } else { + return false; + } +} + export const color = [ "#93a3db", "#e6c3db", diff --git a/apps/OpenSign/src/pages/GenerateToken.js b/apps/OpenSign/src/pages/GenerateToken.js index 65b2c0f4f..026dfb7e9 100644 --- a/apps/OpenSign/src/pages/GenerateToken.js +++ b/apps/OpenSign/src/pages/GenerateToken.js @@ -3,10 +3,11 @@ import Title from "../components/Title"; import axios from "axios"; import Alert from "../primitives/Alert"; import ModalUi from "../primitives/ModalUi"; -import { rejectBtn, submitBtn } from "../constant/const"; -import { openInNewTab } from "../constant/Utils"; +import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; +import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; +import Upgrade from "../primitives/Upgrade"; function GenerateToken() { const [parseBaseUrl] = useState(localStorage.getItem("baseUrl")); @@ -17,6 +18,7 @@ function GenerateToken() { const [isGenerate, setIsGenerate] = useState(false); const [isErr, setIsErr] = useState(false); const [isModal, setIsModal] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); useEffect(() => { fetchToken(); @@ -25,6 +27,11 @@ function GenerateToken() { const fetchToken = async () => { try { + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribed(); + setIsSubscribe(getIsSubscribe); + } + const url = parseBaseUrl + "functions/getapitoken"; const headers = { "Content-Type": "application/json", @@ -35,6 +42,7 @@ function GenerateToken() { if (res) { SetApiToken(res.data.result.result); } + setIsLoader(false); } catch (err) { SetApiToken(); @@ -118,13 +126,29 @@ function GenerateToken() { ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> <PremiumAlertHeader /> - <h1 className="ml-4 mt-3 mb-2 font-semibold"> + <h1 + className={ + isSubscribe + ? "ml-4 mt-3 mb-2 font-semibold" + : "ml-4 mt-3 mb-2 font-semibold text-gray-300" + } + > API Token{" "} <Tooltip - url={"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"} + url={ + "https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1" + } + iconColor={!isSubscribe && "gray"} /> + {!isSubscribe && <Upgrade />} </h1> - <ul className="w-full flex flex-col p-2 text-sm"> + <ul + className={ + isSubscribe + ? "w-full flex flex-col p-2 text-sm " + : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" + } + > <li className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} > @@ -138,7 +162,13 @@ function GenerateToken() { </span> </li> </ul> - <div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4"> + <div + className={ + isSubscribe + ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" + : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" + } + > <button type="button" onClick={apiToken ? handleModal : handleSubmit} diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 6fdf11cee..21edb67a7 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -104,6 +104,7 @@ function PlaceHolderSign() { const [widgetName, setWidgetName] = useState(false); const [mailStatus, setMailStatus] = useState(""); const [isCurrUser, setIsCurrUser] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); const [isAlreadyPlace, setIsAlreadyPlace] = useState({ status: false, message: "" @@ -190,6 +191,7 @@ function PlaceHolderSign() { return true; } else if (billingDate) { if (billingDate > new Date()) { + setIsSubscribe(true); return true; } else { navigate(`/subscription`); @@ -1617,6 +1619,7 @@ function PlaceHolderSign() { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> <DropdownWidgetOption type="checkbox" @@ -1627,6 +1630,7 @@ function PlaceHolderSign() { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> <DropdownWidgetOption type="dropdown" @@ -1637,6 +1641,7 @@ function PlaceHolderSign() { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> {/* pdf header which contain funish back button */} @@ -1852,6 +1857,7 @@ function PlaceHolderSign() { isOpen={isNameModal} handleClose={handleNameModal} handleData={handleWidgetdefaultdata} + isSubscribe={isSubscribe} /> </div> </> diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index c3274128f..67118f40f 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -53,6 +53,7 @@ const TemplatePlaceholder = () => { const [isSelectListId, setIsSelectId] = useState(); const [isSendAlert, setIsSendAlert] = useState(false); const [isCreateDocModal, setIsCreateDocModal] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); const [isLoading, setIsLoading] = useState({ isLoad: true, message: "This might take some time" @@ -180,6 +181,7 @@ const TemplatePlaceholder = () => { return true; } else if (billingDate) { if (billingDate > new Date()) { + setIsSubscribe(true); return true; } else { navigate(`/subscription`); @@ -1345,6 +1347,7 @@ const TemplatePlaceholder = () => { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> <DropdownWidgetOption type="checkbox" @@ -1355,6 +1358,7 @@ const TemplatePlaceholder = () => { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> <DropdownWidgetOption type="dropdown" @@ -1365,6 +1369,7 @@ const TemplatePlaceholder = () => { currWidgetsDetails={currWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails} handleClose={handleNameModal} + isSubscribe={isSubscribe} /> <PlaceholderCopy isPageCopy={isPageCopy} @@ -1564,6 +1569,7 @@ const TemplatePlaceholder = () => { isOpen={isNameModal} handleClose={handleNameModal} handleData={handleWidgetdefaultdata} + isSubscribe={isSubscribe} /> </div> ); diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 45e32a393..4048a04ba 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -8,6 +8,9 @@ import sanitizeFileName from "../primitives/sanitizeFileName"; import axios from "axios"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; +import { isEnableSubscription } from "../constant/const"; +import { checkIsSubscribed } from "../constant/Utils"; +import Upgrade from "../primitives/Upgrade"; function UserProfile() { const navigate = useNavigate(); @@ -21,15 +24,24 @@ function UserProfile() { const [isLoader, setIsLoader] = useState(false); const [percentage, setpercentage] = useState(0); const [isDisableDocId, setIsDisableDocId] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); useEffect(() => { + getUserDetail(); + }, []); + + const getUserDetail = async () => { const extClass = localStorage.getItem("Extand_Class"); const jsonSender = JSON.parse(extClass); const HeaderDocId = jsonSender[0]?.HeaderDocId; + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribed(); + setIsSubscribe(getIsSubscribe); + } if (HeaderDocId) { setIsDisableDocId(HeaderDocId); } - }, []); + }; const handleSubmit = async (e) => { e.preventDefault(); setIsLoader(true); @@ -156,7 +168,6 @@ function UserProfile() { const handleDisableDocId = () => { setIsDisableDocId((prevChecked) => !prevChecked); }; - return ( <React.Fragment> <Title title={"Profile"} /> @@ -263,18 +274,30 @@ function UserProfile() { </li> <li className="border-y-[1px] border-gray-300 break-all"> <div className="flex justify-between items-center py-2"> - <span className="font-semibold"> + <span + className={ + isSubscribe + ? "font-semibold" + : "font-semibold text-gray-300" + } + > Disable DocumentId :{" "} <Tooltip url={ "https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid" } + isSubscribe={isSubscribe} />{" "} + {!isSubscribe && <Upgrade />} </span>{" "} <label - className={`${ - editmode ? "cursor-pointer" : "" - } relative inline-flex items-center mb-0`} + className={ + !isSubscribe + ? "relative inline-flex items-center mb-0 pointer-events-none opacity-50" + : `${ + editmode ? "cursor-pointer" : "" + } relative inline-flex items-center mb-0` + } > <input disabled={editmode ? false : true} diff --git a/apps/OpenSign/src/pages/Webhook.js b/apps/OpenSign/src/pages/Webhook.js index fb7fe0616..b89bda460 100644 --- a/apps/OpenSign/src/pages/Webhook.js +++ b/apps/OpenSign/src/pages/Webhook.js @@ -3,11 +3,12 @@ import Title from "../components/Title"; import axios from "axios"; import Alert from "../primitives/Alert"; import ModalUi from "../primitives/ModalUi"; -import { rejectBtn, submitBtn } from "../constant/const"; -import { openInNewTab } from "../constant/Utils"; +import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; +import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import Parse from "parse"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; +import Upgrade from "../primitives/Upgrade"; function Webhook() { const [parseBaseUrl] = useState(localStorage.getItem("baseUrl")); @@ -17,6 +18,7 @@ function Webhook() { const [isGenerate, setIsGenerate] = useState(false); const [isErr, setIsErr] = useState(false); const [isModal, setIsModal] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); useEffect(() => { fetchWebhook(); @@ -25,6 +27,10 @@ function Webhook() { const fetchWebhook = async () => { const email = Parse.User.current().getEmail(); + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribed(); + setIsSubscribe(getIsSubscribe); + } const params = { email: email }; try { const extRes = await Parse.Cloud.run("getUserDetails", params); @@ -104,13 +110,27 @@ function Webhook() { ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> <PremiumAlertHeader /> - <h1 className="ml-4 mt-3 mb-2 font-semibold"> + <h1 + className={ + isSubscribe + ? "ml-4 mt-3 mb-2 font-semibold" + : "ml-4 mt-3 mb-2 font-semibold text-gray-300" + } + > Webhook{" "} <Tooltip url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"} + isSubscribe={isSubscribe} /> + {!isSubscribe && <Upgrade />} </h1> - <ul className="w-full flex flex-col p-2 text-sm"> + <ul + className={ + isSubscribe + ? "w-full flex flex-col p-2 text-sm " + : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" + } + > <li className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} > @@ -120,7 +140,13 @@ function Webhook() { </span> </li> </ul> - <div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4"> + <div + className={ + isSubscribe + ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" + : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" + } + > <button type="button" onClick={handleModal} diff --git a/apps/OpenSign/src/primitives/Tooltip.js b/apps/OpenSign/src/primitives/Tooltip.js index b3fdc3e64..2a8006deb 100644 --- a/apps/OpenSign/src/primitives/Tooltip.js +++ b/apps/OpenSign/src/primitives/Tooltip.js @@ -1,9 +1,16 @@ import React from "react"; import { Tooltip as ReactTooltip } from "react-tooltip"; import { openInNewTab } from "../constant/Utils"; -const Tooltip = ({ id, message, url, iconColor }) => +const Tooltip = ({ id, message, url, iconColor, isSubscribe }) => url ? ( - <button onClick={() => openInNewTab(url)} className="text-center"> + <button + onClick={() => openInNewTab(url)} + className={ + isSubscribe + ? "text-center" + : "text-center opacity-20 pointer-events-none" + } + > <sup> <i className="fa-solid fa-question rounded-full" diff --git a/apps/OpenSign/src/primitives/Upgrade.js b/apps/OpenSign/src/primitives/Upgrade.js new file mode 100644 index 000000000..9480752a6 --- /dev/null +++ b/apps/OpenSign/src/primitives/Upgrade.js @@ -0,0 +1,18 @@ +import React from "react"; + +function Upgrade() { + return ( + <sup> + <a + href="https://www.opensignlabs.com/plans-pricing" + target="_blank" + rel="noopener noreferrer" + className="text-blue-800 text-sm cursor-pointer " + > + Upgrade now + </a> + </sup> + ); +} + +export default Upgrade; From 881ac12b03c656017036def98cea25bfa762503b Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 29 Mar 2024 17:26:33 +0530 Subject: [PATCH 08/26] feat: savefileusage in server side --- apps/OpenSign/src/pages/PlaceHolderSign.js | 5 + apps/OpenSignServer/Utils.js | 100 ++++++---- .../v1/routes/createDocumentwithCoordinate.js | 14 +- .../customRoute/v1/routes/createTemplate.js | 11 +- .../v1/routes/createTemplatewithCoordinate.js | 9 +- .../customRoute/v1/routes/draftDocument.js | 7 +- .../cloud/parsefunction/pdf/PDF.min.js | 183 +++++++++--------- 7 files changed, 188 insertions(+), 141 deletions(-) diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 21edb67a7..b176a043f 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -39,6 +39,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn"; import ModalUi from "../primitives/ModalUi"; import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption"; import WidgetNameModal from "../components/pdf/WidgetNameModal"; +import { SaveFileSize } from "../constant/saveFileSize"; function PlaceHolderSign() { const navigate = useNavigate(); @@ -759,6 +760,10 @@ function PlaceHolderSign() { // Save the Parse File if needed const pdfData = await pdfFile.save(); const pdfUrl = pdfData.url(); + const tenantId = localStorage.getItem("TenantId"); + const buffer = atob(pdfBytes) + console.log("buffer", buffer.length) + SaveFileSize(buffer.length, pdfUrl, tenantId); return pdfUrl; } catch (e) { console.log("error", e); diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 9684ec154..50d5027c5 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -43,51 +43,69 @@ export function replaceMailVaribles(subject, body, variables) { return result; } -export const saveFileUsage = async (size, imageUrl, tenantId) => { +export const saveFileUsage = async (size, imageUrl, userId) => { //checking server url and save file's size - const tenantPtr = { - __type: 'Pointer', - className: 'partners_Tenant', - objectId: tenantId, - }; - const _tenantPtr = JSON.stringify(tenantPtr); - try { - const res = await axios.get( - `${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, - { - headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': appId, - }, - } - ); - const response = res.data.results; - let data; - // console.log("response", response); - if (response && response.length > 0) { - data = { - usedStorage: response[0].usedStorage ? response[0].usedStorage + size : size, + try { + const tenantQuery = new Parse.Query('partners_Tenant'); + tenantQuery.equalTo('UserId', { + __type: 'Pointer', + className: '_User', + objectId: userId, + }); + const tenant = await tenantQuery.first(); + if (tenant) { + const tenantPtr = { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenant.id, }; - await axios.put(`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`, data, { - headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': appId, - }, - }); - } else { - data = { usedStorage: size, PartnersTenant: tenantPtr }; - await axios.post(`${serverUrl}classes/partners_TenantCredits`, data, { - headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': parseAppId, - }, - }); + const _tenantPtr = JSON.stringify(tenantPtr); + try { + const res = await axios.get( + `${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, + { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + }, + } + ); + const response = res.data.results; + + let data; + // console.log("response", response); + if (response && response.length > 0) { + data = { + usedStorage: response[0].usedStorage ? response[0].usedStorage + size : size, + }; + await axios.put( + `${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`, + data, + { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + }, + } + ); + } else { + data = { usedStorage: size, PartnersTenant: tenantPtr }; + await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + }, + }); + } + } catch (err) { + console.log('err in save usage', err); + } + saveDataFile(size, imageUrl, tenantPtr); } } catch (err) { - console.log('err in save usage', err); + console.log('err in fetch tenant Id', err); } - saveDataFile(size, imageUrl, tenantPtr); }; //function for save fileUrl and file size in particular client db class partners_DataFiles @@ -100,10 +118,10 @@ const saveDataFile = async (size, imageUrl, tenantPtr) => { // console.log("data save",file, data) try { - await axios.post(`${serverUrl}classes/partners_DataFiles`, data, { + await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, { headers: { 'Content-Type': 'application/json', - 'X-Parse-Application-Id': parseAppId, + 'X-Parse-Application-Id': appId, }, }); } catch (err) { diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index ba7d7b22d..6f7d1ae20 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { color, customAPIurl, replaceMailVaribles } from '../../../../Utils.js'; +import { color, customAPIurl, replaceMailVaribles, saveFileUsage } from '../../../../Utils.js'; // `sendDoctoWebhook` is used to send res data of document on webhook async function sendDoctoWebhook(doc, WebhookUrl, userId) { @@ -8,6 +8,7 @@ async function sendDoctoWebhook(doc, WebhookUrl, userId) { event: 'created', ...doc, }; + await axios .post(WebhookUrl, params, { headers: { 'Content-Type': 'application/json' }, @@ -65,6 +66,7 @@ export default async function createDocumentwithCoordinate(request, response) { if (!reqToken) { return response.status(400).json({ error: 'Please Provide API Token' }); } + try { const tokenQuery = new Parse.Query('appToken'); tokenQuery.equalTo('token', reqToken); @@ -81,17 +83,22 @@ export default async function createDocumentwithCoordinate(request, response) { if (signers && signers.length > 0) { let fileUrl; if (request.files?.[0]) { + const base64 = fileData?.toString('base64'); const file = new Parse.File(request.files?.[0]?.originalname, { - base64: fileData?.toString('base64'), + base64: base64, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } else { const file = new Parse.File(`${name}.pdf`, { base64: base64File, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } const contractsUser = new Parse.Query('contracts_Users'); contractsUser.equalTo('UserId', userPtr); @@ -242,7 +249,6 @@ export default async function createDocumentwithCoordinate(request, response) { year: 'numeric', }); let sender = parseExtUser.Email; - let sendMail; const serverUrl = process.env.SERVER_URL; const newServer = serverUrl.replaceAll('/', '%2F'); const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`; @@ -332,7 +338,7 @@ export default async function createDocumentwithCoordinate(request, response) { html: html, }; - sendMail = await axios.post(url, params, { headers: headers }); + await axios.post(url, params, { headers: headers }); } catch (error) { console.log('error', error); } diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplate.js index 2219dc2df..a0874f1e0 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplate.js @@ -1,6 +1,4 @@ -import { customAPIurl } from '../../../../Utils.js'; - -const randomId = () => Math.floor(1000 + Math.random() * 9000); +import { saveFileUsage } from '../../../../Utils.js'; export default async function createTemplate(request, response) { const name = request.body?.title; const note = request.body?.note; @@ -36,12 +34,17 @@ export default async function createTemplate(request, response) { }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } else { + const base64 = fileData?.toString('base64'); const file = new Parse.File(request.files?.[0]?.originalname, { - base64: fileData?.toString('base64'), + base64: base64, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } const contractsUser = new Parse.Query('contracts_Users'); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js index b2a4d2aef..28203575e 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { color, customAPIurl } from '../../../../Utils.js'; +import { color, customAPIurl, saveFileUsage } from '../../../../Utils.js'; const randomId = () => Math.floor(1000 + Math.random() * 9000); export default async function createTemplatewithCoordinate(request, response) { @@ -34,17 +34,22 @@ export default async function createTemplatewithCoordinate(request, response) { if (signers && signers.length > 0) { let fileUrl; if (request.files?.[0]) { + const base64 = fileData?.toString('base64'); const file = new Parse.File(request.files?.[0]?.originalname, { - base64: fileData?.toString('base64'), + base64: base64, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } else { const file = new Parse.File(`${name}.pdf`, { base64: base64File, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } const contractsUser = new Parse.Query('contracts_Users'); contractsUser.equalTo('UserId', userPtr); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js index 3dda24f5d..7f31b66bc 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js @@ -36,17 +36,22 @@ export default async function draftDocument(request, response) { if (signers && signers.length > 0) { let fileUrl; if (request.files?.[0]) { + const base64 = fileData?.toString('base64'); const file = new Parse.File(request.files?.[0]?.originalname, { - base64: fileData?.toString('base64'), + base64: base64, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } else { const file = new Parse.File(`${name}.pdf`, { base64: base64File, }); await file.save({ useMasterKey: true }); fileUrl = file.url(); + const buffer = Buffer.from(base64File, 'base64'); + saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId); } const contractsUser = new Parse.Query('contracts_Users'); contractsUser.equalTo('UserId', userPtr); diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index 166f4449e..b123d4e25 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -3,6 +3,7 @@ import fs from 'node:fs'; import axios from 'axios'; import { pdflibAddPlaceholder } from './customSignPdf/pdflibplaceholder.min.js'; import { PDFDocument } from 'pdf-lib'; +import { saveFileUsage } from '../../../Utils.js'; const serverUrl = process.env.SERVER_URL, APPID = process.env.APP_ID, masterKEY = process.env.MASTER_KEY; @@ -151,11 +152,11 @@ async function sendDoctoWebhook(t, e, a, s) { } })); } -async function PDF(i) { +async function PDF(o) { try { - var e = i.params.docId, - a = i.params.userId, - o = await axios.get( + var e = o.params.docId, + a = o.params.userId, + n = await axios.get( serverUrl + '/classes/contracts_Document/' + e + '?include=ExtUserPtr,Signers', { headers: { @@ -165,141 +166,145 @@ async function PDF(i) { }, } ), - t = await axios.get(serverUrl + '/users/me', { + d = await axios.get(serverUrl + '/users/me', { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': i.headers.sessiontoken, + 'X-Parse-Session-Token': o.headers.sessiontoken, }, }); - if (!t.data || !t.data.objectId) return { status: 'error', message: 'this user not allowed!' }; + if (!d.data || !d.data.objectId) return { status: 'error', message: 'This user not allowed!' }; { - var n, - d, + var t, + s, l, c = JSON.stringify({ objectId: a }); - let s, r; - r = a - ? (n = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, { + let r, i; + i = a + ? (t = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': i.headers.sessiontoken, + 'X-Parse-Session-Token': o.headers.sessiontoken, }, - })).data && 0 < n.data.results.length - ? ((s = n), 'contracts_Contactbook') - : ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, { + })).data && 0 < t.data.results.length + ? ((r = t), 'contracts_Contactbook') + : ((r = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, { headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY }, })), 'contracts_Users') - : ((d = JSON.stringify({ - UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId }, + : ((s = JSON.stringify({ + UserId: { __type: 'Pointer', className: '_User', objectId: d.data.objectId }, })), - (l = await axios.get(serverUrl + '/classes/contracts_Users?where=' + d, { + (l = await axios.get(serverUrl + '/classes/contracts_Users?where=' + s, { headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY }, })).data && 0 < l.data.results.length - ? ((s = l), 'contracts_Users') - : ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + d, { + ? ((r = l), 'contracts_Users') + : ((r = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + s, { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': i.headers.sessiontoken, + 'X-Parse-Session-Token': o.headers.sessiontoken, }, })), 'contracts_Contactbook')); - var p = s.data.results[0].Name, - m = s.data.results[0].Email; - if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' }; + var p = r.data.results[0].Name, + m = r.data.results[0].Email; + if (!o.params.pdfFile) return { status: 'error', message: 'pdf file not present!' }; { - let e = Buffer.from(i.params.pdfFile, 'base64'); + let e = Buffer.from(o.params.pdfFile, 'base64'); var g = process.env.PFX_BASE64, h = Buffer.from(g, 'base64'), u = { - UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId }, + UserPtr: { __type: 'Pointer', className: i, objectId: r.data.results[0].objectId }, SignedUrl: '', Activity: 'Signed', - ipAddress: i.headers['x-real-ip'], + ipAddress: o.headers['x-real-ip'], }; let a; var f = (a = - o.data.AuditTrail && 0 < o.data.AuditTrail.length - ? [...o.data.AuditTrail, u] + n.data.AuditTrail && 0 < n.data.AuditTrail.length + ? [...n.data.AuditTrail, u] : [u]).filter(e => 'Signed' === e.Activity); let t = !1; !( - (o.data.Signers && 0 < o.data.Signers.length && f.length !== o.data.Signers.length) || + (n.data.Signers && 0 < n.data.Signers.length && f.length !== n.data.Signers.length) || !(t = !0) ); var y, - P, v, + P, b, - x, - w, U, - I, - S, - A = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, - D = './exports/' + A, - E = - (t - ? ((y = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')), - (e = - y && 0 < y.length - ? ((P = await PDFDocument.load(e)), - pdflibAddPlaceholder({ - pdfDoc: P, - reason: 'Digitally signed by OpenSign for ' + y?.join(', '), - location: 'location', - signatureLength: 15e3, - }), - (v = await P.save()), - Buffer.from(v)) - : ((b = await PDFDocument.load(e)), - pdflibAddPlaceholder({ - pdfDoc: b, - reason: 'Digitally signed by OpenSign for ' + p + ' <' + m + '>', - location: 'location', - signatureLength: 15e3, - }), - (x = await b.save()), - Buffer.from(x))), - (w = await new SignPDF(e, h).signPDF()), - fs.writeFileSync(D, w)) - : fs.writeFileSync(D, e), - await uploadFile(A, D)); + x, + w = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, + I = './exports/' + w; + let s = e.length; + s = ( + t + ? ((y = n.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')), + (e = + y && 0 < y.length + ? ((v = await PDFDocument.load(e)), + pdflibAddPlaceholder({ + pdfDoc: v, + reason: 'Digitally signed by OpenSign for ' + y?.join(', '), + location: 'location', + signatureLength: 15e3, + }), + (P = await v.save()), + Buffer.from(P)) + : ((b = await PDFDocument.load(e)), + pdflibAddPlaceholder({ + pdfDoc: b, + reason: 'Digitally signed by OpenSign for ' + p + ' <' + m + '>', + location: 'location', + signatureLength: 15e3, + }), + (U = await b.save()), + Buffer.from(U))), + (x = await new SignPDF(e, h).signPDF()), + fs.writeFileSync(I, x), + x) + : (fs.writeFileSync(I, e), e) + ).length; + var S, + A, + D, + E = await uploadFile(w, I); if (E && E.imageUrl) return ( - (U = await updateDoc( - i.params.docId, + (S = await updateDoc( + o.params.docId, E.imageUrl, - s.data.results[0].objectId, - i.headers['x-real-ip'], - o.data, - r + r.data.results[0].objectId, + o.headers['x-real-ip'], + n.data, + i )), - (I = { + (A = { url: E.imageUrl, - sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name }, - pdfName: o.data.Name, + sender: { Mail: n.data.ExtUserPtr.Email, Name: n.data.ExtUserPtr.Name }, + pdfName: n.data.Name, receiver: m, }), - o.data.IsSendMail && !1 === o.data.IsSendMail + n.data.IsSendMail && !1 === n.data.IsSendMail ? console.log("don't send mail") - : sendMail(I), - sendDoctoWebhook(o, E.imageUrl, 'signed', s?.data.results?.[0]), - U && - U.isCompleted && - ((S = { + : sendMail(A), + sendDoctoWebhook(n, E.imageUrl, 'signed', r?.data.results?.[0]), + saveFileUsage(s, E.imageUrl, d.data.objectId), + S && + S.isCompleted && + ((D = { url: E.imageUrl, - sender: { Mail: o.data.ExtUserPtr.Email, Name: 'OpenSign™' }, - pdfName: o.data.Name, - receiver: o.data.ExtUserPtr.Email, + sender: { Mail: n.data.ExtUserPtr.Email, Name: 'OpenSign™' }, + pdfName: n.data.Name, + receiver: n.data.ExtUserPtr.Email, }), - o.data.IsSendMail && !1 === o.data.IsSendMail + n.data.IsSendMail && !1 === n.data.IsSendMail ? console.log("don't send mail") - : sendCompletedMail(S), - sendDoctoWebhook(o, E.imageUrl, 'completed')), - fs.unlinkSync(D), - console.log('New Signed PDF created called: ' + D), - 'success' === U.message + : sendCompletedMail(D), + sendDoctoWebhook(n, E.imageUrl, 'completed')), + fs.unlinkSync(I), + console.log('New Signed PDF created called: ' + I), + 'success' === S.message ? { status: 'success', data: E.imageUrl } : { status: 'error', message: 'please provide required parameters!' } ); From de26c2d71912b7c94e9eb6e5a539587ff7fe6652 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Fri, 29 Mar 2024 17:51:59 +0530 Subject: [PATCH 09/26] fix: handle condition of free validation warning message --- .../components/pdf/DropdownWidgetOption.js | 24 +++++++++-------- .../src/components/pdf/WidgetNameModal.js | 26 ++++++++++++------- apps/OpenSign/src/constant/Utils.js | 2 +- apps/OpenSign/src/pages/GenerateToken.js | 10 +++---- apps/OpenSign/src/pages/UserProfile.js | 18 +++++++------ apps/OpenSign/src/pages/Webhook.js | 10 +++---- apps/OpenSign/src/primitives/Tooltip.js | 3 ++- 7 files changed, 52 insertions(+), 41 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js index 08de190f7..3f95eaac4 100644 --- a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js +++ b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { themeColor } from "../../constant/const"; +import { isEnableSubscription, themeColor } from "../../constant/const"; import ModalUi from "../../primitives/ModalUi"; import { radioButtonWidget } from "../../constant/Utils"; import PremiumAlertHeader from "../../primitives/PremiumAlertHeader"; @@ -254,7 +254,7 @@ function DropdownWidgetOption(props) { > Minimun check </label> - {!props.isSubscribe && <Upgrade />} + {!props.isSubscribe && isEnableSubscription && <Upgrade />} <input required defaultValue={0} @@ -264,7 +264,7 @@ function DropdownWidgetOption(props) { setMinCount(count); }} className={ - props.isSubscribe + props.isSubscribe || !isEnableSubscription ? "drodown-input" : "disabled drodown-input" } @@ -287,7 +287,7 @@ function DropdownWidgetOption(props) { setMaxCount(count); }} className={ - props.isSubscribe + props.isSubscribe || !isEnableSubscription ? "drodown-input" : "disabled drodown-input" } @@ -402,13 +402,15 @@ 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." - } - /> - )} + {props.type === "checkbox" && + !props.isSignYourself && + !isEnableSubscription && ( + <PremiumAlertHeader + message={ + "Field validations are free in beta, this feature will incur a fee later." + } + /> + )} <div className={`${ props.type === "checkbox" && !props.isSignYourself diff --git a/apps/OpenSign/src/components/pdf/WidgetNameModal.js b/apps/OpenSign/src/components/pdf/WidgetNameModal.js index eb8bfb937..baaadadba 100644 --- a/apps/OpenSign/src/components/pdf/WidgetNameModal.js +++ b/apps/OpenSign/src/components/pdf/WidgetNameModal.js @@ -5,6 +5,7 @@ import RegexParser from "regex-parser"; import { textInputWidget } from "../../constant/Utils"; import PremiumAlertHeader from "../../primitives/PremiumAlertHeader"; import Upgrade from "../../primitives/Upgrade"; +import { isEnableSubscription } from "../../constant/const"; const WidgetNameModal = (props) => { const [formdata, setFormdata] = useState({ @@ -101,13 +102,14 @@ const WidgetNameModal = (props) => { title={"Widget info"} > {(props.defaultdata?.type === textInputWidget || - props.widgetName === textInputWidget) && ( - <PremiumAlertHeader - message={ - "Field validations are free in beta, this feature will incur a fee later." - } - /> - )} + props.widgetName === textInputWidget) && + !isEnableSubscription && ( + <PremiumAlertHeader + message={ + "Field validations are free in beta, this feature will incur a fee later." + } + /> + )} <form onSubmit={handleSubmit} className={`${ @@ -136,14 +138,18 @@ const WidgetNameModal = (props) => { <div className="form-section"> <label htmlFor="textvalidate" - className="disabled" + className={ + !props.isSubscribe && isEnableSubscription && "disabled" + } style={{ fontSize: 13 }} > Validation </label> - {!props.isSubscribe && <Upgrade />} + {!props.isSubscribe && isEnableSubscription && <Upgrade />} <div - className="disabled" + className={ + !props.isSubscribe && isEnableSubscription && "disabled" + } style={{ display: "flex", flexDirection: "row", diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 4483bfe78..bd5b45b67 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -26,7 +26,7 @@ export async function checkIsSubscribed() { const user = await Parse.Cloud.run("getUserDetails", { email: jsonSender[0].Email }); - const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; + const freeplan = user?.get("Plan") && user?.get("Plan")?.plan_code; const billingDate = user?.get("Next_billing_date") && user?.get("Next_billing_date"); if (freeplan === "freeplan") { diff --git a/apps/OpenSign/src/pages/GenerateToken.js b/apps/OpenSign/src/pages/GenerateToken.js index 026dfb7e9..9e4035191 100644 --- a/apps/OpenSign/src/pages/GenerateToken.js +++ b/apps/OpenSign/src/pages/GenerateToken.js @@ -125,10 +125,10 @@ function GenerateToken() { </div> ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> - <PremiumAlertHeader /> + {!isEnableSubscription && <PremiumAlertHeader />} <h1 className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "ml-4 mt-3 mb-2 font-semibold" : "ml-4 mt-3 mb-2 font-semibold text-gray-300" } @@ -140,11 +140,11 @@ function GenerateToken() { } iconColor={!isSubscribe && "gray"} /> - {!isSubscribe && <Upgrade />} + {!isSubscribe && isEnableSubscription && <Upgrade />} </h1> <ul className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "w-full flex flex-col p-2 text-sm " : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" } @@ -164,7 +164,7 @@ function GenerateToken() { </ul> <div className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" } diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 4048a04ba..648c8eaae 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -276,7 +276,7 @@ function UserProfile() { <div className="flex justify-between items-center py-2"> <span className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "font-semibold" : "font-semibold text-gray-300" } @@ -288,11 +288,11 @@ function UserProfile() { } isSubscribe={isSubscribe} />{" "} - {!isSubscribe && <Upgrade />} + {!isSubscribe && isEnableSubscription && <Upgrade />} </span>{" "} <label className={ - !isSubscribe + !isSubscribe || isEnableSubscription ? "relative inline-flex items-center mb-0 pointer-events-none opacity-50" : `${ editmode ? "cursor-pointer" : "" @@ -310,11 +310,13 @@ function UserProfile() { <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> </div> - <PremiumAlertHeader - message={ - "Disable documentId is free in beta, this feature will incur a fee later." - } - /> + {!isEnableSubscription && ( + <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"> diff --git a/apps/OpenSign/src/pages/Webhook.js b/apps/OpenSign/src/pages/Webhook.js index b89bda460..12e4fef17 100644 --- a/apps/OpenSign/src/pages/Webhook.js +++ b/apps/OpenSign/src/pages/Webhook.js @@ -109,10 +109,10 @@ function Webhook() { </div> ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> - <PremiumAlertHeader /> + {!isEnableSubscription && <PremiumAlertHeader />} <h1 className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "ml-4 mt-3 mb-2 font-semibold" : "ml-4 mt-3 mb-2 font-semibold text-gray-300" } @@ -122,11 +122,11 @@ function Webhook() { url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"} isSubscribe={isSubscribe} /> - {!isSubscribe && <Upgrade />} + {!isSubscribe && isEnableSubscription && <Upgrade />} </h1> <ul className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "w-full flex flex-col p-2 text-sm " : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" } @@ -142,7 +142,7 @@ function Webhook() { </ul> <div className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" } diff --git a/apps/OpenSign/src/primitives/Tooltip.js b/apps/OpenSign/src/primitives/Tooltip.js index 2a8006deb..f49e4bce7 100644 --- a/apps/OpenSign/src/primitives/Tooltip.js +++ b/apps/OpenSign/src/primitives/Tooltip.js @@ -1,12 +1,13 @@ import React from "react"; import { Tooltip as ReactTooltip } from "react-tooltip"; import { openInNewTab } from "../constant/Utils"; +import { isEnableSubscription } from "../constant/const"; const Tooltip = ({ id, message, url, iconColor, isSubscribe }) => url ? ( <button onClick={() => openInNewTab(url)} className={ - isSubscribe + isSubscribe || !isEnableSubscription ? "text-center" : "text-center opacity-20 pointer-events-none" } From 741f66b019a4e227cb355cd99e9118eed775df0e Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 08:28:17 +0530 Subject: [PATCH 10/26] feat: add documentCount and templateCount for user --- apps/OpenSign/src/pages/PlaceHolderSign.js | 1 - apps/OpenSignServer/cloud/main.js | 4 +++ .../cloud/parsefunction/DocumentBeforesave.js | 35 +++++++++++++++++++ .../cloud/parsefunction/SubscribeFree.js | 4 +-- .../cloud/parsefunction/TemplateBeforesave.js | 31 ++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index b176a043f..132f49934 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -762,7 +762,6 @@ function PlaceHolderSign() { const pdfUrl = pdfData.url(); const tenantId = localStorage.getItem("TenantId"); const buffer = atob(pdfBytes) - console.log("buffer", buffer.length) SaveFileSize(buffer.length, pdfUrl, tenantId); return pdfUrl; } catch (e) { diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 6e1141cdd..d6f0adaf6 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -24,6 +24,8 @@ import GetTemplate from './parsefunction/GetTemplate.js'; import savewebhook from './parsefunction/saveWebhook.js'; import callWebhook from './parsefunction/callWebhook.js'; import SubscribeFree from './parsefunction/SubscribeFree.js'; +import DocumentBeforesave from './parsefunction/DocumentBeforesave.js'; +import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); @@ -51,3 +53,5 @@ Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave); Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave); +Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave); +Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave); diff --git a/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js new file mode 100644 index 000000000..14358e098 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js @@ -0,0 +1,35 @@ +async function DocumentBeforesave(request) { + try { + // below code is used to update document when user sent document or self signed + const document = request.object; + const oldDocument = request.original; + + // Check if SignedUrl field has been added (transition from undefined to defined) + if (!oldDocument.get('SignedUrl') && document.get('SignedUrl')) { + const SignedUrl = document.get('SignedUrl'); + + // Update count in contracts_Users class + const query = new Parse.Query('contracts_Users'); + query.equalTo('objectId', oldDocument.get('ExtUserPtr').id); + + try { + const contractUser = await query.first({ useMasterKey: true }); + if (contractUser) { + contractUser.increment('DocumentCount', 1); + await contractUser.save(null, { useMasterKey: true }); + } else { + // Create new entry if not found + const ContractsUsers = Parse.Object.extend('contracts_users'); + const newContractUser = new ContractsUsers(); + newContractUser.set('DocumentCount', 1); + await newContractUser.save(null, { useMasterKey: true }); + } + } catch (error) { + console.log('Error updating document count in contracts_users: ' + error.message); + } + } + } catch (err) { + console.log('err in document beforesave', err.message); + } +} +export default DocumentBeforesave; diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js index 2bcf48500..d2f63cdc5 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js @@ -6,11 +6,11 @@ export default async function SubscribeFree(request) { extQuery.equalTo('UserId', userPtr); const extUser = await extQuery.first({ useMasterKey: true }); - if (extUser) { + if (extUser && extUser?.Next_billing_date < new Date()) { try { const extUpdate = new Parse.Object('contracts_Users'); extUpdate.id = extUser.id; - extUpdate.set('Plan', { plan_code: 'freeplan' }); + extUpdate.set('Plan', { plan_code: 'freeplan' }); const updatePlan = await extUpdate.save(null, { useMasterKey: true }); return { status: 'success', result: 'subscribed!' }; } catch (err) { diff --git a/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js b/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js new file mode 100644 index 000000000..b2ba6e3d3 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js @@ -0,0 +1,31 @@ +async function TemplateBeforeSave(request) { + try { + if (!request.original) { + // below code is used to update template when user sent template or self signed + const template = request.object; + + // Update count in contracts_Users class + const query = new Parse.Query('contracts_Users'); + query.equalTo('objectId', template.get('ExtUserPtr').id); + + try { + const contractUser = await query.first({ useMasterKey: true }); + if (contractUser) { + contractUser.increment('TemplateCount', 1); + await contractUser.save(null, { useMasterKey: true }); + } else { + // Create new entry if not found + const ContractsUsers = Parse.Object.extend('contracts_users'); + const newContractUser = new ContractsUsers(); + newContractUser.set('TemplateCount', 1); + await newContractUser.save(null, { useMasterKey: true }); + } + } catch (error) { + console.log('Error updating template count in contracts_users: ' + error.message); + } + } + } catch (err) { + console.log('err in template beforesave', err.message); + } +} +export default TemplateBeforeSave; From 5930fd940d903dde9710e50497a085f33e480936 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 09:08:27 +0530 Subject: [PATCH 11/26] refactor: add loader in for free subscription --- apps/OpenSign/src/pages/PlanSubscriptions.js | 23 ++++++++++++++--- .../cloud/parsefunction/SubscribeFree.js | 25 +++++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index abae7edbc..bff48d33f 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -45,10 +45,25 @@ const PlanSubscriptions = () => { }, []); const handleFreePlan = async () => { - const params = { userId: Parse.User.current().id }; - const res = await Parse.Cloud.run("freesubscription", params); - if (res.status === "success") { - navigate("/"); + setIsLoader(true); + try { + const params = { userId: Parse.User.current().id }; + const res = await Parse.Cloud.run("freesubscription", params); + console.log("res.result ", res.result); + if (res.status === "success" && res.result === "already subscribed!") { + setIsLoader(false); + alert("You have already subscription of free plan"); + } else if (res.status === "success") { + setIsLoader(false); + navigate("/"); + } else if (res.status === "error") { + setIsLoader(false); + alert(res.result); + } + } catch (err) { + setIsLoader(false); + console.log("err in free subscribe", err.message); + alert("Somenthing went wrong, please try again later!"); } }; return ( diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js index d2f63cdc5..70d5d4834 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js @@ -5,17 +5,20 @@ export default async function SubscribeFree(request) { const extQuery = new Parse.Query('contracts_Users'); extQuery.equalTo('UserId', userPtr); const extUser = await extQuery.first({ useMasterKey: true }); - - if (extUser && extUser?.Next_billing_date < new Date()) { - try { - const extUpdate = new Parse.Object('contracts_Users'); - extUpdate.id = extUser.id; - extUpdate.set('Plan', { plan_code: 'freeplan' }); - const updatePlan = await extUpdate.save(null, { useMasterKey: true }); - return { status: 'success', result: 'subscribed!' }; - } catch (err) { - console.log('err ', err); - return { status: 'error', result: err.message }; + if (extUser) { + if (extUser?.get('Plan')?.plan_code === 'freeplan') { + return { status: 'success', result: 'already subscribed!' }; + } else if (extUser?.get('Next_billing_date') < new Date()) { + try { + const extUpdate = new Parse.Object('contracts_Users'); + extUpdate.id = extUser.id; + extUpdate.set('Plan', { plan_code: 'freeplan' }); + const updatePlan = await extUpdate.save(null, { useMasterKey: true }); + return { status: 'success', result: 'subscribed!' }; + } catch (err) { + console.log('err ', err); + return { status: 'error', result: err.message }; + } } } else { return { status: 'error', result: 'User not found!' }; From d588caf6d4ee084b103da7c84d0da4ee79562dd7 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 09:28:35 +0530 Subject: [PATCH 12/26] fix: not able to subscribed free plan & stop sending individual mail --- .../cloud/parsefunction/SubscribeFree.js | 13 +- .../cloud/parsefunction/pdf/PDF.min.js | 143 +++++++----------- 2 files changed, 66 insertions(+), 90 deletions(-) diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js index 70d5d4834..674d56223 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js @@ -13,7 +13,18 @@ export default async function SubscribeFree(request) { const extUpdate = new Parse.Object('contracts_Users'); extUpdate.id = extUser.id; extUpdate.set('Plan', { plan_code: 'freeplan' }); - const updatePlan = await extUpdate.save(null, { useMasterKey: true }); + await extUpdate.save(null, { useMasterKey: true }); + return { status: 'success', result: 'subscribed!' }; + } catch (err) { + console.log('err ', err); + return { status: 'error', result: err.message }; + } + } else { + try { + const extUpdate = new Parse.Object('contracts_Users'); + extUpdate.id = extUser.id; + extUpdate.set('Plan', { plan_code: 'freeplan' }); + await extUpdate.save(null, { useMasterKey: true }); return { status: 'success', result: 'subscribed!' }; } catch (err) { console.log('err ', err); diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index b123d4e25..37d9a39da 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -17,20 +17,20 @@ async function uploadFile(e, a) { console.log('Err ', e), fs.unlinkSync(a); } } -async function updateDoc(t, s, r, i, o, n) { +async function updateDoc(t, s, r, o, i, n) { try { var d = { UserPtr: { __type: 'Pointer', className: n, objectId: r }, SignedUrl: s, Activity: 'Signed', - ipAddress: i, + ipAddress: o, }; let e; - var l = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, d] : [d]).filter( + var l = (e = i.AuditTrail && 0 < i.AuditTrail.length ? [...i.AuditTrail, d] : [d]).filter( e => 'Signed' === e.Activity ); let a = !1; - !((o.Signers && 0 < o.Signers.length && l.length !== o.Signers.length) || !(a = !0)); + !((i.Signers && 0 < i.Signers.length && l.length !== i.Signers.length) || !(a = !0)); var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a }; await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, { headers: { @@ -44,31 +44,6 @@ async function updateDoc(t, s, r, i, o, n) { return console.log('update doc err ', e), 'err'; } } -async function sendMail(e) { - var a = e.url, - t = e.sender, - s = e.pdfName, - a = { - url: a, - from: 'OpenSign™', - recipient: e.receiver, - subject: 'You have signed the doc - ' + s, - pdfName: s, - html: - "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " + - s + - ' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' + - t.Mail + - ' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>', - }; - await axios.post(serverUrl + '/functions/sendmailv3', a, { - headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': APPID, - 'X-Parse-Master-Key': masterKEY, - }, - }); -} async function sendCompletedMail(e) { var a = e.url, t = e.sender, @@ -152,10 +127,10 @@ async function sendDoctoWebhook(t, e, a, s) { } })); } -async function PDF(o) { +async function PDF(i) { try { - var e = o.params.docId, - a = o.params.userId, + var e = i.params.docId, + a = i.params.userId, n = await axios.get( serverUrl + '/classes/contracts_Document/' + e + '?include=ExtUserPtr,Signers', { @@ -169,7 +144,7 @@ async function PDF(o) { d = await axios.get(serverUrl + '/users/me', { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': o.headers.sessiontoken, + 'X-Parse-Session-Token': i.headers.sessiontoken, }, }); if (!d.data || !d.data.objectId) return { status: 'error', message: 'This user not allowed!' }; @@ -178,12 +153,12 @@ async function PDF(o) { s, l, c = JSON.stringify({ objectId: a }); - let r, i; - i = a + let r, o; + o = a ? (t = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': o.headers.sessiontoken, + 'X-Parse-Session-Token': i.headers.sessiontoken, }, })).data && 0 < t.data.results.length ? ((r = t), 'contracts_Contactbook') @@ -201,56 +176,56 @@ async function PDF(o) { : ((r = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + s, { headers: { 'X-Parse-Application-Id': APPID, - 'X-Parse-Session-Token': o.headers.sessiontoken, + 'X-Parse-Session-Token': i.headers.sessiontoken, }, })), 'contracts_Contactbook')); var p = r.data.results[0].Name, m = r.data.results[0].Email; - if (!o.params.pdfFile) return { status: 'error', message: 'pdf file not present!' }; + if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' }; { - let e = Buffer.from(o.params.pdfFile, 'base64'); + let e = Buffer.from(i.params.pdfFile, 'base64'); var g = process.env.PFX_BASE64, - h = Buffer.from(g, 'base64'), - u = { - UserPtr: { __type: 'Pointer', className: i, objectId: r.data.results[0].objectId }, + u = Buffer.from(g, 'base64'), + h = { + UserPtr: { __type: 'Pointer', className: o, objectId: r.data.results[0].objectId }, SignedUrl: '', Activity: 'Signed', - ipAddress: o.headers['x-real-ip'], + ipAddress: i.headers['x-real-ip'], }; let a; var f = (a = n.data.AuditTrail && 0 < n.data.AuditTrail.length - ? [...n.data.AuditTrail, u] - : [u]).filter(e => 'Signed' === e.Activity); + ? [...n.data.AuditTrail, h] + : [h]).filter(e => 'Signed' === e.Activity); let t = !1; !( (n.data.Signers && 0 < n.data.Signers.length && f.length !== n.data.Signers.length) || !(t = !0) ); - var y, + var P, + y, v, - P, b, U, - x, - w = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, - I = './exports/' + w; + I, + S = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, + A = './exports/' + S; let s = e.length; s = ( t - ? ((y = n.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')), + ? ((P = n.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')), (e = - y && 0 < y.length - ? ((v = await PDFDocument.load(e)), + P && 0 < P.length + ? ((y = await PDFDocument.load(e)), pdflibAddPlaceholder({ - pdfDoc: v, - reason: 'Digitally signed by OpenSign for ' + y?.join(', '), + pdfDoc: y, + reason: 'Digitally signed by OpenSign for ' + P?.join(', '), location: 'location', signatureLength: 15e3, }), - (P = await v.save()), - Buffer.from(P)) + (v = await y.save()), + Buffer.from(v)) : ((b = await PDFDocument.load(e)), pdflibAddPlaceholder({ pdfDoc: b, @@ -260,40 +235,30 @@ async function PDF(o) { }), (U = await b.save()), Buffer.from(U))), - (x = await new SignPDF(e, h).signPDF()), - fs.writeFileSync(I, x), - x) - : (fs.writeFileSync(I, e), e) + (I = await new SignPDF(e, u).signPDF()), + fs.writeFileSync(A, I), + I) + : (fs.writeFileSync(A, e), e) ).length; - var S, - A, + var w, D, - E = await uploadFile(w, I); - if (E && E.imageUrl) + x = await uploadFile(S, A); + if (x && x.imageUrl) return ( - (S = await updateDoc( - o.params.docId, - E.imageUrl, + (w = await updateDoc( + i.params.docId, + x.imageUrl, r.data.results[0].objectId, - o.headers['x-real-ip'], + i.headers['x-real-ip'], n.data, - i + o )), - (A = { - url: E.imageUrl, - sender: { Mail: n.data.ExtUserPtr.Email, Name: n.data.ExtUserPtr.Name }, - pdfName: n.data.Name, - receiver: m, - }), - n.data.IsSendMail && !1 === n.data.IsSendMail - ? console.log("don't send mail") - : sendMail(A), - sendDoctoWebhook(n, E.imageUrl, 'signed', r?.data.results?.[0]), - saveFileUsage(s, E.imageUrl, d.data.objectId), - S && - S.isCompleted && + sendDoctoWebhook(n, x.imageUrl, 'signed', r?.data.results?.[0]), + saveFileUsage(s, x.imageUrl, d.data.objectId), + w && + w.isCompleted && ((D = { - url: E.imageUrl, + url: x.imageUrl, sender: { Mail: n.data.ExtUserPtr.Email, Name: 'OpenSign™' }, pdfName: n.data.Name, receiver: n.data.ExtUserPtr.Email, @@ -301,11 +266,11 @@ async function PDF(o) { n.data.IsSendMail && !1 === n.data.IsSendMail ? console.log("don't send mail") : sendCompletedMail(D), - sendDoctoWebhook(n, E.imageUrl, 'completed')), - fs.unlinkSync(I), - console.log('New Signed PDF created called: ' + I), - 'success' === S.message - ? { status: 'success', data: E.imageUrl } + sendDoctoWebhook(n, x.imageUrl, 'completed')), + fs.unlinkSync(A), + console.log('New Signed PDF created called: ' + A), + 'success' === w.message + ? { status: 'success', data: x.imageUrl } : { status: 'error', message: 'please provide required parameters!' } ); } From 5adaffc684dacfa49b870edd529ce92958c752ae Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 12:42:08 +0530 Subject: [PATCH 13/26] feat: add emailcount of user --- .../src/components/pdf/EmailComponent.js | 4 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 4 +- apps/OpenSign/src/pages/PlaceHolderSign.js | 5 +- apps/OpenSign/src/pages/PlanSubscriptions.js | 1 - apps/OpenSign/src/pages/SignyourselfPdf.js | 12 ++-- apps/OpenSignServer/Utils.js | 24 ++++++++ .../v1/routes/CreateDocumentWithTemplate.js | 1 + .../v1/routes/createDocumentwithCoordinate.js | 1 + apps/OpenSignServer/cloud/main.js | 6 +- .../cloud/parsefunction/SendMailOTPv1.js | 53 ++++++++-------- .../cloud/parsefunction/SendMailv1.js | 27 -------- .../cloud/parsefunction/pdf/PDF.min.js | 61 ++++++++++--------- .../{sendMail.js => sendMailv3.js} | 19 +++++- apps/OpenSignServer/index.js | 10 +-- 14 files changed, 126 insertions(+), 102 deletions(-) delete mode 100644 apps/OpenSignServer/cloud/parsefunction/SendMailv1.js rename apps/OpenSignServer/cloud/parsefunction/{sendMail.js => sendMailv3.js} (88%) diff --git a/apps/OpenSign/src/components/pdf/EmailComponent.js b/apps/OpenSign/src/components/pdf/EmailComponent.js index 3ca39f177..879ff5fa2 100644 --- a/apps/OpenSign/src/components/pdf/EmailComponent.js +++ b/apps/OpenSign/src/components/pdf/EmailComponent.js @@ -15,7 +15,8 @@ function EmailComponent({ setSuccessEmail, pdfName, sender, - setIsAlert + setIsAlert, + extUserId }) { const [emailList, setEmailList] = useState([]); const [emailValue, setEmailValue] = useState(); @@ -38,6 +39,7 @@ function EmailComponent({ const openSignUrl = "https://www.opensignlabs.com/contact-us"; const themeBGcolor = themeColor; let params = { + extUserId: extUserId, pdfName: pdfName, url: pdfUrl, recipient: emailList[i], diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 8df0ad294..7c05fa4ec 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -94,6 +94,7 @@ function PdfRequestFiles() { const [sendInOrder, setSendInOrder] = useState(false); const [currWidgetsDetails, setCurrWidgetsDetails] = useState({}); const [isSubscribed, setIsSubscribed] = useState(false); + const [extUserId, setExtUserId] = useState(""); const divRef = useRef(null); const isMobile = window.innerWidth < 767; const rowLevel = @@ -169,6 +170,7 @@ function PdfRequestFiles() { if (isEnableSubscription) { await checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); } + setExtUserId(documentData[0]?.ExtUserPtr?.objectId); const isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted; const expireDate = documentData[0].ExpiryDate.iso; @@ -624,10 +626,10 @@ function PdfRequestFiles() { : ""; const themeBGcolor = themeColor; let params = { + extUserId: extUserId, recipient: user.Email, subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, from: sender, - html: "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + imgPng + diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 132f49934..2162b3202 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -110,6 +110,7 @@ function PlaceHolderSign() { status: false, message: "" }); + const [extUserId, setExtUserId] = useState(""); const isMobile = window.innerWidth < 767; const [, drop] = useDrop({ accept: "BOX", @@ -206,6 +207,7 @@ function PlaceHolderSign() { //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { + setExtUserId(documentData[0]?.ExtUserPtr?.objectId); if (isEnableSubscription) { checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email); } @@ -761,7 +763,7 @@ function PlaceHolderSign() { const pdfData = await pdfFile.save(); const pdfUrl = pdfData.url(); const tenantId = localStorage.getItem("TenantId"); - const buffer = atob(pdfBytes) + const buffer = atob(pdfBytes); SaveFileSize(buffer.length, pdfUrl, tenantId); return pdfUrl; } catch (e) { @@ -856,6 +858,7 @@ function PlaceHolderSign() { : ""; const themeBGcolor = themeColor; let params = { + extUserId: extUserId, recipient: signerMail[i].Email, subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, from: sender, diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index bff48d33f..954cc432d 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -49,7 +49,6 @@ const PlanSubscriptions = () => { try { const params = { userId: Parse.User.current().id }; const res = await Parse.Cloud.run("freesubscription", params); - console.log("res.result ", res.result); if (res.status === "success" && res.result === "already subscribed!") { setIsLoader(false); alert("You have already subscription of free plan"); diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 4538732f8..9cdef9b7a 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -99,6 +99,7 @@ function SignYourSelf() { }); const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" }); const [isDontShow, setIsDontShow] = useState(false); + const [extUserId, setExtUserId] = useState(""); const divRef = useRef(null); const nodeRef = useRef(null); const [, drop] = useDrop({ @@ -186,7 +187,7 @@ function SignYourSelf() { if (documentData && documentData.length > 0) { setPdfDetails(documentData); - + setExtUserId(documentData[0]?.ExtUserPtr?.objectId); isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted; if (isCompleted) { const docStatus = { @@ -433,13 +434,13 @@ function SignYourSelf() { Width: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).width - : "", + ? defaultWidthHeight(dragTypeValue).width + : "", Height: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).height - : "", + ? defaultWidthHeight(dragTypeValue).height + : "", options: addWidgetOptions(dragTypeValue) }; @@ -1120,6 +1121,7 @@ function SignYourSelf() { setSuccessEmail={setSuccessEmail} sender={jsonSender} setIsAlert={setIsAlert} + extUserId={extUserId} /> {/* pdf header which contain funish back button */} <Header diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 50d5027c5..a6c8cb6c6 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -128,3 +128,27 @@ const saveDataFile = async (size, imageUrl, tenantPtr) => { console.log('error in save usage ', err); } }; + +export const updateMailCount = async extUserId => { + // Update count in contracts_Users class + const query = new Parse.Query('contracts_Users'); + query.equalTo('objectId', extUserId); + + try { + const contractUser = await query.first({ useMasterKey: true }); + console.log("contractUser", contractUser) + + if (contractUser) { + contractUser.increment('EmailCount', 1); + await contractUser.save(null, { useMasterKey: true }); + } else { + // Create new entry if not found + const ContractsUsers = Parse.Object.extend('contracts_users'); + const newContractUser = new ContractsUsers(); + newContractUser.set('EmailCount', 1); + await newContractUser.save(null, { useMasterKey: true }); + } + } catch (error) { + console.log('Error updating EmailCount in contracts_users: ' + error.message); + } +}; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index 970c95b5f..b3b6f2179 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -284,6 +284,7 @@ export default async function createDocumentWithTemplate(request, response) { subject: subject, from: sender, html: html, + extUserId: template.ExtUserPtr.objectId, }; sendMail = await axios.post(url, params, { headers: headers }); } catch (error) { diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index 6f7d1ae20..05477562e 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -336,6 +336,7 @@ export default async function createDocumentwithCoordinate(request, response) { subject: subject, from: sender, html: html, + extUserId: extUser.id, }; await axios.post(url, params, { headers: headers }); diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index d6f0adaf6..7850e9b56 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -1,5 +1,5 @@ import PDF from './parsefunction/pdf/PDF.min.js'; -import sendmail from './parsefunction/sendMail.js'; +import sendmailv3 from './parsefunction/sendMailv3.js'; import GoogleSign from './parsefunction/GoogleSign.js'; import ZohoDetails from './parsefunction/ZohoDetails.js'; import usersignup from './parsefunction/usersignup.js'; @@ -10,7 +10,6 @@ import DocumentAftersave from './parsefunction/DocumentAftersave.js'; import ContactbookAftersave from './parsefunction/ContactBookAftersave.js'; import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js'; import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js'; -import SendMailv1 from './parsefunction/SendMailv1.js'; import AuthLoginAsMail from './parsefunction/AuthLoginAsMail.js'; import getUserId from './parsefunction/getUserId.js'; import getUserDetails from './parsefunction/getUserDetails.js'; @@ -30,13 +29,12 @@ import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); Parse.Cloud.define('signPdf', PDF); -Parse.Cloud.define('sendmailv3', sendmail); +Parse.Cloud.define('sendmailv3', sendmailv3); Parse.Cloud.define('googlesign', GoogleSign); Parse.Cloud.define('zohodetails', ZohoDetails); Parse.Cloud.define('usersignup', usersignup); Parse.Cloud.define('facebooksign', FacebookSign); Parse.Cloud.define('SendOTPMailV1', sendMailOTPv1); -Parse.Cloud.define('sendmail', SendMailv1); Parse.Cloud.define('AuthLoginAsMail', AuthLoginAsMail); Parse.Cloud.define('getUserId', getUserId); Parse.Cloud.define('getUserDetails', getUserDetails); diff --git a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js index 4c3b52283..f29f01ca2 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js +++ b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js @@ -1,4 +1,4 @@ -import axios from 'axios' +import { updateMailCount } from '../../Utils.js'; async function sendMailOTPv1(request) { try { @@ -11,37 +11,40 @@ async function sendMailOTPv1(request) { // console.log(JSON.stringify(request)); if (email) { - axios({ - method: 'POST', - url: process.env.SERVER_URL + '/functions/sendmail', - headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': process.env.APP_ID, - }, - params: { - otp: code, - email: email, - TenantId: TenantId, - }, - }).then( - function (httpResponse) {}, - function (httpResponse) { - console.error('sms Request failed with response code ' + httpResponse.status); - return Promise.reject('sms Request failed with response code ' + httpResponse.status); + const recipient = request.params.email; + const otp = request.params.otp; + const mailsender = process.env.SMTP_ENABLE + ? process.env.SMTP_USER_EMAIL + : process.env.MAILGUN_SENDER; + try { + await Parse.Cloud.sendEmail({ + from: 'Opensign™' + ' <' + mailsender + '>', + recipient: recipient, + subject: 'Your OpenSign™ OTP', + text: 'This email is a test.', + html: + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" + + otp + + '</p></div> </div> </div></body></html>', + }); + console.log('OTP sent'); + if (request.params?.extUserId) { + await updateMailCount(request.params.extUserId); } - ); - + } catch (err) { + console.log('error in send OTP mail', err); + } const tempOtp = new Parse.Query('defaultdata_Otp'); tempOtp.equalTo('Email', email); const resultOTP = await tempOtp.first({ useMasterKey: true }); - console.log('resultOTP', resultOTP); + // console.log('resultOTP', resultOTP); if (resultOTP !== undefined) { const updateOtpQuery = new Parse.Query('defaultdata_Otp'); const updateOtp = await updateOtpQuery.get(resultOTP.id, { useMasterKey: true, }); updateOtp.set('OTP', code); - const updateRes = updateOtp.save(null, { useMasterKey: true }); + updateOtp.save(null, { useMasterKey: true }); // console.log("update otp Res in tempSendOtp ", updateRes); } else { const otpClass = Parse.Object.extend('defaultdata_Otp'); @@ -49,17 +52,17 @@ async function sendMailOTPv1(request) { newOtpQuery.set('OTP', code); newOtpQuery.set('Email', email); newOtpQuery.set('TenantId', TenantId); - const newRes = await newOtpQuery.save(null, { useMasterKey: true }); + await newOtpQuery.save(null, { useMasterKey: true }); // console.log("new otp Res in tempSendOtp ", newRes); } return 'Otp send'; } else { - return Promise.reject('Please Enter valid email'); + return 'Please Enter valid email'; } } catch (err) { console.log('err in sendMailOTPv1'); console.log(err); - return Promise.reject(err); + return err; } } export default sendMailOTPv1; diff --git a/apps/OpenSignServer/cloud/parsefunction/SendMailv1.js b/apps/OpenSignServer/cloud/parsefunction/SendMailv1.js deleted file mode 100644 index 741ac4ec0..000000000 --- a/apps/OpenSignServer/cloud/parsefunction/SendMailv1.js +++ /dev/null @@ -1,27 +0,0 @@ -async function SendMailv1(request) { - console.log('in SendMailv1'); - - try { - const recipient = request.params.email; - const otp = request.params.otp; - const res = await Parse.Cloud.sendEmail({ - from: 'Test user' + ' <' + process.env.SMTP_ENABLE ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER + '>', - recipient: recipient, - subject: 'Your OpenSign™ OTP', - text: 'This email is a test.', - html: - "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" + - otp + - '</p></div> </div> </div></body></html>', - // "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body style='text-align: center;'><div style='display:flex;flex-direction:column;justify-content:center;align-item:center;margin:40px'> <p style='font-weight: bolder; font-size: large;'>Hello,</p> <span>Your OTP for LegaDaft verification . </span> <p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px'>76984</p><span>Thank You!</span> </div> </body></html>" - }); - console.log('Res'); - console.log(res); - return otp; - } catch (err) { - console.log('err in SendMailv1'); - console.log(err); - return err; - } -} -export default SendMailv1; diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index 37d9a39da..fe1463137 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -48,10 +48,12 @@ async function sendCompletedMail(e) { var a = e.url, t = e.sender, s = e.pdfName, - a = { + r = e.receiver, + e = { + extUserId: e.extUserId, url: a, from: 'OpenSign™', - recipient: e.receiver, + recipient: r, subject: `Document ${s} has been signed by all parties`, pdfName: s, html: @@ -61,7 +63,7 @@ async function sendCompletedMail(e) { t.Mail + ' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>', }; - await axios.post(serverUrl + '/functions/sendmailv3', a, { + await axios.post(serverUrl + '/functions/sendmailv3', e, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, @@ -206,11 +208,11 @@ async function PDF(i) { var P, y, v, - b, U, + b, I, S = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, - A = './exports/' + S; + x = './exports/' + S; let s = e.length; s = ( t @@ -226,51 +228,52 @@ async function PDF(i) { }), (v = await y.save()), Buffer.from(v)) - : ((b = await PDFDocument.load(e)), + : ((U = await PDFDocument.load(e)), pdflibAddPlaceholder({ - pdfDoc: b, + pdfDoc: U, reason: 'Digitally signed by OpenSign for ' + p + ' <' + m + '>', location: 'location', signatureLength: 15e3, }), - (U = await b.save()), - Buffer.from(U))), + (b = await U.save()), + Buffer.from(b))), (I = await new SignPDF(e, u).signPDF()), - fs.writeFileSync(A, I), + fs.writeFileSync(x, I), I) - : (fs.writeFileSync(A, e), e) + : (fs.writeFileSync(x, e), e) ).length; - var w, - D, - x = await uploadFile(S, A); - if (x && x.imageUrl) + var A, + w, + D = await uploadFile(S, x); + if (D && D.imageUrl) return ( - (w = await updateDoc( + (A = await updateDoc( i.params.docId, - x.imageUrl, + D.imageUrl, r.data.results[0].objectId, i.headers['x-real-ip'], n.data, o )), - sendDoctoWebhook(n, x.imageUrl, 'signed', r?.data.results?.[0]), - saveFileUsage(s, x.imageUrl, d.data.objectId), - w && - w.isCompleted && - ((D = { - url: x.imageUrl, + sendDoctoWebhook(n, D.imageUrl, 'signed', r?.data.results?.[0]), + saveFileUsage(s, D.imageUrl, d.data.objectId), + A && + A.isCompleted && + ((w = { + url: D.imageUrl, sender: { Mail: n.data.ExtUserPtr.Email, Name: 'OpenSign™' }, pdfName: n.data.Name, receiver: n.data.ExtUserPtr.Email, + extUserId: n.data.ExtUserPtr.objectId, }), n.data.IsSendMail && !1 === n.data.IsSendMail ? console.log("don't send mail") - : sendCompletedMail(D), - sendDoctoWebhook(n, x.imageUrl, 'completed')), - fs.unlinkSync(A), - console.log('New Signed PDF created called: ' + A), - 'success' === w.message - ? { status: 'success', data: x.imageUrl } + : sendCompletedMail(w), + sendDoctoWebhook(n, D.imageUrl, 'completed')), + fs.unlinkSync(x), + console.log('New Signed PDF created called: ' + x), + 'success' === A.message + ? { status: 'success', data: D.imageUrl } : { status: 'error', message: 'please provide required parameters!' } ); } diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMail.js b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js similarity index 88% rename from apps/OpenSignServer/cloud/parsefunction/sendMail.js rename to apps/OpenSignServer/cloud/parsefunction/sendMailv3.js index f8fb5f8da..dd5844e61 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMail.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js @@ -3,8 +3,9 @@ import https from 'https'; import formData from 'form-data'; import Mailgun from 'mailgun.js'; import { createTransport } from 'nodemailer'; +import { updateMailCount } from '../../Utils.js'; -async function sendmail(req) { +async function sendmailv3(req) { try { let transporterSMTP; let mailgunClient; @@ -77,6 +78,9 @@ async function sendmail(req) { const res = await transporterSMTP.sendMail(messageParams); console.log('Res ', res); if (!res.err) { + if (req.params?.extUserId) { + await updateMailCount(req.params.extUserId); + } return { status: 'success', }; @@ -85,6 +89,9 @@ async function sendmail(req) { const res = await mailgunClient.messages.create(mailgunDomain, messageParams); console.log('Res ', res); if (res.status === 200) { + if (req.params?.extUserId) { + await updateMailCount(req.params.extUserId); + } return { status: 'success', }; @@ -109,6 +116,9 @@ async function sendmail(req) { const res = await transporterSMTP.sendMail(messageParams); console.log('Res ', res); if (!res.err) { + if (req.params?.extUserId) { + await updateMailCount(req.params.extUserId); + } return { status: 'success', }; @@ -117,6 +127,9 @@ async function sendmail(req) { const res = await mailgunClient.messages.create(mailgunDomain, messageParams); console.log('Res ', res); if (res.status === 200) { + if (req.params?.extUserId) { + await updateMailCount(req.params.extUserId); + } return { status: 'success', }; @@ -124,11 +137,11 @@ async function sendmail(req) { } } } catch (err) { - console.log('err ', err); + console.log('err in sendmailv3', err); if (err) { return { status: 'error' }; } } } -export default sendmail; +export default sendmailv3; diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 9ae7cce6b..521205fa6 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -86,7 +86,9 @@ if (process.env.SMTP_ENABLE) { console.log('Please provide valid Mailgun credentials'); } } - +const mailsender = process.env.SMTP_ENABLE + ? process.env.SMTP_USER_EMAIL + : process.env.MAILGUN_SENDER; export const config = { databaseURI: process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev', @@ -102,7 +104,7 @@ export const config = { verifyUserEmails: isMailAdapter === true ? true : false, publicServerURL: process.env.SERVER_URL || 'http://localhost:8080/app', // Your apps name. This will appear in the subject and body of the emails that are sent. - appName: 'Open Sign', + appName: 'Opensign', allowClientClassCreation: false, allowExpiredAuthDataToken: false, encodeParseObjectInCloudFunction: true, @@ -112,9 +114,7 @@ export const config = { module: 'parse-server-api-mail-adapter', options: { // The email address from which emails are sent. - sender: process.env.SMTP_ENABLE - ? process.env.SMTP_USER_EMAIL - : process.env.MAILGUN_SENDER, + sender: 'Opensign™' + ' <' + mailsender + '>', // The email templates. templates: { // The template used by Parse Server to send an email for password From 099a3cc46d6ba602831f2bdb3c564241637c9b2e Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 13:21:04 +0530 Subject: [PATCH 14/26] refactor: update Emailcount in OTP mail --- apps/OpenSign/src/pages/GuestLogin.js | 3 +- apps/OpenSignServer/Utils.js | 2 -- .../cloud/parsefunction/SendMailOTPv1.js | 31 ++++++++++++++----- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index 6f048a64a..cce8cb208 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -59,7 +59,8 @@ function GuestLogin() { "X-Parse-Application-Id": parseId }; let body = { - email: email.toString() + email: email.toString(), + docId: id }; let Otp = await axios.post(url, body, { headers: headers }); diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index a6c8cb6c6..d94b46118 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -136,8 +136,6 @@ export const updateMailCount = async extUserId => { try { const contractUser = await query.first({ useMasterKey: true }); - console.log("contractUser", contractUser) - if (contractUser) { contractUser.increment('EmailCount', 1); await contractUser.save(null, { useMasterKey: true }); diff --git a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js index f29f01ca2..290ff0c3a 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js +++ b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js @@ -1,18 +1,30 @@ import { updateMailCount } from '../../Utils.js'; - +async function getDocument(docId) { + try { + const query = new Parse.Query('contracts_Document'); + query.equalTo('objectId', docId); + query.include('ExtUserPtr'); + query.include('CreatedBy'); + query.include('Signers'); + query.include('AuditTrail.UserPtr'); + query.include('Placeholders'); + query.notEqualTo('IsArchive', true); + const res = await query.first({ useMasterKey: true }); + const _res = res.toJSON(); + return _res?.ExtUserPtr?.objectId; + } catch (err) { + console.log('err ', err); + } +} async function sendMailOTPv1(request) { try { //--for elearning app side let code = Math.floor(1000 + Math.random() * 9000); let email = request.params.email; var TenantId = request.params.TenantId ? request.params.TenantId : undefined; - // console.log("In tempSendOTPv2"); - - // console.log(JSON.stringify(request)); if (email) { const recipient = request.params.email; - const otp = request.params.otp; const mailsender = process.env.SMTP_ENABLE ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER; @@ -24,12 +36,15 @@ async function sendMailOTPv1(request) { text: 'This email is a test.', html: "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" + - otp + + code + '</p></div> </div> </div></body></html>', }); console.log('OTP sent'); - if (request.params?.extUserId) { - await updateMailCount(request.params.extUserId); + if (request.params?.docId) { + const extUserId = await getDocument(request.params?.docId); + if (extUserId) { + updateMailCount(extUserId); + } } } catch (err) { console.log('error in send OTP mail', err); From 6c210285232ba56a13110ee3a0a90e7496cb9abe Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 14:11:26 +0530 Subject: [PATCH 15/26] feat: add upgrade now button in header --- apps/OpenSign/src/components/Header.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 6306d5135..5dcd590ac 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -83,6 +83,14 @@ const Header = ({ showSidebar }) => { id="profile-menu" className="flex justify-between items-center gap-x-3" > + <div> + <button + className="text-xs bg-[#002864] p-2 text-white rounded shadow" + onClick={() => navigation("/subscription")} + > + Upgrade Now + </button> + </div> <div> <FullScreenButton /> </div> From 7eec822a656bb23d23471caa84b0b3dc36be5bec Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Sun, 31 Mar 2024 14:16:23 +0530 Subject: [PATCH 16/26] refactor: correct grammatical mistake in report help message --- apps/OpenSign/src/json/ReportJson.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js index 1c002c8ab..af1cc024a 100644 --- a/apps/OpenSign/src/json/ReportJson.js +++ b/apps/OpenSign/src/json/ReportJson.js @@ -19,7 +19,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This are documents you have started but have not finalized for sending." + helpMsg: "These are documents you have started but have not finalized for sending." }; // Need your sign report case "4Hhwbp482K": @@ -36,7 +36,7 @@ export default function reportJson(id) { } ], helpMsg: - "This is a list of documents that are waiting for your signature" + "These is a list of documents that are waiting for your signature" }; // In progess report case "1MwEuxLEkF": @@ -53,7 +53,7 @@ export default function reportJson(id) { } ], helpMsg: - "This is a list of documents you've sent to other parties for signature." + "These is a list of documents you've sent to other parties for signature." }; // completed documents report case "kQUoW4hUXz": @@ -69,7 +69,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have been signed by all parties." + helpMsg: "These is a list of documents that have been signed by all parties." }; // declined documents report case "UPr2Fm5WY3": @@ -85,7 +85,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have been declined by one of the signers." + helpMsg: "These is a list of documents that have been declined by one of the signers." }; // Expired Documents report case "zNqBHXHsYH": @@ -101,7 +101,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have reached their expiration date." + helpMsg: "These is a list of documents that have reached their expiration date." }; // Recently sent for signatures report show on dashboard case "d9k3UfYHBc": @@ -162,7 +162,7 @@ export default function reportJson(id) { } ], 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." + helpMsg: "These 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": @@ -186,7 +186,7 @@ export default function reportJson(id) { } ], 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." + "These 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; From 58199a934a41a06db22cb694a91e021208163356 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 1 Apr 2024 13:27:43 +0530 Subject: [PATCH 17/26] fix: change upgrade button redirect ul, disable documentId issue --- apps/OpenSign/src/pages/UserProfile.js | 6 +++--- apps/OpenSign/src/primitives/Upgrade.js | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 648c8eaae..7fd7163fb 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -292,11 +292,11 @@ function UserProfile() { </span>{" "} <label className={ - !isSubscribe || isEnableSubscription - ? "relative inline-flex items-center mb-0 pointer-events-none opacity-50" - : `${ + isSubscribe || !isEnableSubscription + ? `${ editmode ? "cursor-pointer" : "" } relative inline-flex items-center mb-0` + : "relative inline-flex items-center mb-0 pointer-events-none opacity-50" } > <input diff --git a/apps/OpenSign/src/primitives/Upgrade.js b/apps/OpenSign/src/primitives/Upgrade.js index 9480752a6..b21d43270 100644 --- a/apps/OpenSign/src/primitives/Upgrade.js +++ b/apps/OpenSign/src/primitives/Upgrade.js @@ -1,16 +1,17 @@ import React from "react"; +import { useNavigate } from "react-router-dom"; function Upgrade() { + const navigation = useNavigate(); + return ( <sup> - <a - href="https://www.opensignlabs.com/plans-pricing" - target="_blank" - rel="noopener noreferrer" - className="text-blue-800 text-sm cursor-pointer " + <span + onClick={() => navigation("/subscription")} + className="text-blue-800 text-sm cursor-pointer hover:underline" > Upgrade now - </a> + </span> </sup> ); } From 0ee85f53dae2c69494e56a2a4d7b5920a4eb41b3 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 14:31:14 +0530 Subject: [PATCH 18/26] refactor: handle updgrade now visibililty in header --- apps/OpenSign/src/components/Header.js | 57 ++++++++++++++----- .../src/components/pdf/AddRoleModal.js | 2 +- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 5dcd590ac..268cf883b 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -5,19 +5,48 @@ import { useNavigate } from "react-router-dom"; import Parse from "parse"; import { useWindowSize } from "../hook/useWindowSize"; import { openInNewTab } from "../constant/Utils"; +import { isEnableSubscription } from "../constant/const"; + const Header = ({ showSidebar }) => { - const navigation = useNavigate(); + const navigate = useNavigate(); const { width } = useWindowSize(); let applogo = localStorage.getItem("appLogo") || ""; let username = localStorage.getItem("username"); const image = localStorage.getItem("profileImg") || dp; const [isOpen, setIsOpen] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(false); const toggleDropdown = () => { setIsOpen(!isOpen); }; - + useEffect(() => { + checkIsSubscribed(); + }, []); + async function checkIsSubscribed() { + const currentUser = Parse.User.current(); + const user = await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }); + if (isEnableSubscription) { + const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; + const billingDate = + user?.get("Next_billing_date") && user?.get("Next_billing_date"); + if (freeplan === "freeplan") { + setIsSubscribe(true); + } else if (billingDate) { + if (billingDate > new Date()) { + setIsSubscribe(true); + } else { + navigate(`/subscription`); + } + } else { + navigate(`/subscription`); + } + } else { + setIsSubscribe(true); + } + } const closeDropdown = () => { setIsOpen(false); Parse.User.logOut(); @@ -43,7 +72,7 @@ const Header = ({ showSidebar }) => { localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("parseAppId", appid); - navigation("/"); + navigate("/"); }; //handle to close profile drop down menu onclick screen @@ -83,14 +112,16 @@ const Header = ({ showSidebar }) => { id="profile-menu" className="flex justify-between items-center gap-x-3" > - <div> - <button - className="text-xs bg-[#002864] p-2 text-white rounded shadow" - onClick={() => navigation("/subscription")} - > - Upgrade Now - </button> - </div> + {isSubscribe && ( + <div> + <button + className="text-xs bg-[#002864] p-2 text-white rounded shadow" + onClick={() => navigate("/subscription")} + > + Upgrade Now + </button> + </div> + )} <div> <FullScreenButton /> </div> @@ -131,7 +162,7 @@ const Header = ({ showSidebar }) => { className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal" onClick={() => { setIsOpen(false); - navigation("/profile"); + navigate("/profile"); }} > <i className="fa-regular fa-user"></i> Profile @@ -140,7 +171,7 @@ const Header = ({ showSidebar }) => { className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal" onClick={() => { setIsOpen(false); - navigation("/changepassword"); + navigate("/changepassword"); }} > <i className="fa-solid fa-lock"></i> Change Password diff --git a/apps/OpenSign/src/components/pdf/AddRoleModal.js b/apps/OpenSign/src/components/pdf/AddRoleModal.js index 8a2d1acb1..630a4bd61 100644 --- a/apps/OpenSign/src/components/pdf/AddRoleModal.js +++ b/apps/OpenSign/src/components/pdf/AddRoleModal.js @@ -30,7 +30,7 @@ const AddRoleModal = (props) => { margin: "10px 0 10px 5px" }} > - e.g: Hr, Director, Manager, New joinee, Accountant, etc... + e.g: Customer, Hr, Director, Manager, Student, etc... </p> <div> <div From e8aaab99f979b453c53110f821a3184aa405ee2d Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 16:54:26 +0530 Subject: [PATCH 19/26] fix: change message of already subscribed --- apps/OpenSign/src/components/Header.js | 1 + apps/OpenSign/src/pages/PlanSubscriptions.js | 2 +- apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 268cf883b..17e5e2dcd 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -22,6 +22,7 @@ const Header = ({ showSidebar }) => { }; useEffect(() => { checkIsSubscribed(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); async function checkIsSubscribed() { const currentUser = Parse.User.current(); diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 954cc432d..7c56f6e37 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -51,7 +51,7 @@ const PlanSubscriptions = () => { const res = await Parse.Cloud.run("freesubscription", params); if (res.status === "success" && res.result === "already subscribed!") { setIsLoader(false); - alert("You have already subscription of free plan"); + alert("You have already subscribed to plan!"); } else if (res.status === "success") { setIsLoader(false); navigate("/"); diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js index 674d56223..03c4fb95f 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscribeFree.js @@ -19,6 +19,8 @@ export default async function SubscribeFree(request) { console.log('err ', err); return { status: 'error', result: err.message }; } + } else if (extUser?.get('Next_billing_date') > new Date()) { + return { status: 'success', result: 'already subscribed!' }; } else { try { const extUpdate = new Parse.Object('contracts_Users'); From 96a4a2e7faed0d060075d94f6bcd298bb70b8883 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 17:17:58 +0530 Subject: [PATCH 20/26] feat: add help button for Role in template --- .../src/components/pdf/SignerListPlace.js | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/apps/OpenSign/src/components/pdf/SignerListPlace.js b/apps/OpenSign/src/components/pdf/SignerListPlace.js index 335bc4411..be9965bf4 100644 --- a/apps/OpenSign/src/components/pdf/SignerListPlace.js +++ b/apps/OpenSign/src/components/pdf/SignerListPlace.js @@ -1,6 +1,7 @@ import React from "react"; import { themeColor } from "../../constant/const"; import RecipientList from "./RecipientList"; +import { Tooltip } from "react-tooltip"; function SignerListPlace(props) { return ( @@ -13,6 +14,58 @@ function SignerListPlace(props) { > <span className="signedStyle"> {props.title ? props.title : "Recipients"} + <span className="absolute text-xs z-[30] mt-1 ml-0.5"> + {props?.title === "Roles" && ( + <> + <a data-tooltip-id="my-tooltip"> + <sup> + <i + className="fa-solid fa-question rounded-full" + style={{ + borderColor: "white", + color: "white", + fontSize: 11, + borderWidth: 1.5, + padding: "1px 3px" + }} + ></i> + </sup> + </a> + <Tooltip id="my-tooltip"> + <div className="max-w-[450px]"> + <p className="font-bold">What are template roles?</p> + <p> + Begin by specifying each role needed for the completion of + the document. Think about the parties involved in the + signing process and what their responsibilities are. + Common roles include HR for internal documents, Customer + for agreements or Vendor for business agreements.{" "} + </p> + <p className="font-bold"> + Why pre-attach users to some roles? + </p> + <p> + For roles that consistently involve the same individual + (e.g., the CEO's signature on employee offer + letters), you can pre-attach a user to a role within the + template. This step is optional but recommended for + efficiency and consistency across documents. + </p> + <p className="font-bold"> + When do i specify the user attached to each role? + </p> + <p> + When you create a document from your template, you'll + be prompted to attach users to each defined role. If a + role already has a user attached, this will be pre-filled, + but you can modify it as needed before sending out the + document. + </p> + </div> + </Tooltip> + </> + )} + </span> </span> </div> <div className="signerList"> From d31910aefaf73aef34b28d75512f7b9aeccb1a37 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 17:25:23 +0530 Subject: [PATCH 21/26] fix: visibility of upgrade now button in header --- apps/OpenSign/src/components/Header.js | 30 ++++++-------------------- apps/OpenSign/src/constant/Utils.js | 6 ------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 17e5e2dcd..7f788fc71 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -4,7 +4,7 @@ import FullScreenButton from "./FullScreenButton"; import { useNavigate } from "react-router-dom"; import Parse from "parse"; import { useWindowSize } from "../hook/useWindowSize"; -import { openInNewTab } from "../constant/Utils"; +import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import { isEnableSubscription } from "../constant/const"; const Header = ({ showSidebar }) => { @@ -21,31 +21,13 @@ const Header = ({ showSidebar }) => { setIsOpen(!isOpen); }; useEffect(() => { - checkIsSubscribed(); + checkSubscription(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - async function checkIsSubscribed() { - const currentUser = Parse.User.current(); - const user = await Parse.Cloud.run("getUserDetails", { - email: currentUser.get("email") - }); + async function checkSubscription() { if (isEnableSubscription) { - const freeplan = user?.get("Plan") && user?.get("Plan").plan_code; - const billingDate = - user?.get("Next_billing_date") && user?.get("Next_billing_date"); - if (freeplan === "freeplan") { - setIsSubscribe(true); - } else if (billingDate) { - if (billingDate > new Date()) { - setIsSubscribe(true); - } else { - navigate(`/subscription`); - } - } else { - navigate(`/subscription`); - } - } else { - setIsSubscribe(true); + const getIsSubscribe = await checkIsSubscribed(); + setIsSubscribe(getIsSubscribe); } } const closeDropdown = () => { @@ -113,7 +95,7 @@ const Header = ({ showSidebar }) => { id="profile-menu" className="flex justify-between items-center gap-x-3" > - {isSubscribe && ( + {!isSubscribe && ( <div> <button className="text-xs bg-[#002864] p-2 text-white rounded shadow" diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index bd5b45b67..fa7099415 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -15,12 +15,6 @@ export const openInNewTab = (url) => { //function to get subcripition details from Extand user class export async function checkIsSubscribed() { - // const tenantId = localStorage.getItem("TenantId"); - // const users = new Parse.Query("partners_Tenant"); - // users.equalTo("objectId", tenantId); - // const res = await users.first(); - // const jsonRes = JSON.parse(JSON.stringify(res)); - const extClass = localStorage.getItem("Extand_Class"); const jsonSender = JSON.parse(extClass); const user = await Parse.Cloud.run("getUserDetails", { From aa04a957ed9db7e0ebea992f1e46109e97a6179f Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 1 Apr 2024 17:48:12 +0530 Subject: [PATCH 22/26] feat: change webhook & API token UI --- apps/OpenSign/src/pages/GenerateToken.js | 91 ++++++++----- apps/OpenSign/src/pages/Webhook.js | 162 ++++++++++++++--------- 2 files changed, 152 insertions(+), 101 deletions(-) diff --git a/apps/OpenSign/src/pages/GenerateToken.js b/apps/OpenSign/src/pages/GenerateToken.js index 9e4035191..b5cb2b481 100644 --- a/apps/OpenSign/src/pages/GenerateToken.js +++ b/apps/OpenSign/src/pages/GenerateToken.js @@ -1,15 +1,16 @@ import React, { useEffect, useState } from "react"; import Title from "../components/Title"; import axios from "axios"; +import { useNavigate } from "react-router-dom"; import Alert from "../primitives/Alert"; import ModalUi from "../primitives/ModalUi"; import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; -import Upgrade from "../primitives/Upgrade"; function GenerateToken() { + const navigation = useNavigate(); const [parseBaseUrl] = useState(localStorage.getItem("baseUrl")); const [parseAppId] = useState(localStorage.getItem("parseAppId")); const [apiToken, SetApiToken] = useState(""); @@ -98,6 +99,7 @@ function GenerateToken() { }, 1500); // Reset copied state after 1.5 seconds }; const handleModal = () => setIsModal(!isModal); + return ( <React.Fragment> <Title title={"API Token"} /> @@ -126,56 +128,46 @@ function GenerateToken() { ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> {!isEnableSubscription && <PremiumAlertHeader />} - <h1 - className={ - isSubscribe || !isEnableSubscription - ? "ml-4 mt-3 mb-2 font-semibold" - : "ml-4 mt-3 mb-2 font-semibold text-gray-300" - } - > - API Token{" "} + <h1 className={"ml-4 mt-3 mb-2 font-semibold"}> + OpenSign™ API{" "} <Tooltip url={ "https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1" } - iconColor={!isSubscribe && "gray"} + isSubscribe={true} /> - {!isSubscribe && isEnableSubscription && <Upgrade />} </h1> <ul className={ isSubscribe || !isEnableSubscription ? "w-full flex flex-col p-2 text-sm " - : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" + : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none" } > <li - className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} + className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} > - <span className="w-[40%]">Api Token:</span>{" "} - <span - id="token" - className="w-[60%] md:text-end cursor-pointer" - onClick={() => copytoclipboard(apiToken)} + <div className="w-[70%] flex-col md:flex-row flex items-center gap-5 "> + <span className="">Api Token:</span>{" "} + <span + id="token" + className=" md:text-end cursor-pointer" + onClick={() => copytoclipboard(apiToken)} + > + {apiToken ? apiToken : "_____"} + </span> + </div> + <button + type="button" + onClick={apiToken ? handleModal : handleSubmit} + className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none" > - {apiToken && apiToken} - </span> + {apiToken ? "Regenerate Token" : "Generate Token"} + </button> </li> </ul> - <div - className={ - isSubscribe || !isEnableSubscription - ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" - : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" - } - > - <button - type="button" - onClick={apiToken ? handleModal : handleSubmit} - className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none" - > - {apiToken ? "Regenerate Token" : "Generate Token"} - </button> + + <div className="flex items-center justify-center "> <button type="button" onClick={() => @@ -183,12 +175,41 @@ function GenerateToken() { "https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1" ) } - className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" + className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" > View Docs </button> </div> + {!isSubscribe && isEnableSubscription && ( + <> + <h1 className={"ml-4 mt-3 mb-2 font-semibold"}> + Upgrade to PRO Plan + </h1> + <ul className={"w-full flex flex-col p-2 text-sm "}> + <li + className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} + > + <div className="w-[70%] flex-col md:flex-row flex items-center gap-3 "> + <span className="">$29.99/month:</span>{" "} + <span id="token" className=" md:text-end cursor-pointer"> + Sign 100 docs using API. Only 0.15/docs after that. + </span> + </div> + {!isSubscribe && isEnableSubscription && ( + <button + type="button" + onClick={() => navigation("/subscription")} + className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" + > + Upgrade Now + </button> + )} + </li> + </ul> + </> + )} + <ModalUi isOpen={isModal} title={"Regenerate Token"} diff --git a/apps/OpenSign/src/pages/Webhook.js b/apps/OpenSign/src/pages/Webhook.js index 12e4fef17..c32e5b9d7 100644 --- a/apps/OpenSign/src/pages/Webhook.js +++ b/apps/OpenSign/src/pages/Webhook.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import Title from "../components/Title"; import axios from "axios"; +import { useNavigate } from "react-router-dom"; import Alert from "../primitives/Alert"; import ModalUi from "../primitives/ModalUi"; import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; @@ -8,9 +9,9 @@ import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import Parse from "parse"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; -import Upgrade from "../primitives/Upgrade"; function Webhook() { + const navigation = useNavigate(); const [parseBaseUrl] = useState(localStorage.getItem("baseUrl")); const [parseAppId] = useState(localStorage.getItem("parseAppId")); const [webhook, setWebhook] = useState(); @@ -19,6 +20,7 @@ function Webhook() { const [isErr, setIsErr] = useState(false); const [isModal, setIsModal] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false); + const [error, setError] = useState(""); useEffect(() => { fetchWebhook(); @@ -46,41 +48,48 @@ function Webhook() { }; const handleSubmit = async (e) => { e.preventDefault(); - setIsLoader(true); - setIsModal(false); - try { - const params = { url: webhook }; - const url = parseBaseUrl + "functions/savewebhook"; - const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId, - sessiontoken: localStorage.getItem("accesstoken") - }; - await axios.post(url, params, { headers: headers }).then((res) => { - if (res.data && res.data.result && res.data.result.Webhook) { - setWebhook(res.data.result.Webhook); - setIsGenerate(true); - setTimeout(() => { - setIsGenerate(false); - }, 1500); - setIsLoader(false); - } else { - console.error("Error while generating webhook"); - setIsLoader(false); - setIsErr(true); - setTimeout(() => { - setIsErr(false); - }, 1500); - } - }); - } catch (error) { - setIsLoader(false); - setIsErr(true); - setTimeout(() => { - setIsErr(false); - }, 1500); + if (webhook && webhook.startsWith("https://")) { + setIsLoader(true); + setIsModal(false); + try { + const params = { url: webhook }; + const url = parseBaseUrl + "functions/savewebhook"; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + sessiontoken: localStorage.getItem("accesstoken") + }; + await axios.post(url, params, { headers: headers }).then((res) => { + if (res.data && res.data.result && res.data.result.Webhook) { + setWebhook(res.data.result.Webhook); + setIsGenerate(true); + setTimeout(() => { + setIsGenerate(false); + }, 1500); + setIsLoader(false); + } else { + console.error("Error while generating webhook"); + setIsLoader(false); + setIsErr(true); + setTimeout(() => { + setIsErr(false); + }, 1500); + } + }); + } catch (error) { + setIsLoader(false); + setIsErr(true); + setTimeout(() => { + setIsErr(false); + }, 1500); - console.log("err", error); + console.log("err", error); + } + } else { + setError("The webhook url should always start with https://"); + setTimeout(() => { + setError(""); + }, 1500); } }; @@ -90,6 +99,7 @@ function Webhook() { <Title title={"Webhook"} /> {isGenerate && <Alert type="success">Webhook added successfully!</Alert>} {isErr && <Alert type="danger">Something went wrong!</Alert>} + {isLoader ? ( <div style={{ @@ -110,50 +120,40 @@ function Webhook() { ) : ( <div className="bg-white flex flex-col justify-center shadow rounded"> {!isEnableSubscription && <PremiumAlertHeader />} - <h1 - className={ - isSubscribe || !isEnableSubscription - ? "ml-4 mt-3 mb-2 font-semibold" - : "ml-4 mt-3 mb-2 font-semibold text-gray-300" - } - > - Webhook{" "} + <h1 className={"ml-4 mt-3 mb-2 font-semibold"}> + OpenSign™ Webhook{" "} <Tooltip url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"} - isSubscribe={isSubscribe} + isSubscribe={true} /> - {!isSubscribe && isEnableSubscription && <Upgrade />} </h1> <ul className={ isSubscribe || !isEnableSubscription ? "w-full flex flex-col p-2 text-sm " - : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none" + : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none" } > <li - className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} + className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} > - <span className="w-[40%]">Webhook:</span>{" "} - <span id="token" className="w-[60%] md:text-end cursor-pointer"> - {webhook && webhook} - </span> + <div className="w-[70%] flex-col md:flex-row flex items-center gap-5 "> + <span className="">Webhook:</span>{" "} + <span id="token" className=" md:text-end cursor-pointer"> + {webhook ? webhook : "_____"} + </span> + </div> + <button + type="button" + onClick={handleModal} + className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none" + > + {webhook ? "Update Webhook" : "Add Webhook"} + </button> </li> </ul> - <div - className={ - isSubscribe || !isEnableSubscription - ? "flex flex-col md:flex-row items-center justify-center gap-2 pb-4" - : "flex flex-col md:flex-row items-center justify-center gap-2 pb-4 opacity-40 pointer-events-none" - } - > - <button - type="button" - onClick={handleModal} - className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none" - > - {webhook ? "Update Webhook" : "Add Webhook"} - </button> + + <div className="flex items-center justify-center "> <button type="button" onClick={() => @@ -161,17 +161,47 @@ function Webhook() { "https://docs.opensignlabs.com/docs/API-docs/save-update-webhook" ) } - className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" + className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" > View Docs </button> </div> + {!isSubscribe && isEnableSubscription && ( + <> + <h1 className={"ml-4 mt-3 mb-2 font-semibold"}> + Upgrade to PRO Plan + </h1> + <ul className={"w-full flex flex-col p-2 text-sm "}> + <li + className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`} + > + <div className="w-[70%] flex-col md:flex-row flex items-center gap-3 "> + <span className="">$29.99/month:</span>{" "} + <span id="token" className=" md:text-end cursor-pointer"> + First 100 documents included, then just $0.15 per + document. + </span> + </div> + {!isSubscribe && isEnableSubscription && ( + <button + type="button" + onClick={() => navigation("/subscription")} + className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none" + > + Upgrade Now + </button> + )} + </li> + </ul> + </> + )} <ModalUi isOpen={isModal} title={"Regenerate Token"} handleClose={handleModal} > + {error && <Alert type="danger">{error}</Alert>} <div className="m-[20px]"> <div className="text-lg font-normal text-black"> <label className="text-sm ml-2">Webhook</label> From 79e3762b365546b927257277f8f21e48a1a9971a Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 18:47:03 +0530 Subject: [PATCH 23/26] feat: change subscription plans --- apps/OpenSign/src/components/Header.js | 2 +- apps/OpenSign/src/json/plansArr.json | 75 +++++++++-------- apps/OpenSign/src/pages/PlanSubscriptions.js | 89 ++++++++++++++++---- 3 files changed, 114 insertions(+), 52 deletions(-) diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index 7f788fc71..236120820 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -15,7 +15,7 @@ const Header = ({ showSidebar }) => { const image = localStorage.getItem("profileImg") || dp; const [isOpen, setIsOpen] = useState(false); - const [isSubscribe, setIsSubscribe] = useState(false); + const [isSubscribe, setIsSubscribe] = useState(true); const toggleDropdown = () => { setIsOpen(!isOpen); diff --git a/apps/OpenSign/src/json/plansArr.json b/apps/OpenSign/src/json/plansArr.json index f372d7493..e8047441d 100644 --- a/apps/OpenSign/src/json/plansArr.json +++ b/apps/OpenSign/src/json/plansArr.json @@ -1,63 +1,70 @@ [ { - "planName": "Free Plan", + "planName": "OPENSIGN™ FREE", "currency": "", - "price": "Free", - "subtitle": "Free for life.", + "monthlyPrice": "Free", + "yearlyPrice": "Free", + "subtitle": "Free Unlimited E-signatures, Forever.", "btnText": "Subscribe", "url": "", "target": "_blank", "benefits": [ - "Sign Unlimited Documents", - "S3 Doc Storage", - "Unlimited Guest Signers", - "Unlimited completion certificates", - "Unique Code(OTP) verification for guest signers", - "Expiring docs & reject signature support", - "PDF Template Creation", - "Audit Trails", - "API support", - "Free for lifetime", - "Community support" + "Unlimited envelopes", + "Sign documents yourself", + "Request signatures from others", + "14 field types", + "Automatic e-signatures", + "Completion certificates", + "Send in order", + "Document templates", + "Import from Dropbox", + "Contact book", + "Document expiry support", + "Decline document support", + "Email notifications", + "Recipient authentication using OTP", + "And much more" ] }, { - "planName": "THANKS PLAN", + "planName": "OPENSIGN™ PROFESSIONAL", "currency": "$", - "price": "29.99", - "subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)", + "monthlyPrice": "29.99", + "yearlyPrice": "329.99", + "subtitle": "Exclusive Access to advanced features.", "btnText": "Subscribe", - "url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks", + "url": "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly", + "yearlyUrl": "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly", "target": "_self", "benefits": [ - "Sign Unlimited Documents", - "Unlimited Secure Doc Storage with OpenSign™ Drive", - "Unlimited Guest Signers", - "Unlimited completion certificates", - "Unique Code(OTP) verification for guest signers", - "Expiring docs & reject signature support", - "PDF Template Creation(coming soon)", - "Audit Trails", - "API support", - "Add-ons support(coming soon)", - "Self hosted cloud infrastructure" + "Everything in OpenSign™ professional", + "Field validations", + "Regular expression validations", + "Organize docs in OpenSign™ Drive", + "Webhooks", + "Zapier integration", + "API Access", + "100 API signatures included", + "DocumentId removal from signed docs", + "Custom email templates(coming soon)" ] }, { - "planName": "ENTERPRISE", + "planName": "OPENSIGN™ ENTERPRISE", "currency": "", - "price": "Request Price", - "subtitle": "Customization available Priority support.", + "monthlyPrice": "Request Price", + "yearlyPrice": "Request Price", + "subtitle": "Scalable Features with priority support.", "btnText": "Contact us", "url": "https://www.opensignlabs.com/contact-us", "target": "_blank", "benefits": [ "All features", "Custom domain", - "Whitelabeling", + "Custom branding", "Uptime SLA", "SSO support", - "24/7 support" + "Priority support" ] } ] diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 7c56f6e37..0570d8130 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -37,7 +37,6 @@ const PlanSubscriptions = () => { useEffect(() => { // if (localStorage.getItem("accesstoken")) { setIsLoader(false); - setYearlyVisible(false); // } else { // navigate("/", { replace: true }); // } @@ -90,20 +89,37 @@ const PlanSubscriptions = () => { style={{ backgroundColor: "white", overflowY: "auto", - maxHeight: "100%", - "--theme-color": "#7952b3", - "--plan-width": 30 + maxHeight: "100%" }} > - <div - id="monthlyPlans" - className={`${yearlyVisible ? "none" : "block my-2"}`} - > - <div className="flex justify-center w-full my-2"> - <ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300"> + <div id="monthlyPlans" className="block my-2"> + <div className="flex flex-col justify-center items-center w-full"> + <div className="mb-6 mt-2 flex flex-row border-[1px] p-2 border-gray-300 rounded text-sm"> + <span + onClick={() => setYearlyVisible(false)} + className={`${ + !yearlyVisible + ? "bg-[#002862] text-white" + : "bg-white text-black" + } px-4 py-1 rounded cursor-pointer`} + > + Monthly + </span> + <span + onClick={() => setYearlyVisible(true)} + className={`${ + yearlyVisible + ? "bg-[#002862] text-white" + : "bg-white text-black" + } px-4 py-1 rounded cursor-pointer`} + > + Yearly (10% off) + </span> + </div> + <ul className=" flex flex-col md:flex-row h-full bg-white justify-center"> {plansArr.map((item) => ( <li - className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 max-w-[260px]" + className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]" key={item.planName} > <div className="p-2 flex flex-col justify-center items-center min-h-[320px]"> @@ -120,17 +136,23 @@ const PlanSubscriptions = () => { <div className=""> <span className="text-3xl"> {item.currency && <small>{item.currency}</small>} - {item.price} + {yearlyVisible + ? item?.yearlyPrice + : item.monthlyPrice} </span> + <p className="font-semibold pt-2 text-sm"> + {yearlyVisible ? "Billed Yearly" : "Billed Monthly"} + </p> <div className={`${ item.subtitle.length <= 32 - ? "w-[150px] text-center" + ? "w-[150px] h-[40px] text-center" : "" } text-sm text-center my-2`} > <div style={{ + textAlign: "center", backgroundColor: item.subtitlecolor ? item.subtitlecolor : "white" @@ -148,22 +170,23 @@ const PlanSubscriptions = () => { </div> </div> </div> - {item.url ? ( <NavLink to={ item.btnText === "Subscribe" - ? item.url + details + ? yearlyVisible + ? item.yearlyUrl + details + : item.url + details : item.url } - className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" + className="bg-[#002862] w-full mt-1 text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" target={item.target} > {item.btnText} </NavLink> ) : ( <button - className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" + className="bg-[#002862] w-full mt-1 text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" onClick={() => handleFreePlan()} > {item.btnText} @@ -184,6 +207,38 @@ const PlanSubscriptions = () => { ))} </ul> </div> + <div + style={{ + display: "flex", + alignItems: "center", + justifyContent: "center" + }} + className="text-sm" + > + <hr + className={"border-[1px] border-gray-300 w-[20%]"} + style={{ color: "grey" }} + /> + <span style={{ color: "grey" }} className="px-2 "> + or + </span> + <hr + className={"border-[1px] border-gray-300 w-[20%]"} + style={{ color: "grey" }} + /> + </div> + <div className="flex flex-col justify-center w-full items-center"> + <h3 className="text-[#002862] mt-1 mb-2"> + Host it yourself for free + </h3> + <NavLink + to={"https://github.com/OpenSignLabs/OpenSign"} + className="bg-[#002862] w-[200px] text-center text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer" + target={"_blank"} + > + Visit Github + </NavLink> + </div> </div> </div> )} From cea1653805f92b90d3c1801eb85f157afed9a862 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 19:51:11 +0530 Subject: [PATCH 24/26] feat: auto assign free plan to new signup --- apps/OpenSign/src/pages/PlanSubscriptions.js | 4 +- apps/OpenSign/src/pages/Signup.js | 42 +++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 0570d8130..d68a85fc6 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -116,10 +116,10 @@ const PlanSubscriptions = () => { Yearly (10% off) </span> </div> - <ul className=" flex flex-col md:flex-row h-full bg-white justify-center"> + <ul className="flex flex-col md:flex-row h-full bg-white justify-center"> {plansArr.map((item) => ( <li - className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]" + className="flex flex-col md:my-0 text-center border-collapse border-[1px] border-gray-300 w-[260px]" key={item.planName} > <div className="p-2 flex flex-col justify-center items-center min-h-[320px]"> diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index 1099d4a88..a253f6a9a 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import Parse from "parse"; import axios from "axios"; import Title from "../components/Title"; -import { useNavigate, NavLink } from "react-router-dom"; +import { useNavigate, NavLink, useLocation } from "react-router-dom"; import login_img from "../assets/images/login_img.svg"; import { useWindowSize } from "../hook/useWindowSize"; import Alert from "../primitives/Alert"; @@ -14,6 +14,7 @@ import { isEnableSubscription } from "../constant/const"; const Signup = () => { const { width } = useWindowSize(); const navigate = useNavigate(); + const location = useLocation(); const dispatch = useDispatch(); const [name, setName] = useState(""); const [phone, setPhone] = useState(""); @@ -68,6 +69,19 @@ const Signup = () => { localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("parseAppId", appid); }; + + const handleFreePlan = async (id) => { + try { + const params = { userId: id }; + const res = await Parse.Cloud.run("freesubscription", params); + if (res.status === "error") { + alert(res.result); + } + } catch (err) { + console.log("err in free subscribe", err.message); + alert("Somenthing went wrong, please try again later!"); + } + }; const handleSubmit = (event) => { event.preventDefault(); if (lengthValid && caseDigitValid && specialCharValid) { @@ -111,7 +125,13 @@ const Signup = () => { params ); if (usersignup) { - handleNavigation(r.getSessionToken()); + const param = new URLSearchParams(location.search); + const isFreeplan = + param?.get("subscription") === "freeplan"; + if (isFreeplan) { + await handleFreePlan(r.id); + } + handleNavigation(r.getSessionToken(), isFreeplan); } } catch (err) { alert(err.message); @@ -156,7 +176,7 @@ const Signup = () => { } }; - const handleNavigation = async (sessionToken) => { + const handleNavigation = async (sessionToken, isFreeplan = false) => { const baseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); const res = await axios.get(baseUrl + "users/me", { @@ -325,7 +345,13 @@ const Signup = () => { ); setState({ loading: false }); if (isEnableSubscription) { - navigate(`/subscription`, { replace: true }); + if (isFreeplan) { + navigate( + `/${element.pageType}/${element.pageId}` + ); + } else { + navigate(`/subscription`, { replace: true }); + } } else { alert("Registered user successfully"); navigate( @@ -343,7 +369,13 @@ const Signup = () => { localStorage.setItem("pageType", element.pageType); setState({ loading: false }); if (isEnableSubscription) { - navigate(`/subscription`, { replace: true }); + if (isFreeplan) { + navigate( + `/${element.pageType}/${element.pageId}` + ); + } else { + navigate(`/subscription`, { replace: true }); + } } else { navigate( `/${element.pageType}/${element.pageId}` From add01cb02a689617fa52acc1b85b426d3237167d Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 20:08:02 +0530 Subject: [PATCH 25/26] feat: auto assign free plan in google signup flow --- apps/OpenSign/src/pages/Login.js | 53 ++++++++++++++++++++++++++++--- apps/OpenSign/src/pages/Signup.js | 2 +- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 2ee9d6c60..ba1714abb 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -395,6 +395,18 @@ function Login() { const setThirdpartyLoader = (value) => { setState({ ...state, thirdpartyLoader: value }); }; + const handleFreePlan = async (id) => { + try { + const params = { userId: id }; + const res = await Parse.Cloud.run("freesubscription", params); + if (res.status === "error") { + alert(res.result); + } + } catch (err) { + console.log("err in free subscribe", err.message); + alert("Somenthing went wrong, please try again later!"); + } + }; const thirdpartyLoginfn = async (sessionToken, billingDate) => { const baseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); @@ -404,6 +416,11 @@ function Login() { "X-Parse-Application-Id": parseAppId } }); + const param = new URLSearchParams(location.search); + const isFreeplan = param?.get("subscription") === "freeplan"; + if (isFreeplan) { + await handleFreePlan(res.data.objectId); + } await Parse.User.become(sessionToken).then(() => { window.localStorage.setItem("accesstoken", sessionToken); }); @@ -580,13 +597,23 @@ function Login() { if (billingDate > new Date()) { localStorage.removeItem("userDetails"); navigate(redirectUrl); + } else { + if (isFreeplan) { + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } + } + } else { + if (isFreeplan) { + navigate(redirectUrl); } else { navigate(`/subscription`, { replace: true }); } - } else { - navigate(`/subscription`, { replace: true }); } } else { navigate(redirectUrl); @@ -608,10 +635,22 @@ function Login() { // Redirect to the appropriate URL after successful login navigate(redirectUrl); } else { - navigate(`/subscription`, { replace: true }); + if (isFreeplan) { + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } } } else { - navigate(`/subscription`, { replace: true }); + if (isFreeplan) { + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } } } else { navigate(redirectUrl); @@ -1112,7 +1151,11 @@ function Login() { </button> <NavLink className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase" - to="/signup" + to={ + location.search + ? "/signup" + location.search + : "/signup" + } style={width < 768 ? { textAlign: "center" } : {}} > Create Account diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index a253f6a9a..a3af7263c 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -657,7 +657,7 @@ const Signup = () => { </button> <NavLink className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase" - to="/" + to={location.search ? "/" + location.search : "/"} style={width < 768 ? { textAlign: "center" } : {}} > Login From e2d450f9aae6a267a8b999d5ff1676d0db3dc436 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Apr 2024 20:13:56 +0530 Subject: [PATCH 26/26] fix: correct grammatical mistake in report help button --- apps/OpenSign/src/json/ReportJson.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js index af1cc024a..813ba0f80 100644 --- a/apps/OpenSign/src/json/ReportJson.js +++ b/apps/OpenSign/src/json/ReportJson.js @@ -36,7 +36,7 @@ export default function reportJson(id) { } ], helpMsg: - "These is a list of documents that are waiting for your signature" + "This is a list of documents that are waiting for your signature" }; // In progess report case "1MwEuxLEkF": @@ -53,7 +53,7 @@ export default function reportJson(id) { } ], helpMsg: - "These is a list of documents you've sent to other parties for signature." + "This is a list of documents you've sent to other parties for signature." }; // completed documents report case "kQUoW4hUXz": @@ -69,7 +69,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "These is a list of documents that have been signed by all parties." + helpMsg: "This is a list of documents that have been signed by all parties." }; // declined documents report case "UPr2Fm5WY3": @@ -85,7 +85,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "These is a list of documents that have been declined by one of the signers." + helpMsg: "This is a list of documents that have been declined by one of the signers." }; // Expired Documents report case "zNqBHXHsYH": @@ -101,7 +101,7 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "These is a list of documents that have reached their expiration date." + helpMsg: "This is a list of documents that have reached their expiration date." }; // Recently sent for signatures report show on dashboard case "d9k3UfYHBc": @@ -162,7 +162,7 @@ export default function reportJson(id) { } ], form: "ContactBook", - helpMsg: "These is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document." + 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": @@ -186,7 +186,7 @@ export default function reportJson(id) { } ], helpMsg: - "These 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." + "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;