Merge pull request #915 from OpenSignLabs/raktima-opensignlabs-patch-11

feat : implement public profile functionality and develop script for public sign-in
This commit is contained in:
prafull-opensignlabs
2024-07-11 10:10:45 +05:30
committed by GitHub
35 changed files with 219341 additions and 626 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-react"]
};
+907 -67
View File
File diff suppressed because it is too large Load Diff
+18 -4
View File
@@ -51,12 +51,14 @@
"ws": "^8.18.0"
},
"scripts": {
"build-webpack": "webpack --config webpack.config.js",
"build": "npm run version && npm run build-webpack && react-scripts build",
"build-watch": "webpack --config webpack.config.js --watch",
"start-dev": "concurrently \"npm run build-watch\" \"react-scripts start\"",
"start": "serve -s build",
"start-dev": "react-scripts start",
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
"build": "npm run version && react-scripts build",
"build-win": "npm run version-win && react-scripts build",
"build-win": "npm run version-win && npm run build-webpack && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"release": "standard-version",
@@ -86,18 +88,30 @@
}
},
"devDependencies": {
"@babel/core": "^7.24.6",
"@babel/preset-env": "^7.24.6",
"@babel/preset-react": "^7.24.6",
"@babel/runtime-corejs2": "^7.24.7",
"autoprefixer": "^10.4.19",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
"commitizen": "^4.3.0",
"concurrently": "^8.2.2",
"css-loader": "^7.1.2",
"daisyui": "^4.12.10",
"dotenv": "^16.4.5",
"dotenv-webpack": "^8.1.0",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.3",
"lint-staged": "^15.2.7",
"mini-css-extract-plugin": "^2.9.0",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.4"
"tailwindcss": "^3.4.4",
"terser-webpack-plugin": "^5.3.10",
"webpack-cli": "^5.1.4"
},
"overrides": {
"nth-check": "$nth-check",
+6 -6
View File
@@ -28356,7 +28356,7 @@
:root {
--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Pro";
}
/* @font-face {
@font-face {
font-family: "Font Awesome 6 Pro";
font-style: normal;
font-weight: 900;
@@ -28364,7 +28364,7 @@
src:
url(../webfonts/fa-solid-900.woff2) format("woff2"),
url(../webfonts/fa-solid-900.ttf) format("truetype");
} */
}
.fa-solid,
.fas {
font-weight: 900;
@@ -28395,14 +28395,14 @@
url(../webfonts/fa-brands-400.woff2) format("woff2"),
url(../webfonts/fa-brands-400.ttf) format("truetype");
}
/* @font-face {
@font-face {
font-family: "Font Awesome 5 Pro";
font-display: block;
font-weight: 900;
src:
url(../webfonts/fa-solid-900.woff2) format("woff2"),
url(../webfonts/fa-solid-900.ttf) format("truetype");
} */
}
/* @font-face {
font-family: "Font Awesome 5 Pro";
font-display: block;
@@ -28427,13 +28427,13 @@
url(../webfonts/fa-duotone-900.woff2) format("woff2"),
url(../webfonts/fa-duotone-900.ttf) format("truetype");
} */
/* @font-face {
@font-face {
font-family: "FontAwesome";
font-display: block;
src:
url(../webfonts/fa-solid-900.woff2) format("woff2"),
url(../webfonts/fa-solid-900.ttf) format("truetype");
} */
}
@font-face {
font-family: "FontAwesome";
font-display: block;
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+5
View File
@@ -166,11 +166,16 @@ function App() {
path="/pdfRequestFiles/:docId"
element={<PdfRequestFiles />}
/>
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
<Route
path="/recipientSignPdf/:docId"
element={<PdfRequestFiles />}
/>
</Route>
<Route path="/sso" element={<SSOVerify />} />
<Route path="*" element={<PageNotFound />} />
+8 -2
View File
@@ -85,7 +85,13 @@ const Header = ({ showSidebar }) => {
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isOpen]);
const handleConsoleRedirect = () => {
if (window.location.host === "staging-app.opensignlabs.com") {
window.open(" https://staging-console.opensignlabs.com/");
} else {
window.open("https://console.opensignlabs.com/");
}
};
return (
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
@@ -188,7 +194,7 @@ const Header = ({ showSidebar }) => {
</li>
<li
onClick={() => {
window.open("https://console.opensignlabs.com/");
handleConsoleRedirect();
}}
>
<span>
@@ -104,7 +104,7 @@ function DriveBody(props) {
const signedUrl = data.SignedUrl;
//checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
}
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
@@ -112,7 +112,7 @@ function DriveBody(props) {
}
//checking if document has declined by someone
else if (isDecline) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
//checking draft type document
} else if (
signerExist?.length > 0 &&
@@ -123,7 +123,7 @@ function DriveBody(props) {
}
//Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
} //placeholder draft document
else if (
(signerExist?.length > 0 &&
@@ -1,23 +1,19 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError";
import { useLocation, useNavigate } from "react-router-dom";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
function DraftDocument() {
const navigate = useNavigate();
const query = useQuery();
const docId = query.get("docId");
const [isLoading, setIsLoading] = useState({
isLoader: true,
message: "This might take some time"
});
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const docId =
rowLevel && rowLevel?.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
useEffect(() => {
getDocumentDetails();
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -53,7 +49,7 @@ function DraftDocument() {
const signedUrl = data.SignedUrl;
//checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
}
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
@@ -61,7 +57,7 @@ function DraftDocument() {
}
//checking if document has declined by someone
else if (isDecline) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
//checking draft type document
} else if (
signerExist?.length > 0 &&
@@ -72,7 +68,7 @@ function DraftDocument() {
}
//Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`);
navigate(`/recipientSignPdf/${data.objectId}`);
} //placeholder draft document
else if (
(signerExist?.length > 0 &&
+20 -7
View File
@@ -7,7 +7,6 @@ import {
} from "../../constant/Utils";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
@@ -33,12 +32,12 @@ function Header({
isSignYourself,
setIsEmail,
completeBtnTitle,
setIsEditTemplate
setIsEditTemplate,
isPublicTemplate
}) {
const filterPrefill =
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner");
@@ -61,7 +60,7 @@ function Header({
}}
>
<div className="flex justify-between items-center py-[5px] px-[10px] ">
<div onClick={() => navigate(-1)}>
<div onClick={() => window.history.go(-2)}>
<i
className="fa-light fa-arrow-left text-base-content"
aria-hidden="true"
@@ -272,7 +271,7 @@ function Header({
</button>
)}
<button
onClick={() => navigate(-1)}
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
@@ -338,7 +337,7 @@ function Header({
) : (
<div className="flex" data-tut="reactourFifth">
<button
onClick={() => navigate(-1)}
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
@@ -414,10 +413,24 @@ function Header({
<span className="hidden lg:block ml-1">Mail</span>
</button>
</div>
) : isPublicTemplate ? (
<>
<div className="flex">
<button
type="button"
className="op-btn op-btn-primary op-btn-sm shadow"
onClick={() => {
embedWidgetsData();
}}
>
Sign Now
</button>
</div>
</>
) : (
<div className="flex">
<button
onClick={() => navigate(-1)}
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
+42 -24
View File
@@ -234,25 +234,29 @@ function Placeholder(props) {
setDraggingEnabled(false);
}
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
if (props.pos.type) {
if (
props.pos.type === "signature" ||
props.pos.type === "stamp" ||
props.pos.type === "image"
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (props.pos.type === "initials") {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
props.setIsInitial(true);
}
if (props?.ispublicTemplate) {
props.handleUserDetails();
} else {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
if (props.pos.type) {
if (
props.pos.type === "signature" ||
props.pos.type === "stamp" ||
props.pos.type === "image"
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (props.pos.type === "initials") {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
props.setIsInitial(true);
}
} else {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
}
}
} else if (
props.isPlaceholder &&
@@ -295,6 +299,12 @@ function Placeholder(props) {
}
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
handleWidgetIdandPopup();
} else if (
props?.isNeedSign &&
props?.uniqueId &&
props.data?.Id === props?.uniqueId
) {
handleWidgetIdandPopup();
} else if (props.isPlaceholder || props.isSignYourself) {
handleWidgetIdandPopup();
}
@@ -660,6 +670,19 @@ function Placeholder(props) {
}
};
const getCursor = () => {
if (props.data && props.isNeedSign) {
if (props?.signerObjId && props.data?.signerObjId === props.signerObjId) {
return "pointer";
} else if (props?.uniqueId && props.data?.Id === props.uniqueId) {
return "pointer";
} else {
return "not-allowed";
}
} else {
return "all-scroll";
}
};
return (
<Rnd
id={props.pos.key}
@@ -710,12 +733,7 @@ function Placeholder(props) {
props.pos.type !== "company" &&
props.pos.type !== "job title" &&
"center",
cursor:
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId
? "pointer"
: "not-allowed"
: "all-scroll",
cursor: getCursor(),
zIndex:
props.pos.type === "date"
? props.pos.key === props.selectWidgetId
@@ -57,7 +57,7 @@ function RenderAllPdfPage({
<div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
<i
style={{ color: bookmarkColor || "red" }}
className="fa-light fa-bookmark"
className="fa-solid fa-bookmark"
></i>
</div>
)
@@ -58,7 +58,9 @@ function RenderPdf({
uniqueId,
pdfOriginalWH,
scale,
setIsSelectId
setIsSelectId,
ispublicTemplate,
handleUserDetails
}) {
const isMobile = window.innerWidth < 767;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
@@ -195,6 +197,8 @@ function RenderPdf({
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
ispublicTemplate={ispublicTemplate}
handleUserDetails={handleUserDetails}
/>
</React.Fragment>
)
@@ -430,7 +434,7 @@ function RenderPdf({
>
<div
style={{
width: containerWH.width && containerWH.width * scale
width: containerWH?.width && containerWH?.width * scale
}}
ref={drop}
id="container"
+2 -2
View File
@@ -164,8 +164,8 @@ function SignPad({
}
}
const trimmedName = currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const trimmedName = currentUserName && currentUserName.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName);
setFontSelect("Fasthand");
@@ -35,7 +35,9 @@ function SignerListComponent(props) {
className="flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1"
>
<span className="text-[12px] text-center font-bold text-black uppercase">
{getFirstLetter(props?.obj?.Name || props?.obj?.Role)}
{getFirstLetter(
props.obj?.Name || props.obj?.email || props.obj?.Role
)}
</span>
</div>
<div className="flex flex-col">
+4 -4
View File
@@ -43,7 +43,7 @@ export default function reportJson(id) {
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature",
redirectUrl: "pdfRequestFiles",
redirectUrl: "draftDocument",
action: "redirect"
}
],
@@ -69,7 +69,7 @@ export default function reportJson(id) {
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye",
redirectUrl: "pdfRequestFiles",
redirectUrl: "draftDocument",
action: "redirect"
},
{
@@ -207,7 +207,7 @@ export default function reportJson(id) {
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye",
redirectUrl: "pdfRequestFiles",
redirectUrl: "draftDocument",
action: "redirect"
},
{
@@ -258,7 +258,7 @@ export default function reportJson(id) {
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature",
redirectUrl: "pdfRequestFiles",
redirectUrl: "draftDocument",
action: "redirect"
}
]
+10
View File
@@ -67,6 +67,8 @@ const HomeLayout = () => {
const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
const serverUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
secure: true,
domain: updateDomain
@@ -75,6 +77,14 @@ const HomeLayout = () => {
secure: true,
domain: updateDomain
});
setCookie("server_url", serverUrl, {
secure: true,
domain: updateDomain
});
setCookie("parse_app_id", parseAppId, {
secure: true,
domain: updateDomain
});
};
async function checkIsSubscribed() {
+417 -43
View File
@@ -6,7 +6,6 @@ import Parse from "parse";
import axios from "axios";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import SignPad from "../components/pdf/SignPad";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import Tour from "reactour";
@@ -49,15 +48,7 @@ import SignerListComponent from "../components/pdf/SignerListComponent";
import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
function PdfRequestFiles() {
const { docId } = useParams();
const navigate = useNavigate();
const location = useLocation();
const query = useQuery();
const sendmail = query.get("sendmail");
function PdfRequestFiles(props) {
const [pdfDetails, setPdfDetails] = useState([]);
const [signedSigners, setSignedSigners] = useState([]);
const [unsignedSigners, setUnSignedSigners] = useState([]);
@@ -129,19 +120,18 @@ function PdfRequestFiles() {
const [zoomPercent, setZoomPercent] = useState(0);
const [totalZoomPercent, setTotalZoomPercent] = useState();
const [scale, setScale] = useState(1);
const [uniqueId, setUniqueId] = useState("");
const [isPublicTemplate, setIsPublicTemplate] = useState(false);
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
const [isOtp, setIsOtp] = useState(false);
const [loading, setLoading] = useState(false);
const [res, setRes] = useState({});
const [documentId, setDocumentId] = useState("");
const [isPublicContact, setIsPublicContact] = useState(false);
const isHeader = useSelector((state) => state.showHeader);
const divRef = useRef(null);
const isMobile = window.innerWidth < 767;
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const objectId =
rowLevel && rowLevel.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
const documentId = docId ? docId : objectId && objectId;
const senderUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
@@ -151,12 +141,36 @@ function PdfRequestFiles() {
);
const jsonSender = JSON.parse(senderUser);
let isGuestSignFlow = false;
let sendmail;
let getDocId = "";
const route = !props.templateId && window.location.pathname;
const getQuery =
!props.templateId &&
window.location?.search &&
window.location?.search?.split("?");
if (getQuery) {
sendmail = getQuery[1].split("=")[1];
}
const checkSplit = route && route?.split("/");
if (checkSplit && checkSplit.length > 4) {
isGuestSignFlow = true;
getDocId = checkSplit[3];
} else {
getDocId = checkSplit[2];
}
let getDocumentId = getDocId || documentId;
useEffect(() => {
if (documentId) {
getDocumentDetails();
if (getDocumentId) {
setDocumentId(getDocumentId);
getDocumentDetails(getDocumentId);
} else if (props.templateId) {
getTemplateDetails();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [props.templateId, getDocumentId]);
useEffect(() => {
const updateSize = () => {
if (divRef.current) {
@@ -171,7 +185,6 @@ function PdfRequestFiles() {
// Use setTimeout to wait for the transition to complete
const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
@@ -208,7 +221,7 @@ function PdfRequestFiles() {
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
// handleRecipientSign();
//handleRecipientSign();
} catch (error) {
alert(error.message);
} finally {
@@ -225,7 +238,7 @@ function PdfRequestFiles() {
await handleSendOTP(currentUser?.email);
};
async function checkIsSubscribed(extUserId, contactId) {
const isGuestSign = location.pathname.includes("/load/") || false;
const isGuestSign = isGuestSignFlow || false;
const res = await fetchSubscription(extUserId, contactId, isGuestSign);
const plan = res.plan;
const billingDate = res?.billingDate;
@@ -237,10 +250,10 @@ function PdfRequestFiles() {
setIsSubscribed(true);
return true;
} else {
if (location.pathname.includes("/load/")) {
if (isGuestSign) {
setIsSubscriptionExpired(true);
} else {
navigate(`/subscription`);
window.location.href = "/subscription";
}
}
} else if (isGuestSign) {
@@ -254,15 +267,92 @@ function PdfRequestFiles() {
if (isGuestSign) {
setIsSubscriptionExpired(true);
} else {
navigate(`/subscription`);
window.location.href = "/subscription";
}
}
}
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (isNextUser) => {
const getTemplateDetails = async () => {
try {
const params = { templateId: props.templateId, ispublic: true };
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
const documentData =
templateDeatils.data && templateDeatils.data.result
? [templateDeatils.data.result]
: [];
if (documentData && documentData.length > 0) {
setIsPublicTemplate(true);
const getPublicRole = documentData[0].PublicRole[0];
const getUniqueIdDetails = documentData[0].Placeholders.find(
(x) => x.Role === getPublicRole
);
if (getUniqueIdDetails) {
setUniqueId(getUniqueIdDetails.Id);
}
setSignerPos(documentData[0].Placeholders);
let placeholdersOrSigners = [];
// const placeholder = documentData[0]?.Placeholders;
for (const placeholder of documentData[0].Placeholders) {
//`emailExist` variable to handle condition for quick send flow and show unsigned signers list
const signerIdExist = placeholder?.signerObjId;
if (signerIdExist) {
const getSignerData = documentData[0].Signers.find(
(data) => data.objectId === placeholder?.signerObjId
);
placeholdersOrSigners.push(getSignerData);
} else {
placeholdersOrSigners.push(placeholder);
}
}
setUnSignedSigners(placeholdersOrSigners);
setPdfDetails(documentData);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else {
setHandleError("No Data Found!");
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
}
} catch (err) {
console.log("err ", err);
if (err?.response?.data?.code === 101) {
setHandleError("Error: Template not found!");
} else {
setHandleError("Error: Something went wrong!");
}
}
};
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (docId, isNextUser) => {
let currUserId;
//getting document details
const documentData = await contractDocument(documentId);
const documentData = await contractDocument(documentId || docId);
if (documentData && documentData.length > 0) {
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const isCompleted =
@@ -336,8 +426,7 @@ function PdfRequestFiles() {
setCurrentSigner(true);
}
}
const isGuestSign = location.pathname.includes("/load/");
const isGuestSign = isGuestSignFlow;
if (
!isGuestSign &&
!isCompleted &&
@@ -436,14 +525,14 @@ function PdfRequestFiles() {
const placeholdersOrSigners = [];
for (const placeholder of documentData[0].Placeholders) {
//`emailExist` variable to handle condition for quick send flow and show unsigned signers list
const emailExist = placeholder?.email;
if (emailExist) {
placeholdersOrSigners.push(placeholder);
} else {
const signerIdExist = placeholder?.signerObjId;
if (signerIdExist) {
const getSignerData = documentData[0].Signers.find(
(data) => data.objectId === placeholder?.signerObjId
);
placeholdersOrSigners.push(getSignerData);
} else {
placeholdersOrSigners.push(placeholder);
}
}
//condition to check already signed document by someone
@@ -872,7 +961,12 @@ function PdfRequestFiles() {
`${pdfDetails?.[0].objectId}/${user.Email}`
);
}
let signPdf = `${hostUrl}/login/${encodeBase64}`;
// let signPdf = `${hostUrl}/login/${encodeBase64}`;
const hostPublicUrl =
"https://staging-app.opensignlabs.com";
let signPdf = props?.templateId
? `${hostPublicUrl}/login/${encodeBase64}`
: `${hostUrl}/login/${encodeBase64}`;
const openSignUrl =
"https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
@@ -1075,7 +1169,6 @@ function PdfRequestFiles() {
return newState; // Update the state with the modified copy
});
};
//function for save button to save signature or image url
const saveSign = (type, isDefaultSign, width, height) => {
const isTypeText = width && height ? true : false;
@@ -1357,9 +1450,141 @@ function PdfRequestFiles() {
/>
);
};
const handleUserDetails = () => {
setIsPublicContact(true);
};
//`handlePublicUser` function to use create user from public role and create document from public template
const handlePublicUser = async (e) => {
e.preventDefault();
setLoading(true);
try {
const params = {
...contact,
templateid: pdfDetails[0]?.objectId,
role: pdfDetails[0]?.PublicRole[0]
};
const userRes = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}/functions/publicuserlinkcontacttodoc`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
if (userRes?.data?.result) {
setRes(userRes.data.result);
await SendOtp();
} else {
alert("something went wrong");
}
} catch (e) {
console.log("e", e);
// setIsLoader(false);
}
};
const handleInputChange = (e) => {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const SendOtp = async () => {
try {
const params = { email: contact.email, docId: res.docId };
const Otp = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/SendOTPMailV1`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
if (Otp) {
setIsOtp(true);
setLoading(false);
}
} catch (error) {
alert("something went wrong!");
}
};
//verify OTP send on via email
const VerifyOTP = async (e) => {
e.preventDefault();
setLoading(true);
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (otp) {
// setLoading(true);
try {
let url = `${serverUrl}/functions/AuthLoginAsMail/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId
};
let body = {
email: contact.email,
otp: otp
};
let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") {
alert("Invalid Otp");
setLoading(false);
} else if (user.data.result === "user not found!") {
alert("User not found!");
setLoading(false);
} else {
let _user = user.data.result;
const parseId = localStorage.getItem("parseAppId");
const contractUserDetails = await contractUsers(_user.email);
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem(
`Parse/${parseId}/currentUser`,
JSON.stringify(_user)
);
if (contractUserDetails && contractUserDetails.length > 0) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
);
}
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken);
setLoading(false);
// navigate(`/load/recipientSignPdf/${res?.docId}/${res?.contactId}`);
// document.getElementById("my_modal").close();
setIsPublicContact(false);
setIsPublicTemplate(false);
setIsLoading({
isLoad: false
});
setDocumentId(res?.docId);
getDocumentDetails(res?.docId);
}
} catch (error) {
console.log("err ", error);
}
} else {
alert("Please Enter OTP!");
}
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} />
<Title title={props.templateId ? "Public Sign" : "Request Sign"} />
{isSubscriptionExpired ? (
<ModalUi
title={"Subscription Expired"}
@@ -1406,7 +1631,9 @@ function PdfRequestFiles() {
}}
className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"
>
{!requestSignTour && requestSignTourFunction()}
{!requestSignTour &&
signerObjectId &&
requestSignTourFunction()}
<ModalUi
isOpen={isAlert.isShow}
title={"Alert message"}
@@ -1481,6 +1708,147 @@ function PdfRequestFiles() {
/>
)}
<ModalUi
isOpen={isPublicContact}
title={"Contact Details"}
handleClose={() => {
setIsPublicContact(false);
}}
>
<div className="h-full p-[20px]">
{isOtp ? (
<form onSubmit={VerifyOTP}>
<div className="flex flex-col gap-2">
<span>You will get a OTP via Email</span>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<input
type="number"
name="otp"
className="grow"
placeholder="Enter Verification Code"
required
value={otp}
onChange={(e) => setOtp(e.target.value)}
disabled={loading}
/>
</label>
</div>
<div className="op-modal-action">
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
// document.getElementById("my_modal").close();
setIsPublicContact(false);
setLoading(false);
setIsOtp(false);
setOtp();
setContact({
name: "",
email: "",
phone: ""
});
}}
>
Cancel
</button>
<button
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Verify"}
</button>
</div>
</div>
</form>
) : (
<form onSubmit={handlePublicUser}>
<div className="flex flex-col gap-2">
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
<input
type="text"
className="grow"
name="name"
value={contact.name}
onChange={handleInputChange}
placeholder="name"
required
disabled={loading}
/>
</label>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
<path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
</svg>
<input
type="text"
className="grow"
name="email"
value={contact.email}
onChange={handleInputChange}
placeholder="Email"
required
disabled={loading}
/>
</label>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
<input
value={contact.phone}
onChange={handleInputChange}
type="text"
name="phone"
className="grow"
placeholder="phone"
disabled={loading}
/>
</label>
</div>
<div className="op-modal-action">
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
// document.getElementById("my_modal").close();
setIsPublicContact(false);
}}
>
Close
</button>
<button
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Submit"}
</button>
</div>
</div>
</form>
)}
</div>
</ModalUi>
<ModalUi
isOpen={defaultSignAlert.isShow}
title={"Auto sign"}
@@ -1673,7 +2041,7 @@ function PdfRequestFiles() {
/>
{/* pdf header which contain funish back button */}
<Header
isPdfRequestFiles={true}
isPdfRequestFiles={isPublicTemplate ? false : true}
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
@@ -1681,7 +2049,9 @@ function PdfRequestFiles() {
signerPos={signerPos}
isSigned={isSigned}
isCompleted={isCompleted.isCertificate}
embedWidgetsData={embedWidgetsData}
embedWidgetsData={
isPublicTemplate ? handleUserDetails : embedWidgetsData
}
isShowHeader={true}
setIsDecline={setIsDecline}
decline={true}
@@ -1696,6 +2066,7 @@ function PdfRequestFiles() {
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
isPublicTemplate={isPublicTemplate}
/>
<div ref={divRef} data-tut="pdfArea" className="h-[95%]">
@@ -1732,6 +2103,9 @@ function PdfRequestFiles() {
setTotalZoomPercent={setTotalZoomPercent}
setScale={setScale}
scale={scale}
uniqueId={uniqueId}
ispublicTemplate={isPublicTemplate}
handleUserDetails={handleUserDetails}
/>
)}
</div>
+3 -12
View File
@@ -170,16 +170,7 @@ function PlaceHolderSign() {
isDragStamp: !!monitor.isDragging()
})
});
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const objectId =
rowLevel && rowLevel.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
const documentId = docId ? docId : objectId && objectId;
const documentId = docId;
useEffect(() => {
if (documentId) {
getDocumentDetails();
@@ -633,8 +624,6 @@ function PlaceHolderSign() {
setShowDropdown(true);
} else if (dragTypeValue === "checkbox") {
setIsCheckbox(true);
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
} else if (
[
textInputWidget,
@@ -647,6 +636,8 @@ function PlaceHolderSign() {
) {
setFontSize(12);
setFontColor("black");
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
}
setWidgetType(dragTypeValue);
setSignKey(key);
+10
View File
@@ -22,6 +22,7 @@ const Report = () => {
const [form, setForm] = useState("");
const [tourData, setTourData] = useState([]);
const [isDontShow, setIsDontShow] = useState(false);
const [isPublic, setIsPublic] = useState({});
const abortController = new AbortController();
const docPerPage = 10;
@@ -156,6 +157,13 @@ const Report = () => {
? [...prevRecord, ...res.data.result]
: res.data.result
);
const listData = res.data.result;
setIsPublic(
listData.reduce((acc, item) => {
acc[item.objectId] = item?.IsPublic || false;
return acc;
}, {})
);
}
}
setIsLoader(false);
@@ -193,6 +201,8 @@ const Report = () => {
report_help={reporthelp}
tourData={tourData}
isDontShow={isDontShow}
setIsPublic={setIsPublic}
isPublic={isPublic}
/>
) : (
<PageNotFound prefix={"Report"} />
+151 -76
View File
@@ -1,8 +1,4 @@
import React, {
useState,
useEffect
// useRef
} from "react";
import React, { useState, useEffect, useRef } from "react";
import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -32,10 +28,11 @@ function UserProfile() {
const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
// const [publicUserName, setPublicUserName] = useState(
// extendUser && extendUser?.[0]?.UserName
// );
// const previousPublicUserName = useRef(publicUserName);
const [isUpgrade, setIsUpgrade] = useState(false);
const [publicUserName, setPublicUserName] = useState(
extendUser && extendUser?.[0]?.UserName
);
const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company
);
@@ -46,7 +43,10 @@ function UserProfile() {
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
// const [userNameError, setUserNameError] = useState("");
const [userNameError, setUserNameError] = useState("");
const [tagLine, setTagLine] = useState(
extendUser && extendUser?.[0]?.Tagline
);
useEffect(() => {
getUserDetail();
}, []);
@@ -85,31 +85,31 @@ function UserProfile() {
}
};
//function to check public username already exist
// const handleCheckPublicUserName = async () => {
// try {
// const res = await Parse.Cloud.run("getpublicusername", {
// username: publicUserName
// });
// if (res) {
// setIsLoader(false);
// setUserNameError("user name already exist");
// setTimeout(() => {
// setUserNameError("");
// }, 3000);
// return res;
// }
// } catch (e) {
// console.log("error in getpublicusername cloud function");
// }
// };
const handleCheckPublicUserName = async () => {
try {
const res = await Parse.Cloud.run("getpublicusername", {
username: publicUserName
});
if (res) {
setIsLoader(false);
setUserNameError("user name already exist");
setTimeout(() => {
setUserNameError("");
}, 3000);
return res;
}
} catch (e) {
console.log("error in getpublicusername cloud function");
}
};
const handleSubmit = async (e) => {
e.preventDefault();
let phn = Phone,
res = "";
//condition to call cloud function when user change publicUserName
// if (previousPublicUserName.current !== publicUserName) {
// res = await handleCheckPublicUserName();
// }
if (previousPublicUserName.current !== publicUserName) {
res = await handleCheckPublicUserName();
}
if (!res) {
setIsLoader(true);
try {
@@ -162,8 +162,9 @@ function UserProfile() {
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company
// UserName: publicUserName || ""
Company: company,
UserName: publicUserName || "",
Tagline: tagLine || ""
};
await axios.put(
@@ -277,23 +278,19 @@ function UserProfile() {
alert("OTP sent on you email");
};
//function to handle onchange username and restrict 6-characters username for free users
// const handleOnchangeUserName = (e) => {
// const value = e.target.value;
// if (value.length > 6 && !isSubscribe) {
// setUserNameError("Please upgrade to allow more than 6 characters.");
// setTimeout(() => {
// setUserNameError("");
// }, 2000);
// } else {
// setPublicUserName(e.target.value);
// }
// };
const handleOnchangeUserName = (e) => {
setPublicUserName(e.target.value);
};
const handleOnchangeTagLine = (e) => {
setTagLine(e.target.value);
};
const handleCancel = () => {
setEditMode(false);
SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg"));
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
@@ -307,13 +304,13 @@ function UserProfile() {
</div>
) : (
<div className="flex justify-center items-center w-full relative">
{/* {userNameError && (
{userNameError && (
<div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
>
{userNameError}
</div>
)} */}
)}
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
@@ -441,37 +438,75 @@ function UserProfile() {
)}
</span>
</li>
{/* {isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">
Public profile :{" "}
<Tooltip
message={`this is your public URL. Copy or share it
{isEnableSubscription && (
<>
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold flex gap-1">
Public profile :{" "}
<Tooltip
maxWidth="max-w-[250px]"
message={`this is your public URL. Copy or share it
with the signer, and you will be able to see
all your publicly set templates.`}
/>
</span>
<div className="flex text-xs items-center">
<span>opensign-me.vercel.app/</span>
{editmode ? (
<input
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="op-input op-input-bordered op-input-xs"
</span>
<div className="flex md:flex-row flex-col md:items-center">
<span className="mb-1 md:mb-1">opensign.me/</span>
{editmode ? (
<input
maxLength={40}
style={{
border:
!isSubscribe &&
publicUserName.length > 0 &&
publicUserName.length < 9 &&
"solid red"
}}
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="op-input op-input-bordered op-input-xs"
/>
)}
</div>
</li>
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold flex gap-1">
Tagline :{" "}
<Tooltip
maxWidth="max-w-[250px]"
message={`This Tagline will appear on your OpenSign public profile for ex. https://opensign.me/alex`}
/>
)}
</div>
</li>
)} */}
</span>
<div className="flex md:flex-row flex-col md:items-center">
{editmode ? (
<input
onChange={handleOnchangeTagLine}
value={tagLine}
disabled={!editmode}
placeholder="enter tagline"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
) : (
<input
value={extendUser?.[0]?.Tagline}
disabled
placeholder="enter tagline"
className="op-input op-input-bordered op-input-xs"
/>
)}
</div>
</li>
</>
)}
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span
@@ -517,9 +552,17 @@ function UserProfile() {
<div className="flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4">
<button
type="button"
onClick={(e) =>
editmode ? handleSubmit(e) : setEditMode(true)
}
onClick={(e) => {
if (
!isSubscribe &&
publicUserName.length > 0 &&
publicUserName.length < 9
) {
setIsUpgrade(true);
} else {
editmode ? handleSubmit(e) : setEditMode(true);
}
}}
className="op-btn op-btn-primary"
>
{editmode ? "Save" : "Edit"}
@@ -576,6 +619,38 @@ function UserProfile() {
)}
</ModalUi>
)}
{isUpgrade && (
<div className="op-modal op-modal-open">
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
<>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40"
onClick={() => setIsUpgrade(false)}
>
</div>
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body">
<h2 className="op-card-title">Upgrade to Plan</h2>
<p>
To have a username less than 8 character please
subscribe
</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
className="op-btn op-btn-accent"
>
Upgrade Now
</button>
</div>
</div>
</div>
</>
</div>
</div>
)}
</div>
)}
</React.Fragment>
+310 -312
View File
@@ -4,7 +4,7 @@ import { useLocation, useNavigate } from "react-router-dom";
import axios from "axios";
import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner";
// import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription } from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
import { RWebShare } from "react-web-share";
@@ -13,10 +13,10 @@ import Parse from "parse";
import { saveAs } from "file-saver";
import {
checkIsSubscribedTeam,
// copytoData,
copytoData,
replaceMailVaribles
} from "../constant/Utils";
// import Confetti from "react-confetti";
import Confetti from "react-confetti";
import EditorToolbar, {
module1,
formats
@@ -26,7 +26,6 @@ import "react-quill/dist/quill.snow.css";
import BulkSendUi from "../components/BulkSendUi";
import Loader from "./Loader";
import Select from "react-select";
import { isEnableSubscription } from "../constant/const";
import SubscribeCard from "./SubscribeCard";
const ReportTable = (props) => {
@@ -48,7 +47,7 @@ const ReportTable = (props) => {
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isResendMail, setIsResendMail] = useState({});
// const [isMakePublic, setIsMakePublic] = useState({});
const [isMakePublic, setIsMakePublic] = useState({});
const [mail, setMail] = useState({ subject: "", body: "" });
const [userDetails, setUserDetails] = useState({});
const [isNextStep, setIsNextStep] = useState({});
@@ -60,12 +59,10 @@ const ReportTable = (props) => {
const [teamList, setTeamList] = useState([]);
const [selectedTeam, setSelectedTeam] = useState([]);
const onChange = (selectedOptions) => setSelectedTeam(selectedOptions);
// const [selectedPublicRole, setSelectedPublicRole] = useState("");
// const [isCelebration, setIsCelebration] = useState(false);
// const [currentLists, setCurrentLists] = useState([]);
// const [isPublic, setIsPublic] = useState({});
// const [isPublicProfile, setIsPublicProfile] = useState({});
// const [publicUserName, setIsPublicUserName] = useState("");
const [selectedPublicRole, setSelectedPublicRole] = useState("");
const [isCelebration, setIsCelebration] = useState(false);
const [isPublicProfile, setIsPublicProfile] = useState({});
const [publicUserName, setIsPublicUserName] = useState("");
const [isViewShare, setIsViewShare] = useState({});
const [isSubscribe, setIsSubscribe] = useState(true);
const [reason, setReason] = useState("");
@@ -139,7 +136,7 @@ const ReportTable = (props) => {
checkTourStatus();
fetchTeamList();
return () => setCurrentPage(1);
// eslint-disable-next-line
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// `fetchTeamList` is used to fetch team list for share with functionality
@@ -200,7 +197,9 @@ const ReportTable = (props) => {
} else {
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
try {
const params = { templateId: item.objectId };
const params = {
templateId: item.objectId
};
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
@@ -329,9 +328,7 @@ const ReportTable = (props) => {
}
}
} else {
localStorage.removeItem("rowlevel");
navigate(`/${act.redirectUrl}`);
localStorage.setItem("rowlevel", JSON.stringify(item));
navigate(`/${act.redirectUrl}?docId=${item?.objectId}`);
}
};
@@ -370,19 +367,6 @@ const ReportTable = (props) => {
const indexOfLastDoc = currentPage * props.docPerPage;
const indexOfFirstDoc = indexOfLastDoc - props.docPerPage;
const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc);
// useEffect(() => {
// // `currentLists` is total record render on current page
// const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc);
// //check public template and save in a object to show public and private template
// // setIsPublic(
// // currentList.reduce((acc, item) => {
// // acc[item.objectId] = item?.IsPublic || false;
// // return acc;
// // }, {})
// // );
// setCurrentLists(currentList);
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [indexOfLastDoc, indexOfFirstDoc]);
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
@@ -444,19 +428,19 @@ const ReportTable = (props) => {
setActLoader({});
}
};
const handleClose = () => {
const handleClose = (item) => {
setIsRevoke({});
setIsDeleteModal({});
setIsMakePublic({});
setSelectedPublicRole("");
setIsPublicProfile({});
if (item?.objectId) {
props.setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}
setReason("");
// setIsMakePublic({});
// setSelectedPublicRole("");
// setIsPublicProfile({});
// if (item?.objectId) {
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: !prevStates[item.objectId]
// }));
// }
};
const handleShare = (item) => {
@@ -856,131 +840,148 @@ const ReportTable = (props) => {
};
//function to make template public and set public role
// const handlePublicTemplate = async (item) => {
// if (selectedPublicRole || !isPublic[item.objectId]) {
// setActLoader({ [item.objectId]: true });
// setIsMakePublic(false);
// try {
// const res = await Parse.Cloud.run("createpublictemplate", {
// templateid: item.objectId,
// ispublic: isPublic[item.objectId],
// publicrole: [selectedPublicRole]
// });
const handlePublicTemplate = async (item) => {
if (selectedPublicRole || !props.isPublic[item.objectId]) {
setActLoader({ [item.objectId]: true });
setIsMakePublic(false);
try {
const res = await Parse.Cloud.run("createpublictemplate", {
templateid: item.objectId,
ispublic: props.isPublic[item.objectId],
publicrole: [selectedPublicRole]
});
// if (res.status === "success") {
// setIsAlert(true);
// setTimeout(() => setIsAlert(false), 1500);
// if (isPublic[item.objectId]) {
// setAlertMsg({
// type: "success",
// message: "You have successfully made the template public."
// });
// setIsCelebration(true);
// setTimeout(() => {
// setIsCelebration(false);
// }, 5000);
// setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] });
// } else {
// setAlertMsg({
// type: "success",
// message: "You have successfully made the template private."
// });
// setSelectedPublicRole("");
// }
// const updateList = props.List.map((x) =>
// x.objectId === item.objectId
// ? { ...x, IsPublic: isPublic[item.objectId] }
// : x
// );
// props.setList(updateList);
// setActLoader({});
// }
// } catch (e) {
// console.log("error in createpublictemplate", e);
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "Something went wrong, Please try again later!"
// });
// setTimeout(() => setIsAlert(false), 1500);
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: !prevStates[item.objectId]
// }));
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "You need to select a role for the public signers."
// });
// setTimeout(() => setIsAlert(false), 1500);
// }
// };
if (res.status === "success") {
setIsAlert(true);
setTimeout(() => setIsAlert(false), 1500);
if (props.isPublic[item.objectId]) {
setAlertMsg({
type: "success",
message: "You have successfully made the template public."
});
setIsCelebration(true);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
setIsPublicProfile({
[item.objectId]: props.isPublic[item.objectId]
});
} else {
setAlertMsg({
type: "success",
message: "You have successfully made the template private."
});
setSelectedPublicRole("");
}
const updateList = props.List.map((x) =>
x.objectId === item.objectId
? { ...x, IsPublic: props.isPublic[item.objectId] }
: x
);
props.setList(updateList);
setActLoader({});
}
} catch (e) {
console.log("error in createpublictemplate", e);
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Something went wrong, Please try again later!"
});
setTimeout(() => setIsAlert(false), 1500);
props.setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "You need to select a role for the public signers."
});
setTimeout(() => setIsAlert(false), 1500);
}
};
const handleViewSigners = (item) => {
setIsViewShare({ [item.objectId]: true });
};
//function to handle change template status is public or private
// const handlePublicChange = async (e, item) => {
// const getPlaceholder = item?.Placeholders;
// //condiiton to check role is exist or not
// if (getPlaceholder && getPlaceholder.length > 0) {
// const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
// placeholderObj?.placeHolder?.some((holder) =>
// holder?.pos?.some((posItem) => posItem?.type === "signature")
// )
// );
// if (checkIsSignatureExistt) {
// let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
// const userName = extendUser[0]?.UserName;
// setIsPublicUserName(extendUser[0]?.UserName);
// //condition to check user have public url or not
// if (userName) {
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: e.target.checked
// }));
// const getPlaceholder = item?.Placeholders;
// if (getPlaceholder.length === 1) {
// setSelectedPublicRole(getPlaceholder[0]?.Role);
// }
const handlePublicChange = async (e, item) => {
const getPlaceholder = item?.Placeholders;
//condiiton to check role is exist or not
if (getPlaceholder && getPlaceholder.length > 0) {
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
const signers = item?.Signers;
if (getPlaceholder.length - 1 === signers?.length) {
if (checkIsSignatureExistt) {
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const userName = extendUser[0]?.UserName;
setIsPublicUserName(extendUser[0]?.UserName);
//condition to check user have public url or not
if (userName) {
props.setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: e.target.checked
}));
const getRole = item.Placeholders.find((data) => !data.signerObjId);
if (getRole?.Role) {
setSelectedPublicRole(getRole?.Role);
}
// setIsMakePublic({ [item.objectId]: true });
// } else {
// setIsPublicProfile({ [item.objectId]: true });
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message:
// " Please ensure there's at least one signature widget added for all recipients."
// });
// setTimeout(() => setIsAlert(false), 5000);
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "Please assign at least one role to make this template public."
// });
// setTimeout(() => setIsAlert(false), 5000);
// }
// };
setIsMakePublic({ [item.objectId]: true });
} else {
setIsPublicProfile({ [item.objectId]: true });
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message:
" Please ensure there's at least one signature widget added for all recipients."
});
setTimeout(() => setIsAlert(false), 5000);
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message:
"Please attach signers to all remaining users except the public role user."
});
setTimeout(() => setIsAlert(false), 5000);
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Please assign at least one role to make this template public."
});
setTimeout(() => setIsAlert(false), 5000);
}
};
//function to copy public profile links
// const copytoProfileLink = () => {
// const url = `https://opensign-me.vercel.app/${publicUserName}`;
// copytoData(url);
// setIsAlert(true);
// setAlertMsg({
// type: "success",
// message: "Copied."
// });
// setTimeout(() => setIsAlert(false), 1500);
// };
// function to copy public profile links
const copytoProfileLink = () => {
const url = `https://opensign.me/${publicUserName}`;
copytoData(url);
setIsAlert(true);
setAlertMsg({
type: "success",
message: "Copied."
});
setTimeout(() => setIsAlert(false), 1500);
};
const handleShowRole = (item) => {
const getRole = item.Placeholders.find((data) => !data.signerObjId);
return getRole?.Role;
};
// `handleShareWith` is used to save teams in sharedWith field
const handleShareWith = async (e, template) => {
e.preventDefault();
@@ -1024,11 +1025,11 @@ const ReportTable = (props) => {
</div>
)}
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{/* {isCelebration && (
{isCelebration && (
<div className="relative z-[1000]">
<Confetti width={window.innerWidth} height={window.innerHeight} />
</div>
)} */}
)}
{isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
{props.tourData && props.ReportName === "Templates" && (
<Tour
@@ -1077,9 +1078,9 @@ const ReportTable = (props) => {
<th className="px-4 py-2">{item}</th>
</React.Fragment>
))}
{/* {props.ReportName === "Templates" && isEnableSubscription && (
<th className="px-4 py-2">Public</th>
)} */}
{props.ReportName === "Templates" && isEnableSubscription && (
<th className="px-4 py-2">Public</th>
)}
{props.actions?.length > 0 && (
<th className="px-4 py-2 text-transparent pointer-events-none">
Action
@@ -1193,164 +1194,161 @@ const ReportTable = (props) => {
"-"
)}
</td>
{/* {props.ReportName === "Templates" &&
isEnableSubscription && (
<td className=" pl-[20px] py-2">
{props.ReportName === "Templates" && (
<div className="flex flex-row">
<label
className={
"cursor-pointer relative inline-flex items-center mb-0"
{props.ReportName === "Templates" &&
isEnableSubscription && (
<td className=" pl-[20px] py-2">
{props.ReportName === "Templates" && (
<div className="flex flex-row">
<label
className={
"cursor-pointer relative inline-flex items-center mb-0"
}
>
<input
checked={props.isPublic?.[item.objectId]}
onChange={(e) =>
handlePublicChange(e, item)
}
type="checkbox"
value=""
className="sr-only peer"
/>
<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>
)}
{isMakePublic[item.objectId] && (
<ModalUi
isOpen
title={
props.isPublic[item.objectId]
? "Make template public"
: "Make template private"
}
handleClose={() => {
setIsMakePublic({});
setSelectedPublicRole("");
props.setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]:
!prevStates[item.objectId]
}));
}}
>
<div className="m-[20px]">
<div className="font-normal text-black">
<p className="text-lg">
{props.isPublic[item.objectId]
? `Are you sure you want to make this template public ?`
: `Are you sure you want to make this template private ? This will remove it from your public profile ?`}
</p>
{props.isPublic[item.objectId] && (
<div className="flex mt-2 gap-2 md:items-center">
<p className="text-[15px]">
Public role :{" "}
</p>
{/* {item?.Placeholders?.length > 1 ? (
<select
className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]"
name="textvalidate"
value={selectedPublicRole}
onChange={(e) =>
setSelectedPublicRole(
e.target.value
)
}
>
<option
disabled
style={{ fontSize: "13px" }}
value=""
>
Select...
</option>
{handleShowRole(item)}
</select>
) : ( */}
<input
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-sm"
value={handleShowRole(item)}
/>
{/* {item?.Placeholders[0]?.Role} */}
{/* {handleShowRole(item)} */}
{/* </div> */}
{/* )} */}
</div>
)}
</div>
<hr className="bg-[#ccc] mt-2 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() =>
handlePublicTemplate(item)
}
className="op-btn op-btn-primary"
>
Submit
</button>
<button
onClick={() => handleClose(item)}
className="op-btn op-btn-secondary"
>
Cancel
</button>
</div>
</div>
</ModalUi>
)}
{isPublicProfile[item.objectId] && (
<ModalUi
isOpen
title={"Public URL"}
handleClose={() => {
setIsPublicProfile({});
}}
reduceWidth={
"md:min-w-[440px] md:max-w-[550px]"
}
>
<input
checked={isPublic?.[item.objectId]}
onChange={(e) =>
handlePublicChange(e, item)
}
type="checkbox"
value=""
className="sr-only peer"
/>
<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>
)}
{isMakePublic[item.objectId] && (
<ModalUi
isOpen
title={
isPublic[item.objectId]
? "Make template public"
: "Make template private"
}
handleClose={() => {
setIsMakePublic({});
setSelectedPublicRole("");
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}}
>
<div className="m-[20px]">
<div className="font-normal text-black">
<p className="text-lg">
{isPublic[item.objectId]
? `Are you sure you want to make this template public ?`
: `Are you sure you want to make this template private ? This will remove it from your public profile ?`}
</p>
{isPublic[item.objectId] && (
<div className="flex mt-2 gap-2 md:items-center">
<p className="text-[15px]">
Public role :{" "}
<div className="m-[20px]">
{publicUserName ? (
<div className="font-normal text-black">
<p>
Heres your public URL. Copy or share
it with the signer, and you will be
able to see all your publicly set
templates.
</p>
{item?.Placeholders?.length > 1 ? (
<select
className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]"
name="textvalidate"
value={selectedPublicRole}
onChange={(e) =>
setSelectedPublicRole(
e.target.value
)
}
>
<option
disabled
style={{ fontSize: "13px" }}
value=""
>
Select...
</option>
{item?.Placeholders.map(
(data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data?.Role}
>
{data?.Role}
</option>
);
}
)}
</select>
) : (
<div className="w-[60%] md:w-[70%] border-[1px] border-gray-200 rounded-sm p-[2px]">
{item?.Placeholders[0]?.Role}
</div>
)}
<div className=" flex items-center gap-5 mt-2 p-[2px] w-[75%]">
<span className="font-bold">
Public URL :{" "}
</span>
<span
onClick={() => copytoProfileLink()}
className="underline underline-offset-2 cursor-pointer"
>{`https://opensign-me.vercel.app/${publicUserName}`}</span>
</div>
</div>
) : (
<div className="font-normal text-black">
<p>
Please add your public URL, and you
will be able to make a public
template.
</p>
<button
className="mt-3 op-btn op-btn-primary"
onClick={() => navigate("/profile")}
>
Add
</button>
</div>
)}
</div>
<hr className="bg-[#ccc] mt-2 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handlePublicTemplate(item)}
className="op-btn op-btn-primary"
>
Submit
</button>
<button
onClick={() => handleClose(item)}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
)}
{isPublicProfile[item.objectId] && (
<ModalUi
isOpen
title={"Public URL"}
handleClose={() => {
setIsPublicProfile({});
}}
reduceWidth={
"md:min-w-[440px] md:max-w-[400px]"
}
>
<div className="m-[20px]">
{publicUserName ? (
<div className="font-normal text-black">
<p>
Heres your public URL. Copy or share it
with the signer, and you will be able to
see all your publicly set templates.
</p>
<div className=" flex items-center gap-5 mt-2 p-[2px] w-[75%]">
<span className="font-bold">
Public URL :{" "}
</span>
<span
onClick={() => copytoProfileLink()}
className="underline underline-offset-2 cursor-pointer"
>{`https://opensign.me/${publicUserName}`}</span>
</div>
</div>
) : (
<div className="font-normal text-black">
<p>
Please add your public URL, and you will
be able to make a public template.
</p>
<button
className="mt-3 op-btn op-btn-primary"
onClick={() => navigate("/profile")}
>
Add
</button>
</div>
)}
</div>
</ModalUi>
)}
</td>
)} */}
</ModalUi>
)}
</td>
)}
<td className="px-2 py-2">
<div className="text-base-content flex flex-row gap-x-2 gap-y-1 justify-start items-center">
{props.actions?.length > 0 &&
@@ -0,0 +1,48 @@
import React, { useState } from "react";
import Parse from "parse";
import ReactDOM from "react-dom/client";
import PdfRequestFiles from "../pages/PdfRequestFiles";
import { pdfjs } from "react-pdf";
import { Provider } from "react-redux";
import { store } from "../redux/store";
import "../index.css";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.mjs`;
// Wrapper component to manage props
const PublicScriptFileWrapper = ({ initialProps }) => {
const [props, setProps] = useState(initialProps);
// Make the setProps function globally accessible
window.updatePublicTemplateProps = setProps;
return <PdfRequestFiles {...props} />;
};
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
localStorage.setItem("baseUrl", `${serverUrl}/`);
localStorage.setItem("parseAppId", appId);
Parse.initialize(appId);
Parse.serverURL = serverUrl;
// Create a new div element and append it to the body
const scriptComponent = document.createElement("div");
scriptComponent.id = "script-component";
document.body.appendChild(scriptComponent);
const link = document.createElement("link");
link.href = `${process.env.PUBLIC_URL}/css/fonts.css`;
link.rel = "stylesheet";
document.head.appendChild(link);
// Create a root and render the wrapper component with initial props
const root = ReactDOM.createRoot(document.getElementById("script-component"));
root.render(
<div>
<Provider store={store}>
<PublicScriptFileWrapper initialProps={{ text: "templateId" }} />
</Provider>
</div>
);
+2 -2
View File
@@ -60,8 +60,8 @@ module.exports = {
"success-content": "#f5f5f4",
warning: "#ffbe00",
"warning-content": "#ccd9e8",
error: "#ff0000",
"error-content": "#f3f4f6",
error: "#ffa1a7",
"error-content": "#16090a",
"--rounded-btn": "1.9rem",
"--tab-border": "2px",
"--tab-radius": "0.7rem"
+71
View File
@@ -0,0 +1,71 @@
const path = require("path");
const Dotenv = require("dotenv-webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //used to clean directory
const TerserPlugin = require("terser-webpack-plugin"); //used to minify and optimize JavaScript files
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //used to extract css file into separate files
const isProduction = process.env.NODE_ENV;
module.exports = {
mode: isProduction ? "production" : "development",
entry: "./src/script/PublicTemplate.js",
output: {
path: path.resolve(__dirname, "public", "static", "js"),
filename: "public-template.bundle.js",
libraryTarget: "umd",
umdNamedDefine: true,
library: "PublicTemplate",
globalObject: "this"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"]
}
}
},
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader"
]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: "file-loader",
options: {
name: "assets/images/[name].[hash].[ext]"
}
}
]
}
]
},
resolve: {
extensions: [".js", ".jsx"]
},
plugins: [
new Dotenv(),
new CleanWebpackPlugin(),
...(isProduction
? [
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css"
})
]
: [])
],
optimization: {
minimize: isProduction,
minimizer: [new TerserPlugin()]
},
devtool: isProduction ? "source-map" : "inline-source-map"
};
+2
View File
@@ -48,6 +48,7 @@ import getUserByOrg from './parsefunction/getUserByOrg.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import TeamsAftersave from './parsefunction/TeamsAftersave.js';
import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.js';
import PublicUserLinkContactToDoc from './parsefunction/PublicUserLinkContactToDoc.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -105,3 +106,4 @@ Parse.Cloud.define('ssosign', ssoSignin);
Parse.Cloud.define('isextenduser', isextenduser);
Parse.Cloud.define('getuserbyorg', getUserByOrg);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('publicuserlinkcontacttodoc', PublicUserLinkContactToDoc);
@@ -21,7 +21,23 @@ async function AuthLoginAsMail(request) {
if (resOtp === otp) {
var result = await getToken(request);
return result;
if (result && !result?.emailVerified) {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(result?.objectId, {
sessionToken: result.sessionToken,
});
// Update the emailVerified field to true
user.set('emailVerified', true);
// Save the user object
const res = await user.save(null, { useMasterKey: true });
if (res) {
return result;
} else {
reject('user not found!');
}
} else {
return result;
}
async function getToken(request) {
return new Promise(function (resolve, reject) {
@@ -56,6 +72,7 @@ async function AuthLoginAsMail(request) {
.catch(err => {
reject('user not found!');
});
// user couldn't find lets sign up!
})
.catch(() => {
@@ -4,8 +4,12 @@ async function GetPublicTemplate(request) {
if (username) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserName', username);
const res = await extUserQuery.first({ useMasterKey: true });
const userId = res.get('UserId').id;
const userRes = await extUserQuery.first({ useMasterKey: true });
const userId = userRes.get('UserId').id;
//get _user details
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(userId, { useMasterKey: true });
if (userId) {
const templatQuery = new Parse.Query('contracts_Template');
templatQuery.equalTo('CreatedBy', {
@@ -13,11 +17,20 @@ async function GetPublicTemplate(request) {
className: '_User',
objectId: userId,
});
templatQuery.descending('updatedAt');
templatQuery.equalTo('IsPublic', true);
const getTemplate = await templatQuery.find({ useMasterKey: true });
return getTemplate;
const extend_Res = await Parse.Cloud.run('getUserDetails', {
email: user.get('email'),
});
if (extend_Res) {
return { template: getTemplate, user: user, extend_User: extend_Res };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User does not exist');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
@@ -3,55 +3,74 @@ import axios from 'axios';
export default async function GetTemplate(request) {
const serverUrl = process.env.SERVER_URL;
const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
// console.log("templateId ", templateId)
// console.log("userId ",userId)
if (templateId && userId) {
if (!ispublic) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (templateId && userId) {
try {
let template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userRes.data.email);
extUserQuery.include('TeamIds');
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) {
let teamsArr = [];
_extUser?.TeamIds?.forEach(x => (teamsArr = [...teamsArr, ...x.Ancestors]));
// Create the first query
const sharedWithQuery = new Parse.Query('contracts_Template');
sharedWithQuery.containedIn('SharedWith', teamsArr);
// Create the second query
const createdByQuery = new Parse.Query('contracts_Template');
createdByQuery.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
template = Parse.Query.or(sharedWithQuery, createdByQuery);
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
}
}
const res = await template.first({ useMasterKey: true });
if (res) {
// console.log("res ",res)
return res;
} else {
return { error: "You don't have access of this document!" };
}
} catch (err) {
console.log('err', err);
return err;
}
}
} else if (templateId && ispublic) {
try {
let template = new Parse.Query('contracts_Template');
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userRes.data.email);
extUserQuery.include('TeamIds');
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) {
let teamsArr = [];
_extUser?.TeamIds?.forEach(x => (teamsArr = [...teamsArr, ...x.Ancestors]));
// Create the first query
const sharedWithQuery = new Parse.Query('contracts_Template');
sharedWithQuery.containedIn('SharedWith', teamsArr);
// Create the second query
const createdByQuery = new Parse.Query('contracts_Template');
createdByQuery.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
template = Parse.Query.or(sharedWithQuery, createdByQuery);
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
}
}
const res = await template.first({ useMasterKey: true });
// console.log("res ", res)
if (res) {
// console.log("res ",res)
return res;
} else {
return { error: "You don't have access of this document!" };
@@ -0,0 +1,218 @@
// `saveRoleContact` is used to save user in contracts_Guest role and create contact
const saveRoleContact = async contact => {
try {
const Role = new Parse.Query(Parse.Role);
const guestRole = await Role.equalTo('name', 'contracts_Guest').first();
if (guestRole) {
// Check if the user is already in the role
const relation = guestRole.relation('users');
const usersInRoleQuery = relation.query();
usersInRoleQuery.equalTo('objectId', contact.UserId.objectId);
const usersInRole = await usersInRoleQuery.find();
if (usersInRole.length > 0) {
console.log('User already added to Guest role.');
} else {
relation.add({ __type: 'Pointer', className: '_User', id: contact.UserId.objectId });
await guestRole.save(null, { useMasterKey: true });
// console.log('User added to Guest role successfully.');
}
}
} catch (err) {
console.log('err in role save', err);
}
const contactQuery = new Parse.Object('contracts_Contactbook');
contactQuery.set('Name', contact.Name);
contactQuery.set('Email', contact.Email);
if (contact?.Phone) {
contactQuery.set('Phone', contact.Phone);
}
contactQuery.set('CreatedBy', contact.CreatedBy);
contactQuery.set('UserId', contact.UserId);
contactQuery.set('UserRole', 'contracts_Guest');
contactQuery.set('TenantId', contact.TenantId);
contactQuery.set('IsDeleted', false);
const acl = new Parse.ACL();
acl.setReadAccess(contact.CreatedBy.objectId, true);
acl.setWriteAccess(contact.CreatedBy.objectId, true);
acl.setReadAccess(contact.UserId.objectId, true);
acl.setWriteAccess(contact.UserId.objectId, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
if (contactRes) {
return contactRes;
}
};
// `createDocumentFromTemplate` is used to create document from template
const createDocumentFromTemplate = async (template, existContact, index) => {
try {
if (template) {
const Doc = JSON.parse(JSON.stringify(template));
//update contact in placeholder, signers and update ACl in provide document
const object = new Parse.Object('contracts_Document');
object.set('Name', Doc?.Name);
object.set('Description', Doc?.Description);
object.set('Note', Doc?.Note);
object.set('TimeToCompleteDays', Doc.TimeToCompleteDays || 15);
object.set('SendinOrder', Doc?.SendinOrder);
object.set('AutomaticReminders', Doc.AutomaticReminders);
object.set('RemindOnceInEvery', Doc?.RemindOnceInEvery);
object.set('URL', Doc?.URL);
object.set('CreatedBy', Doc?.CreatedBy);
object.set('ExtUserPtr', Doc?.ExtUserPtr);
let signers = Doc?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
};
signers = [...signers.slice(0, index), signerobj, ...signers.slice(index)];
object.set('Signers', signers);
object.set('SignedUrl', Doc.URL || Doc.SignedUrl);
const Placeholders = Doc?.Placeholders || [];
Placeholders[index] = {
...Placeholders[index],
signerObjId: existContact.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
},
};
object.set('Placeholders', Placeholders);
const resDoc = await object.save(null, { useMasterKey: true });
return resDoc;
}
} catch (err) {
console.log('err in create document from template', err);
}
};
// `PublicUserLinkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// create new document from template and save contact pointer in placeholder, signers and ACL of Document
export default async function PublicUserLinkContactToDoc(req) {
const email = req.params.email;
const templateid = req.params.templateid;
const name = req.params.name;
const phone = req.params.phone;
const role = req.params.role;
try {
if (templateid) {
// Execute the query to get the template with the specified 'templateid'
const docQuery = new Parse.Query('contracts_Template');
docQuery.include('ExtUserPtr');
const tempRes = await docQuery.get(templateid, { useMasterKey: true });
// Check if the template was found; if not, throw an error indicating the template was not found
if (!tempRes) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
}
const _tempRes = JSON.parse(JSON.stringify(tempRes));
const Placeholders = _tempRes?.Placeholders || [];
let index;
if (role) {
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
}
if (index !== -1) {
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
const contactCls = new Parse.Query('contracts_Contactbook');
contactCls.equalTo('Email', email);
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, existContact, index);
if (docRes) {
return { contactId: existContact.id, docId: docRes.id };
}
} else {
// Execute the query to check if a user already exists in the 'contracts_Users' class
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', email);
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const contact = {
UserId: _extUser.UserId,
Name: _extUser.Name,
Email: email,
Phone: _extUser?.Phone ? _extUser.Phone : '',
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
if (docRes) {
return { contactId: contactRes.id, docId: docRes.id };
}
} else if (name) {
try {
// Execute the query to check if a user already exists in the '_User' class
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
if (docRes) {
return { contactId: contactRes.id, docId: docRes.id };
}
} else {
// create new user in _User class on the basis of details provide by user
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const newUserRes = await _user.save();
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr.TenantId,
};
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(tempRes, contactRes, index);
if (docRes) {
return { contactId: contactRes.id, docId: docRes.id };
}
}
} catch (err) {
console.log('Err', err);
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
}
} catch (err) {
console.log('err in publicuserlinkcontacttodoc', err);
throw err;
}
}
@@ -0,0 +1,35 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
// TODO: set className here
const className = 'contracts_Users';
const schema = new Parse.Schema(className);
schema.addString('UserName');
schema.addString('Tagline');
schema.addBoolean('SearchIndex');
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'contracts_Users';
const schema = new Parse.Schema(className);
schema.deleteField('UserName');
schema.deleteField('Tagline');
schema.deleteField('SearchIndex');
// TODO: Set the schema here
// Example:
// schema.deleteField('name').deleteField('cash');
return schema.update();
};