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