Merge branch 'staging' into tour_message

This commit is contained in:
prafull-opensignlabs
2024-04-16 12:02:42 +05:30
committed by GitHub
17 changed files with 773 additions and 512 deletions
+35 -16
View File
@@ -13,25 +13,44 @@ export const openInNewTab = (url) => {
window.open(url, "_blank", "noopener,noreferrer");
};
export async function fetchSubscription() {
try {
const extClass = localStorage.getItem("Extand_Class");
const jsonSender = JSON.parse(extClass);
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsubscriptions`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = { extUserId: jsonSender[0].objectId };
const tenatRes = await axios.post(url, params, { headers: headers });
const plan = tenatRes.data?.result?.result?.PlanName;
const billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
return { plan, billingDate };
} catch (err) {
console.log("Err in fetch subscription", err);
return { plan: "", billingDate: "" };
}
}
//function to get subcripition details from Extand user class
export async function checkIsSubscribed() {
const extClass = localStorage.getItem("Extand_Class");
const jsonSender = JSON.parse(extClass);
const user = await Parse.Cloud.run("getUserDetails", {
email: jsonSender[0].Email
});
const freeplan = user?.get("Plan") && user?.get("Plan")?.plan_code;
const billingDate =
user?.get("Next_billing_date") && user?.get("Next_billing_date");
if (freeplan === "freeplan") {
return false;
} else if (billingDate) {
if (billingDate > new Date()) {
return true;
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
} else if (res.billingDate) {
if (new Date(res.billingDate) > new Date()) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
}
}
@@ -1229,8 +1248,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) {
+125 -27
View File
@@ -12,11 +12,22 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "sign",
btnId: "1231",
btnLabel: "Edit",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "draftDocument"
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2142",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
@@ -29,11 +40,13 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "sign",
btnId: "4536",
btnLabel: "Sign",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
],
helpMsg:
@@ -46,11 +59,31 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "8901",
btnLabel: "Share",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1588",
btnLabel: "View",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "1488",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
@@ -63,11 +96,22 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "View",
btnId: "1378",
btnLabel: "Edit",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1278",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
@@ -80,11 +124,22 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1458",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1358",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
@@ -97,11 +152,22 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "1898",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1998",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
@@ -114,11 +180,22 @@ export default function reportJson(id) {
heading: dashboardReportHead,
actions: [
{
btnId: "1999",
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2000",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
};
@@ -129,11 +206,13 @@ export default function reportJson(id) {
heading: dashboardReportHead,
actions: [
{
btnId: "2001",
btnLabel: "Sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
]
};
@@ -144,11 +223,22 @@ export default function reportJson(id) {
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
actions: [
{
btnLabel: "sign",
btnId: "2003",
btnLabel: "Edit",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "draftDocument"
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2004",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
};
@@ -159,10 +249,12 @@ export default function reportJson(id) {
heading: contactbook,
actions: [
{
btnLabel: "",
btnId: "2204",
btnLabel: "Delete",
btnColor: "#f55a42",
textColor: "white",
btnIcon: "fa-solid fa-trash"
btnIcon: "fa-solid fa-trash",
action: "delete"
}
],
form: "ContactBook",
@@ -176,27 +268,33 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "Use",
btnId: "2234",
btnLabel: "Create document",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "placeHolderSign",
selector: "reactourSecond",
message:
"Click the Use button to create a new document from an existing template."
action: "redirect"
},
{
btnLabel: "Edit",
btnId: "2434",
btnColor: "#00c9d5",
textColor: "white",
btnIcon: "fa fa-plus",
btnIcon: "fa-solid fa-pen",
redirectUrl: "template",
selector: "reactourThird",
message:
"Use the Edit button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents."
action: "redirect"
},
{
btnId: "1834",
btnLabel: "Delete",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
],
helpMsg:
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
};
+6 -11
View File
@@ -11,6 +11,7 @@ import ModalUi from "../primitives/ModalUi";
import { useNavigate, useLocation, Outlet } from "react-router-dom";
import { isEnableSubscription } from "../constant/const";
import { useCookies } from "react-cookie";
import { fetchSubscription } from "../constant/Utils";
const HomeLayout = () => {
const navigate = useNavigate();
@@ -36,7 +37,7 @@ const HomeLayout = () => {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get("ProfilePic"));
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
checkIsSubscribed();
} else {
setIsUserValid(false);
@@ -69,19 +70,13 @@ const HomeLayout = () => {
};
async function checkIsSubscribed() {
const currentUser = Parse.User.current();
const user = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (isEnableSubscription) {
const freeplan = user?.get("Plan") && user?.get("Plan").plan_code;
const billingDate =
user?.get("Next_billing_date") && user?.get("Next_billing_date");
if (freeplan === "freeplan") {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
setIsUserValid(true);
setIsLoader(false);
} else if (billingDate) {
if (billingDate > new Date()) {
} else if (res.billingDate) {
if (new Date(res.billingDate) > new Date()) {
setIsUserValid(true);
setIsLoader(false);
} else {
+1 -1
View File
@@ -300,7 +300,7 @@ const Forms = (props) => {
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {getFileName(fileload)}
file selected : {getFileName(fileupload)}
</div>
<div
onClick={() => setFileUpload([])}
+13 -18
View File
@@ -19,7 +19,7 @@ import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { getAppLogo } from "../constant/Utils";
import { fetchSubscription, getAppLogo } from "../constant/Utils";
function Login() {
const navigate = useNavigate();
const location = useLocation();
@@ -169,7 +169,7 @@ function Login() {
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
}).then(
(result) => {
async (result) => {
let tenentInfo = [];
const results = [result];
if (results) {
@@ -270,16 +270,15 @@ function Login() {
"userDetails",
JSON.stringify(LocalUserDetails)
);
const freeplan =
results[0].get("Plan") &&
results[0].get("Plan").plan_code;
const billingDate =
results[0].get("Next_billing_date") &&
results[0].get("Next_billing_date");
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (billingDate > new Date()) {
if (
new Date(billingDate) > new Date()
) {
localStorage.removeItem(
"userDetails"
);
@@ -814,7 +813,7 @@ function Login() {
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
}).then(
(result) => {
async (result) => {
let tenentInfo = [];
const results = [result];
if (results) {
@@ -866,17 +865,13 @@ function Login() {
"userDetails",
JSON.stringify(LocalUserDetails)
);
const billingDate =
results[0].get("Next_billing_date") &&
results[0].get("Next_billing_date");
const freeplan =
results[0]?.get("Plan") &&
results[0]?.get("Plan").plan_code;
const res = await fetchSubscription();
const billingDate = res.billingDate;
const freeplan = res.plan;
if (freeplan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (billingDate > new Date()) {
if (new Date(billingDate) > new Date()) {
localStorage.removeItem("userDetails");
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
+10 -13
View File
@@ -22,7 +22,8 @@ import {
onSaveImage,
addDefaultSignatureImg,
radioButtonWidget,
replaceMailVaribles
replaceMailVaribles,
fetchSubscription
} from "../constant/Utils";
import Loader from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError";
@@ -32,7 +33,6 @@ import PdfDeclineModal from "../primitives/PdfDeclineModal";
import Title from "../components/Title";
import DefaultSignature from "../components/pdf/DefaultSignature";
import ModalUi from "../primitives/ModalUi";
import Parse from "parse";
function PdfRequestFiles() {
const { docId } = useParams();
@@ -135,17 +135,14 @@ function PdfRequestFiles() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current]);
async function checkIsSubscribed(email) {
const user = await Parse.Cloud.run("getUserDetails", {
email: email
});
const freeplan = user?.get("Plan") && user?.get("Plan").plan_code;
const billingDate =
user?.get("Next_billing_date") && user?.get("Next_billing_date");
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
return true;
} else if (billingDate) {
if (billingDate > new Date()) {
if (new Date(billingDate) > new Date()) {
return true;
} else {
if (location.pathname.includes("/load/")) {
@@ -1095,9 +1092,9 @@ function PdfRequestFiles() {
isDecline.currnt === "Sure"
? "Are you sure want to decline this document ?"
: isDecline.currnt === "YouDeclined"
? "You have declined this document!"
: isDecline.currnt === "another" &&
"You cannot sign this document as it has been declined by one or more recipient(s)."
? "You have declined this document!"
: isDecline.currnt === "another" &&
"You cannot sign this document as it has been declined by one or more recipient(s)."
}
footerMessage={isDecline.currnt === "Sure"}
declineDoc={declineDoc}
+7 -9
View File
@@ -32,7 +32,8 @@ import {
color,
getTenantDetails,
replaceMailVaribles,
copytoData
copytoData,
fetchSubscription
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom";
@@ -239,17 +240,14 @@ function PlaceHolderSign() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current]);
async function checkIsSubscribed(email) {
const user = await Parse.Cloud.run("getUserDetails", {
email: email
});
const freeplan = user?.get("Plan") && user?.get("Plan").plan_code;
const billingDate =
user?.get("Next_billing_date") && user?.get("Next_billing_date");
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
return true;
} else if (billingDate) {
if (billingDate > new Date()) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
+7 -10
View File
@@ -23,7 +23,8 @@ import {
defaultWidthHeight,
addWidgetOptions,
textInputWidget,
radioButtonWidget
radioButtonWidget,
fetchSubscription
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -35,7 +36,6 @@ import AddRoleModal from "../components/pdf/AddRoleModal";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import Parse from "parse";
const TemplatePlaceholder = () => {
const navigate = useNavigate();
const { templateId } = useParams();
@@ -170,17 +170,14 @@ const TemplatePlaceholder = () => {
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current]);
async function checkIsSubscribed(email) {
const user = await Parse.Cloud.run("getUserDetails", {
email: email
});
const freeplan = user?.get("Plan") && user?.get("Plan").plan_code;
const billingDate =
user?.get("Next_billing_date") && user?.get("Next_billing_date");
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
return true;
} else if (billingDate) {
if (billingDate > new Date()) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
+366 -366
View File
@@ -3,12 +3,12 @@ import pad from "../assets/images/pad.svg";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import ModalUi from "./ModalUi";
import Tour from "reactour";
import AddSigner from "../components/AddSigner";
import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
import Parse from "parse";
import { RWebShare } from "react-web-share";
const ReportTable = ({
ReportName,
List,
@@ -19,9 +19,7 @@ const ReportTable = ({
isMoreDocs,
docPerPage,
form,
report_help,
tourData,
isDontShow
report_help
}) => {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
@@ -31,8 +29,9 @@ const ReportTable = ({
const [isDocErr, setIsDocErr] = useState(false);
const [isContactform, setIsContactform] = useState(false);
const [isDeleteModal, setIsDeleteModal] = useState({});
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isShare, setIsShare] = useState({});
const [shareUrls, setShareUrls] = useState([]);
const [copied, setCopied] = useState(false);
const startIndex = (currentPage - 1) * docPerPage;
// For loop is used to calculate page numbers visible below table
@@ -46,7 +45,6 @@ const ReportTable = ({
}, [List, docPerPage]);
// below useEffect reset currenpage to 1 if user change route
useEffect(() => {
checkTourStatus();
return () => setCurrentPage(1);
}, []);
@@ -72,13 +70,13 @@ const ReportTable = ({
}
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
// `handlemicroapp` is used to open microapp
const handlemicroapp = async (item, url, btnLabel) => {
// `handleURL` is used to open microapp
const handleURL = async (item, act) => {
if (ReportName === "Templates") {
if (btnLabel === "Edit") {
navigate(`/${url}/${item.objectId}`);
if (act.btnLabel === "Edit") {
navigate(`/${act.redirectUrl}/${item.objectId}`);
} else {
setActLoader({ [`${item.objectId}_${btnLabel}`]: true });
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
try {
const params = {
templateId: item.objectId
@@ -153,11 +151,13 @@ const ReportTable = ({
}
}
);
// console.log("Res ", res.data);
if (res.data && res.data.objectId) {
setActLoader({});
setIsAlert(true);
setTimeout(() => setIsAlert(false), 1500);
navigate(`/${url}/${res.data.objectId}`, {
navigate(`/${act.redirectUrl}/${res.data.objectId}`, {
state: { title: "Use Template" }
});
}
@@ -188,18 +188,20 @@ const ReportTable = ({
}
} else {
localStorage.removeItem("rowlevel");
navigate(`/${url}`);
navigate(`/${act.redirectUrl}`);
localStorage.setItem("rowlevel", JSON.stringify(item));
}
// localStorage.setItem("rowlevelMicro");
};
const handlebtn = async (item) => {
if (ReportName === "Contactbook") {
const handleActionBtn = (act, item) => {
if (act.action === "redirect") {
handleURL(item, act);
} else if (act.action === "delete") {
setIsDeleteModal({ [item.objectId]: true });
} else if (act.action === "share") {
handleShare(item);
}
};
// Get current list
const indexOfLastDoc = currentPage * docPerPage;
const indexOfFirstDoc = indexOfLastDoc - docPerPage;
@@ -220,13 +222,21 @@ const ReportTable = ({
const handleDelete = async (item) => {
setIsDeleteModal({});
setActLoader({ [item.objectId]: true });
setActLoader({ [`${item.objectId}`]: true });
const clsObj = {
Contactbook: "contracts_Contactbook",
Templates: "contracts_Template"
};
try {
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const url = serverUrl + "/classes/contracts_Contactbook/";
const body = { IsDeleted: true };
const cls = clsObj[ReportName] || "contracts_Document";
const url = serverUrl + `/classes/${cls}/`;
const body =
ReportName === "Contactbook"
? { IsDeleted: true }
: { IsArchive: true };
const res = await axios.put(url + item.objectId, body, {
headers: {
"Content-Type": "application/json",
@@ -252,364 +262,354 @@ const ReportTable = ({
};
const handleCloseDeleteModal = () => setIsDeleteModal({});
async function checkTourStatus() {
const currentUser = Parse.User.current();
const cloudRes = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
const res = { data: cloudRes.toJSON() };
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
const tourStatus = res.data.TourStatus;
// console.log("res ", res.data.TourStatus);
setTourStatusArr(tourStatus);
const filteredtourStatus = tourStatus.filter(
(obj) => obj["templateReportTour"]
);
if (filteredtourStatus.length > 0) {
const templateReportTour = filteredtourStatus[0]["templateReportTour"];
const handleShare = (item) => {
setActLoader({ [item.objectId]: true });
const host = window.location.origin;
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const baseURL = serverUrl.replace(/\//g, "%2F");
const urls = item.Signers.map((x) => ({
email: x.Email,
url: `${host}/login/${item.objectId}/${x.Email}/${x.objectId}/${baseURL}%2F&opensign&contracts`
}));
setShareUrls(urls);
setIsShare({ [item.objectId]: true });
};
if (templateReportTour) {
setIsTour(false);
} else {
setIsTour(true);
}
} else {
setIsTour(true);
}
} else {
setIsTour(true);
}
}
const closeTour = async () => {
// console.log("closeTour");
setIsTour(false);
if (isDontShow) {
const serverUrl = localStorage.getItem("baseUrl");
const appId = localStorage.getItem("parseAppId");
const extUserClass = localStorage.getItem("extended_class");
const json = JSON.parse(localStorage.getItem("Extand_Class"));
const extUserId = json && json.length > 0 && json[0].objectId;
// console.log("extUserId ", extUserId)
let updatedTourStatus = [];
if (tourStatusArr.length > 0) {
updatedTourStatus = [...tourStatusArr];
const templateTourIndex = tourStatusArr.findIndex(
(obj) =>
obj["templateReportTour"] === false ||
obj["templateReportTour"] === true
);
if (templateTourIndex !== -1) {
updatedTourStatus[templateTourIndex] = { templateReportTour: true };
} else {
updatedTourStatus.push({ templateReportTour: true });
}
} else {
updatedTourStatus = [{ templateReportTour: true }];
}
await axios.put(
serverUrl + "classes/" + extUserClass + "/" + extUserId,
{
TourStatus: updatedTourStatus
},
{
headers: {
"X-Parse-Application-Id": appId
}
}
);
}
const copytoclipboard = (share) => {
navigator.clipboard.writeText(share.url);
setCopied({ ...copied, [share.email]: true });
};
return (
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
{isErr
? "Something went wrong, Please try again later!"
: "Record deleted successfully!"}
</Alert>
<div className="relative">
{Object.keys(actLoader)?.length > 0 && (
<div className="absolute w-full h-full rounded-md flex justify-center items-center bg-black bg-opacity-30 ">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
{tourData && ReportName === "Templates" && (
<Tour
onRequestClose={closeTour}
steps={tourData}
isOpen={isTour}
// rounded={5}
closeWithMask={false}
/>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
{ReportName}{" "}
{report_help && (
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={report_help} />
</span>
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
{isErr
? "Something went wrong, Please try again later!"
: "Record deleted successfully!"}
</Alert>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
{ReportName}{" "}
{report_help && (
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={report_help} />
</span>
)}
</div>
{ReportName === "Templates" && (
<i
onClick={() => navigate("/form/template")}
className="fa-solid fa-square-plus text-sky-400 text-[25px]"
></i>
)}
{form && (
<div
className="cursor-pointer"
onClick={() => handleContactFormModal()}
>
<i className="fa-solid fa-square-plus text-sky-400 text-[25px]"></i>
</div>
)}
</div>
{ReportName === "Templates" && (
<i
data-tut="reactourFirst"
onClick={() => navigate("/form/template")}
className="fa-solid fa-square-plus text-sky-400 text-[25px]"
></i>
)}
{form && (
<div
className="cursor-pointer"
onClick={() => handleContactFormModal()}
>
<i className="fa-solid fa-square-plus text-sky-400 text-[25px]"></i>
</div>
)}
</div>
<table className="table-auto w-full border-collapse">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2 font-thin">{item}</th>
</React.Fragment>
))}
{actions?.length > 0 && (
<th className="px-4 py-2 font-thin">Action</th>
)}
</tr>
</thead>
<tbody className="text-[12px]">
{List?.length > 0 ? (
<>
{currentLists.map((item, index) =>
ReportName === "Contactbook" ? (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Email || "-"}</td>
<td className="px-4 py-2">{item?.Phone || "-"}</td>
<td className="px-3 py-2 text-white">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() =>
act?.redirectUrl
? handlemicroapp(item, act.redirectUrl)
: handlebtn(item)
}
className={`mb-1 flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
style={{
backgroundColor: act.btnColor
? act.btnColor
: "#3ac9d6",
color: act?.textColor ? act?.textColor : "white"
}}
>
<span>
{act?.btnIcon && (
<i
className={
actLoader[item.objectId]
? "fa-solid fa-spinner fa-spin-pulse"
: act.btnIcon
}
></i>
)}
</span>
{act?.btnLabel && (
<span className="uppercase">
{act?.btnLabel ? act.btnLabel : "View"}
</span>
)}
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete Contact"}
handleClose={handleCloseDeleteModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this contact?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{ backgroundColor: modalSubmitBtnColor }}
>
Yes
</button>
<button
onClick={handleCloseDeleteModal}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
style={{ backgroundColor: modalCancelBtnColor }}
>
No
</button>
</div>
</div>
</ModalUi>
<table className="table-auto w-full border-collapse">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2 font-thin">{item}</th>
</React.Fragment>
))}
{actions?.length > 0 && (
<th className="px-4 py-2 font-thin">Action</th>
)}
</tr>
</thead>
<tbody className="text-[12px]">
{List?.length > 0 && (
<>
{currentLists.map((item, index) =>
ReportName === "Contactbook" ? (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
</td>
</tr>
) : (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
<td className="px-4 py-2 font-semibold w-56">
{item?.Name}{" "}
</td>
{heading.includes("Note") && (
<td className="px-4 py-2">{item?.Note || "-"}</td>
)}
{heading.includes("Folder") && (
<td className="px-4 py-2">
{item?.Folder?.Name || "OpenSign™ Drive"}
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Email || "-"}</td>
<td className="px-4 py-2">{item?.Phone || "-"}</td>
<td className="px-3 py-2 text-white grid grid-cols-2">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() => handleActionBtn(act, item)}
className={`mb-1 flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
title={act.btnLabel}
style={{
backgroundColor: act.btnColor
? act.btnColor
: "#3ac9d6",
color: act?.textColor ? act?.textColor : "white"
}}
>
<i className={act.btnIcon}></i>
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete Contact"}
handleClose={handleCloseDeleteModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this contact?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{
backgroundColor: modalSubmitBtnColor
}}
>
Yes
</button>
<button
onClick={handleCloseDeleteModal}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
style={{
backgroundColor: modalCancelBtnColor
}}
>
No
</button>
</div>
</div>
</ModalUi>
)}
</td>
</tr>
) : (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
<td className="px-4 py-2 font-semibold w-56">
{item?.Name}{" "}
</td>
{heading.includes("Note") && (
<td className="px-4 py-2">{item?.Note || "-"}</td>
)}
{heading.includes("Folder") && (
<td className="px-4 py-2">
{item?.Folder?.Name || "OpenSign™ Drive"}
</td>
)}
<td className="px-4 py-2">
<a
target="_blank"
rel="noopener noreferrer"
title={item?.URL}
href={item?.URL}
download={item?.URL}
className="text-[blue] hover:text-[blue] hover:underline"
>
{item?.URL ? "Download" : "-"}
</a>
</td>
)}
<td className="px-4 py-2">
<a
target="_blank"
rel="noopener noreferrer"
title={item?.URL}
href={item?.URL}
download={item?.URL}
className="text-[blue] hover:text-[blue] hover:underline"
>
{item?.URL ? "Download" : "-"}
</a>
</td>
<td className="px-4 py-2">{formatRow(item?.ExtUserPtr)}</td>
<td className="px-4 py-2">
{item?.Signers ? formatRow(item?.Signers) : "-"}
</td>
<td className="px-3 py-2 text-white">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
data-tut={act?.selector}
key={index}
onClick={() =>
act?.redirectUrl
? handlemicroapp(
item,
act.redirectUrl,
act.btnLabel
)
: handlebtn(item)
}
className={`mb-1 flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
style={{
backgroundColor: act.btnColor
? act.btnColor
: "#3ac9d6",
color: act?.textColor ? act?.textColor : "white"
<td className="px-4 py-2">
{formatRow(item?.ExtUserPtr)}
</td>
<td className="px-4 py-2">
{item?.Signers ? formatRow(item?.Signers) : "-"}
</td>
<td className="px-3 py-2 text-white grid grid-cols-2 gap-1">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() => handleActionBtn(act, item)}
className={`w-[25px] h-[25px] flex justify-center items-center rounded shadow`}
title={act.btnLabel}
style={{
backgroundColor: act.btnColor
? act.btnColor
: "#3ac9d6",
color: act?.textColor ? act?.textColor : "white"
}}
>
<i className={act.btnIcon}></i>
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete Document"}
handleClose={handleCloseDeleteModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this document?
</div>
<hr className="bg-[#ccc] mt-4" />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{
backgroundColor: modalSubmitBtnColor
}}
>
Yes
</button>
<button
onClick={handleCloseDeleteModal}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
style={{
backgroundColor: modalCancelBtnColor
}}
>
No
</button>
</div>
</div>
</ModalUi>
)}
{isShare[item.objectId] && (
<ModalUi
isOpen
title={"Copy link"}
handleClose={() => {
setIsShare({});
setActLoader({});
}}
>
<span>
{act?.btnIcon && (
<i
className={
actLoader[
`${item.objectId}_${act.btnLabel}`
]
? "fa-solid fa-spinner fa-spin-pulse"
: act.btnIcon
}
></i>
)}
</span>
<span className="uppercase">
{act?.btnLabel ? act.btnLabel : "view"}
</span>
</button>
))}
</td>
</tr>
)
<div className="m-[20px]">
{shareUrls.map((share, i) => (
<div
key={i}
className="text-sm font-normal text-black flex my-2 justify-between items-center"
>
<span className="text-sm font-semibold">
{share.email}
</span>
<div>
<RWebShare
data={{
url: share.url,
title: "Sign url"
}}
>
<button className="bg-[#002864] text-white rounded w-[32px] h-[30px] focus:outline-none">
<i className="fa-solid fa-share-from-square"></i>{" "}
</button>
</RWebShare>
<button
className="ml-2 bg-[#002864] text-white rounded w-[100px] h-[30px] focus:outline-none"
onClick={() => copytoclipboard(share)}
>
<i className="fa-solid fa-link"></i>{" "}
{copied[share.email] ? "Copied" : "Copy"}
</button>
</div>
</div>
))}
</div>
</ModalUi>
)}
</td>
</tr>
)
)}
</>
)}
</tbody>
</table>
<div className="flex flex-wrap items-center gap-2 p-2 ">
{List.length > docPerPage && (
<>
{currentPage > 1 && (
<button
onClick={() => paginateBack()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Prev
</button>
)}
</>
) : (
<></>
)}
</tbody>
</table>
<div className="flex flex-wrap items-center gap-2 p-2 ">
{List.length > docPerPage && (
<>
{currentPage > 1 && (
<button
onClick={() => paginateBack()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Prev
</button>
)}
</>
)}
{pageNumbers.map((x) => (
<button
key={x}
onClick={() => setCurrentPage(x)}
className=" bg-sky-400 text-white rounded px-3 py-2 focus:outline-none"
>
{x}
</button>
))}
{isMoreDocs && (
<button className="text-black rounded px-1 py-2">...</button>
)}
{List.length > docPerPage && (
<>
{pageNumbers.includes(currentPage + 1) && (
<button
onClick={() => paginateFront()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Next
</button>
)}
</>
)}
</div>
{List?.length <= 0 && (
<div className="flex flex-col items-center justify-center w-full bg-white rounded py-4">
<div className="w-[60px] h-[60px] overflow-hidden">
<img className="w-full h-full object-contain" src={pad} alt="img" />
{pageNumbers.map((x) => (
<button
key={x}
onClick={() => setCurrentPage(x)}
className=" bg-sky-400 text-white rounded px-3 py-2 focus:outline-none"
>
{x}
</button>
))}
{isMoreDocs && (
<button className="text-black rounded px-1 py-2">...</button>
)}
{List.length > docPerPage && (
<>
{pageNumbers.includes(currentPage + 1) && (
<button
onClick={() => paginateFront()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Next
</button>
)}
</>
)}
</div>
{List?.length <= 0 && (
<div className="flex flex-col items-center justify-center w-full bg-white rounded py-4">
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">No Data Available</div>
</div>
<div className="text-sm font-semibold">No Data Available</div>
</div>
)}
<ModalUi
title={"Add Contact"}
isOpen={isContactform}
handleClose={handleContactFormModal}
>
<AddSigner
handleUserData={handleUserData}
closePopup={handleContactFormModal}
/>
</ModalUi>
<ModalUi
headColor={"#dc3545"}
isOpen={isDocErr}
title={"Receipent required"}
handleClose={() => setIsDocErr(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add receipent in template!</p>
</div>
</ModalUi>
)}
<ModalUi
title={"Add Contact"}
isOpen={isContactform}
handleClose={handleContactFormModal}
>
<AddSigner
handleUserData={handleUserData}
closePopup={handleContactFormModal}
/>
</ModalUi>
<ModalUi
headColor={"#dc3545"}
isOpen={isDocErr}
title={"Receipent required"}
handleClose={() => setIsDocErr(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add receipent in template!</p>
</div>
</ModalUi>
</div>
</div>
);
};
+93
View File
@@ -150,3 +150,96 @@ export const updateMailCount = async extUserId => {
console.log('Error updating EmailCount in contracts_users: ' + error.message);
}
};
export function formatWidgetOptions(type, options) {
const status = options?.required === true ? 'required' : 'optional' || 'required';
const defaultValue = options?.default || '';
const values = options?.values || [];
switch (type) {
case 'signature':
return { name: 'signature', status: 'required' };
case 'stamp':
return { status: status, name: 'stamp' };
case 'initials':
return { status: status, name: options.name || 'initials' };
case 'image':
return { status: status, name: options.name || 'image' };
case 'email':
return { status: status, name: options.name || 'email', validation: { type: 'email' } };
case 'name':
return { status: status, name: options.name || 'name' };
case 'job title':
return { status: status, name: options.name || 'job title' };
case 'company':
return { status: status, name: options.name || 'company' };
case 'date': {
let today = new Date();
let dd = String(today.getDate()).padStart(2, '0');
let mm = String(today.getMonth() + 1).padStart(2, '0'); // January is 0!
let yyyy = today.getFullYear();
today = dd + '-' + mm + '-' + yyyy;
let dateFormat = options?.format;
dateFormat = dateFormat.replace(/m/g, 'M');
return {
status: status,
name: options.name || 'date',
response: defaultValue || today,
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
};
}
case 'textbox':
return {
status: status,
name: 'textbox',
defaultValue: defaultValue,
hint: options.hint,
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
};
case 'checkbox': {
const arr = options?.values;
let selectedvalues = [];
for (const obj of options.selectedvalues) {
const index = arr.indexOf(obj);
selectedvalues.push(index);
}
return {
status: status,
name: options.name || 'checkbox',
values: values,
isReadOnly: options?.readonly || false,
isHideLabel: options?.hidelabel || false,
validation: {
minRequiredCount: options?.validation?.minselections || 0,
maxRequiredCount: options?.validation?.maxselections || 0,
},
defaultValue: selectedvalues || [],
};
}
case 'radio button': {
return {
status: status,
name: options.name || 'radio',
values: values,
isReadOnly: options?.readonly || false,
isHideLabel: options?.hidelabel || false,
defaultValue: defaultValue,
};
}
case 'dropdown':
return {
status: status,
name: options.name || 'dropdown',
values: values,
defaultValue: defaultValue,
};
default:
break;
}
}
export function sanitizeFileName(fileName) {
// Remove spaces and invalid characters
const file = fileName.replace(/[^a-zA-Z0-9._-]/g, '');
const removedot = file.replace(/\.(?=.*\.)/g, '');
return removedot.replace(/[^a-zA-Z0-9._-]/g, '');
}
@@ -31,6 +31,13 @@ export default async function saveInvoice(request, response) {
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')?.id) {
createInvoice.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
await createInvoice.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create invoice!' });
}
@@ -30,6 +30,13 @@ export default async function savePayments(request, response) {
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')?.id) {
createPayment.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
await createPayment.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create payments!' });
}
@@ -2,19 +2,26 @@ export default async function saveSubscription(request, response) {
const SubscriptionId = request.body.data.subscription.subscription_id;
const body = request.body;
const Email = request.body.data.subscription.customer.email;
const Next_billing_date = request.body.data.subscription.next_billing_at;
const planName = request.body.data.subscription.plan.name;
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', Email);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const subcriptionCls = new Parse.Query('contracts_Subscriptions');
subcriptionCls.equalTo('SubscriptionId', SubscriptionId);
subcriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
const subscription = await subcriptionCls.first({ useMasterKey: true });
if (subscription) {
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subscription.id;
updateSubscription.set('SubscriptionDetails', body);
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
updateSubscription.set('PlanName', planName);
await updateSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'update subscription!' });
} else {
@@ -31,6 +38,13 @@ export default async function saveSubscription(request, response) {
className: '_User',
objectId: extUser.get('UserId').id,
});
createSubscription.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
createSubscription.set('PlanName', planName);
await createSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create subscription!' });
}
@@ -1,5 +1,12 @@
import axios from 'axios';
import { color, customAPIurl, replaceMailVaribles, saveFileUsage } from '../../../../Utils.js';
import {
color,
customAPIurl,
replaceMailVaribles,
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
} from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
@@ -92,9 +99,8 @@ export default async function createDocumentwithCoordinate(request, response) {
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
const filename = sanitizeFileName(`${name}.pdf`);
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64File, 'base64');
@@ -177,17 +183,17 @@ export default async function createDocumentwithCoordinate(request, response) {
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const options = formatWidgetOptions(widget.type, widget.options);
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const widgetData = {
isStamp: widget.type === 'stamp'|| widget.type === 'image',
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type,
options: widget.type === 'signature' ? signOpt : widget.options,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
@@ -1,5 +1,11 @@
import axios from 'axios';
import { color, customAPIurl, saveFileUsage } from '../../../../Utils.js';
import {
color,
customAPIurl,
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
} from '../../../../Utils.js';
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createTemplatewithCoordinate(request, response) {
@@ -43,9 +49,8 @@ export default async function createTemplatewithCoordinate(request, response) {
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
const filename = sanitizeFileName(`${name}.pdf`);
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64File, 'base64');
@@ -134,16 +139,16 @@ export default async function createTemplatewithCoordinate(request, response) {
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const options = formatWidgetOptions(widget.type, widget.options);
const widgetData = {
isStamp: widget.type === 'stamp'|| widget.type === 'image',
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type,
options: widget.type === 'signature' ? signOpt : widget.options,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
@@ -6,27 +6,51 @@ export default async function SubscribeFree(request) {
extQuery.equalTo('UserId', userPtr);
const extUser = await extQuery.first({ useMasterKey: true });
if (extUser) {
if (extUser?.get('Plan')?.plan_code === 'freeplan') {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions?.get('PlanName') === 'freeplan') {
return { status: 'success', result: 'already subscribed!' };
} else if (extUser?.get('Next_billing_date') < new Date()) {
} else if (subcripitions?.get('Next_billing_date') < new Date()) {
try {
const extUpdate = new Parse.Object('contracts_Users');
extUpdate.id = extUser.id;
extUpdate.set('Plan', { plan_code: 'freeplan' });
await extUpdate.save(null, { useMasterKey: true });
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subcripitions.id;
updateSubscription.set('PlanName', 'freeplan');
await updateSubscription.save(null, { useMasterKey: true });
return { status: 'success', result: 'subscribed!' };
} catch (err) {
console.log('err ', err);
return { status: 'error', result: err.message };
}
} else if (extUser?.get('Next_billing_date') > new Date()) {
} else if (subcripitions?.get('Next_billing_date') > new Date()) {
return { status: 'success', result: 'already subscribed!' };
} else {
try {
const extUpdate = new Parse.Object('contracts_Users');
extUpdate.id = extUser.id;
extUpdate.set('Plan', { plan_code: 'freeplan' });
await extUpdate.save(null, { useMasterKey: true });
const createSubscription = new Parse.Object('contracts_Subscriptions');
createSubscription.set('PlanName', 'freeplan');
createSubscription.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createSubscription.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')) {
createSubscription.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
await createSubscription.save(null, { useMasterKey: true });
return { status: 'success', result: 'subscribed!' };
} catch (err) {
console.log('err ', err);
@@ -12,19 +12,25 @@ export default async function getSubscription(request) {
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUserId,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
return { status: 'success', result: _subcripitions };
const subcriptionCls = new Parse.Query('contracts_Users');
const exUser = await subcriptionCls.get(extUserId, { useMasterKey: true });
if (exUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: exUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
return { status: 'success', result: _subcripitions };
} else {
return { status: 'success', result: {} };
}
} else {
return { status: 'success', result: {} };
return { status: 'error', result: 'User not found!' };
}
} else {
return { status: 'error', result: 'Invalid session token!' };