Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-12

This commit is contained in:
RaktimaNXG
2024-08-14 16:41:30 +05:30
35 changed files with 1654 additions and 41855 deletions
@@ -586,23 +586,22 @@
"Recent signature requests": "This is a list of documents that are waiting for your signature.",
"Recently sent for signatures":"This is a list of documents you've sent to other parties for signature.",
"Drafts":"This are documents you have started but have not finalized for sending."
},
"enter-email-plaholder":"Add an email address and hit enter",
"success-email-alert":"Email sent successfully!",
"expired-doc-title":"Expired Document",
"expired-on-mssg" :"This document expired on {{expiredDate}} and is no longer available to sign.",
"signature-validate-alert":"Please confirm that you have selected at least {{minRequiredCount}} checkboxes.",
"signature-validate-alert-2" :"Ensure this field is accurately filled and meets all requirements.",
"user-name-exist":"user name already exist"
},
"enter-email-plaholder":"Add an email address and hit enter",
"success-email-alert":"Email sent successfully!",
"expired-doc-title":"Expired Document",
"expired-on-mssg" :"This document expired on {{expiredDate}} and is no longer available to sign.",
"signature-validate-alert":"Please confirm that you have selected at least {{minRequiredCount}} checkboxes.",
"signature-validate-alert-2" :"Ensure this field is accurately filled and meets all requirements.",
"remaing-users":"Remaining users",
"Role":"Role",
"additional-users":"Please purchase add-on users.",
"Quantity-of-users":"Quantity of users",
"Price":"Price",
"Total-price-for-next-time":"Total price for next time",
"Proceed":"Proceed",
"Select":"Select",
"Add-seats":"Add Seats",
"user-name-exist":"user name already exist"
}
@@ -42,7 +42,7 @@
"subscribe-card-plan":"Signez 100 documents à l'aide de l'API. Seulement 0,15 $/doc par la suite.",
"user-name-limit-char" :"Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
"tour-content":"Ne plus afficher",
"team":QUIPE",
"team":quipe",
"docs":"Documents",
"session-expired" :"Votre session a expiré.",
"user":"Utilisateur",
@@ -585,19 +585,24 @@
"Recent signature requests": "Voici une liste de documents qui attendent votre signature.",
"Recently sent for signatures":"Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
"Drafts":"Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi."
},
"enter-email-plaholder":"Ajoutez une adresse email et appuyez sur Entrée",
"success-email-alert" :"E-mail envoyé avec succès!",
"expired-doc-title" :"Document expiré",
"expired-on-mssg" :"Ce document a expiré le {{expiredDate}} et n'est plus disponible pour signature.",
"signature-validate-alert":"Veuillez confirmer que vous avez coché au moins {{minRequiredCount}} cases.",
},
"enter-email-plaholder":"Ajoutez une adresse email et appuyez sur Entrée",
"success-email-alert" :"E-mail envoyé avec succès!",
"expired-doc-title" :"Document expiré",
"expired-on-mssg" :"Ce document a expiré le {{expiredDate}} et n'est plus disponible pour signature.",
"signature-validate-alert":"Veuillez confirmer que vous avez coché au moins {{minRequiredCount}} cases.",
"signature-validate-alert-2" :"Assurez-vous que ce champ est rempli avec précision et répond à toutes les exigences.",
"user-name-exist" :"le nom d'utilisateur existe déjà"
"user-name-exist" :"le nom d'utilisateur existe déjà",
"remaing-users":"Utilisateurs restants",
"Role":"Rôle",
"additional-users":"Veuillez acheter des utilisateurs supplémentaires.",
"Quantity-of-users":"Quantité d'utilisateurs",
"Price":"Prix",
"Total-price-for-next-time":"Prix total pour la prochaine fois",
"Proceed":"Procéder",
"Select":"sélectionner",
"Add-seats":"Ajouter des sièges"
}
File diff suppressed because one or more lines are too long
+331 -181
View File
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { copytoData } from "../constant/Utils";
import { copytoData, fetchSubscriptionInfo } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import ModalUi from "../primitives/ModalUi";
import { useTranslation } from "react-i18next";
function generatePassword(length) {
const characters =
@@ -27,8 +28,22 @@ const AddUser = (props) => {
password: "",
role: ""
});
const [amount, setAmount] = useState({
quantity: 1,
price: 0,
totalPrice: 0
});
const [planInfo, setPlanInfo] = useState({
priceperUser: 0,
price: 0,
totalPrice: 0,
totalAllowedUser: 0
});
const [isFormLoader, setIsFormLoader] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [teamList, setTeamList] = useState([]);
const [allowedUser, setAllowedUser] = useState(0);
const [err, setErr] = useState("");
const role = ["OrgAdmin", "Editor", "User"];
useEffect(() => {
@@ -36,6 +51,40 @@ const AddUser = (props) => {
}, []);
const getTeamList = async () => {
if (isEnableSubscription) {
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
try {
setIsLoader(true);
const resSub = await fetchSubscriptionInfo();
if (!resSub.error) {
setPlanInfo((prev) => ({
...prev,
priceperUser: resSub.price,
totalPrice: resSub.totalPrice,
totalAllowedUser: resSub.totalAllowedUser
}));
setAmount((prev) => ({
...prev,
price: resSub.price,
totalPrice: resSub.price + resSub.totalPrice
}));
const res = await Parse.Cloud.run("allowedusers", {
tenantId: extUser?.TenantId?.objectId
});
setAllowedUser(res);
} else {
setAllowedUser(0);
}
} catch (err) {
setAllowedUser(0);
console.log("Err in alloweduser", err);
setErr(err.message);
} finally {
setIsLoader(false);
}
}
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const teamRes = await Parse.Cloud.run("getteams", { active: true });
if (teamRes.length > 0) {
@@ -67,11 +116,11 @@ const AddUser = (props) => {
e.preventDefault();
e.stopPropagation();
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsLoader(true);
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
setIsLoader(false);
setIsFormLoader(false);
setTimeout(() => {
props.setIsAlert({ type: "success", msg: "" });
}, 1000);
@@ -157,14 +206,8 @@ const AddUser = (props) => {
props.handleUserData(parseData);
}
setIsLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
}
} catch (err) {
console.log("err ", err);
@@ -204,19 +247,13 @@ const AddUser = (props) => {
}
props.handleUserData(parseData);
}
setIsLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
}
}
} catch (err) {
console.log("err", err);
setIsLoader(false);
setIsFormLoader(false);
props.setIsAlert({
type: "danger",
msg: t("something-went-wrong-mssg")
@@ -229,13 +266,7 @@ const AddUser = (props) => {
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
if (props.closePopup) {
props.closePopup();
}
@@ -256,164 +287,283 @@ const AddUser = (props) => {
props.setIsAlert({ type: "success", msg: t("copied") });
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
};
const handlePricePerUser = (e) => {
const quantity = e.target.value;
const price = e.target?.value > 0 ? planInfo.priceperUser * quantity : 0;
const totalprice = price + planInfo.totalPrice;
setAmount((prev) => ({
...prev,
quantity: quantity,
price: price,
totalPrice: totalprice
}));
};
const handleAddOnSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsFormLoader(true);
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
try {
const resAddon = await Parse.Cloud.run("buyaddon", {
users: amount.quantity,
tenantId: extUser?.TenantId?.objectId
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
if (_resAddon.status === "success") {
setAllowedUser(_resAddon.addon);
}
}
} catch (err) {
console.log("Err in buy addon", err);
props.setIsAlert({
type: "danger",
msg: t("something-went-wrong-mssg")
});
} finally {
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 2000);
setIsFormLoader(false);
}
};
return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
<Title title={"Add User"} />
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box">
<Loader />
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<ModalUi
isOpen={props.isOpen}
title={allowedUser > 0 ? t("add-user") : t("Add-seats")}
handleClose={props.closePopup}
>
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
<Title title={t("add-user")} />
{isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
)}
{!isLoader ? (
<>
{err ? (
<div className="flex justify-center items-center h-[70px] text-[18px]">
{err}
</div>
) : (
<div className="w-full mx-auto">
{!isEnableSubscription || allowedUser > 0 ? (
<form onSubmit={handleSubmit}>
<p className="flex justify-center mb-1">
{t("remaing-users")}{" "}
<span
className={`${
allowedUser < 2 ? "op-text-accent" : "op-text-primary"
} font-medium ml-1`}
>
{allowedUser} of {planInfo.totalAllowedUser}
</span>
</p>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
required
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label className="block text-xs text-gray-700 font-semibold">
{t("password")}
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
></i>
</div>
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
{t("password-generateed")}
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder="optional"
value={formdata.phone}
onChange={(e) => handleChange(e)}
// required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
{isEnableSubscription && (
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("team")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<select
name="team"
value={formdata.team}
onChange={(e) => handleChange(e)}
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
)}
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("Role")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.role}
onChange={(e) => handleChange(e)}
name="role"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
{t("submit")}
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
{t("cancel")}
</div>
</div>
</form>
) : (
<form onSubmit={handleAddOnSubmit}>
<p className="flex justify-center text-center mx-2 mb-3 text-base op-text-accent font-medium">
{t("additional-users")}
</p>
<div className="mb-3 flex justify-between">
<label
htmlFor="quantity"
className="block text-xs text-gray-700 font-semibold"
>
{t("Quantity-of-users")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="number"
name="quantity"
value={amount.quantity}
onChange={(e) => handlePricePerUser(e)}
className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
required
/>
</div>
<div className="mb-3 flex justify-between">
<label className="block text-xs text-gray-700 font-semibold">
{t("Price")} (1 * {planInfo.priceperUser})
</label>
<div className="w-1/4 flex justify-center items-center text-sm">
USD {amount.price}
</div>
</div>
<hr className="text-base-content mb-3" />
<div className=" flex justify-between">
<label className="block text-sm text-gray-700 font-semibold">
{t("Total-price-for-next-time")}
</label>
<div className="w-1/4 flex justify-center items-center text-sm font-semibold">
USD {amount.totalPrice}
</div>
</div>
<button className="op-btn op-btn-primary w-full mt-2">
{t("Proceed")}
</button>
</form>
)}
</div>
)}
</>
) : (
<div className="w-full h-[200px] inset-0 flex justify-center items-center z-50">
<Loader />
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("password")}
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
></i>
</div>
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
{t("password-generateed")}
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder="optional"
value={formdata.phone}
onChange={(e) => handleChange(e)}
// required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
{isEnableSubscription && (
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Team<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.team}
onChange={(e) => handleChange(e)}
name="team"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
select
</option>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
)}
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Role<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.role}
onChange={(e) => handleChange(e)}
name="role"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
select
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Cancel
</div>
</div>
</form>
)}
</div>
</div>
</ModalUi>
);
};
+17 -12
View File
@@ -6,12 +6,12 @@ import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize";
import {
checkIsSubscribed,
checkIsSubscribedTeam,
getAppLogo,
openInNewTab,
saveLanguageInLocal
} from "../constant/Utils";
import { isEnableSubscription, isStaging } from "../constant/const";
import { paidUrl, validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const Header = ({ showSidebar }) => {
@@ -22,8 +22,7 @@ const Header = ({ showSidebar }) => {
const image = localStorage.getItem("profileImg") || dp;
const [isOpen, setIsOpen] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(true);
const [isPro, setIsPro] = useState(false);
const [isTeam, setIsTeam] = useState(false);
const [isTeam, setIsTeam] = useState({ plan: "", isValid: false });
const [applogo, setAppLogo] = useState(
localStorage.getItem("appLogo") || " "
);
@@ -37,9 +36,8 @@ const Header = ({ showSidebar }) => {
}, []);
async function checkSubscription() {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
const getIsTeam = await checkIsSubscribedTeam();
if (getIsSubscribe) {
const subscribe = await checkIsSubscribed();
if (subscribe.isValid) {
const applogo = await getAppLogo();
if (applogo?.logo) {
setAppLogo(applogo?.logo);
@@ -47,9 +45,8 @@ const Header = ({ showSidebar }) => {
setAppLogo(localStorage.getItem("appLogo") || "");
}
}
setIsPro(getIsSubscribe);
setIsTeam(getIsTeam);
setIsSubscribe(getIsSubscribe);
setIsTeam(subscribe);
setIsSubscribe(subscribe.isValid);
}
}
@@ -103,6 +100,14 @@ const Header = ({ showSidebar }) => {
window.open("https://console.opensignlabs.com/");
}
};
const handleNavigation = () => {
const route = paidUrl(isTeam.plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
return (
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
@@ -127,18 +132,18 @@ const Header = ({ showSidebar }) => {
<div>
<button
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
onClick={() => navigate("/subscription")}
onClick={() => handleNavigation()}
>
{t("upgrade-now")}
</button>
</div>
)}
{!isTeam && isPro && (
{isTeam.isValid && !validplan[isTeam.plan] && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
{t("pro")}
</div>
)}
{isTeam && (
{validplan[isTeam.plan] && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
{t("team")}
</div>
@@ -23,8 +23,8 @@ const EditTemplate = ({ template, onSuccess }) => {
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
}
};
const handleStrInput = (e) => {
+54 -26
View File
@@ -63,6 +63,8 @@ export async function fetchSubscription(
} else {
plan = tenatRes.data?.result?.result?.PlanCode;
billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
const allowedUsers = tenatRes.data?.result?.result?.AllowedUsers || 0;
localStorage.setItem("allowedUsers", allowedUsers);
}
return { plan, billingDate, status };
} catch (err) {
@@ -70,46 +72,72 @@ export async function fetchSubscription(
return { plan: "", billingDate: "" };
}
}
export async function fetchSubscriptionInfo() {
try {
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
if (extClass && extClass.length > 0) {
const extUser = extClass[0].objectId;
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: extUser };
const tenatRes = await axios.post(url, params, { headers: headers });
const price =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.plan?.price;
const totalPrice =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.amount;
const planId =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.subscription_id;
const plan_code =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.plan?.plan_code;
const totalAllowedUser = tenatRes.data?.result?.result?.AllowedUsers || 0;
return {
status: "success",
price: price,
totalPrice: totalPrice,
planId: planId,
plan_code: plan_code,
totalAllowedUser: totalAllowedUser
};
}
} catch (err) {
console.log("Err in fetch subscription", err);
return { status: "error", error: err };
}
}
//function to get subcripition details from subscription class
export async function checkIsSubscribed() {
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;
}
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
}
}
//function to get subcripition details from subscription class
export async function checkIsSubscribedTeam() {
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
return { plan: res.plan, isValid: false };
} else if (res.billingDate) {
const plan = validplan[res.plan] || false;
if (plan && new Date(res.billingDate) > new Date()) {
return true;
return { plan: res.plan, isValid: true };
} else if (new Date(res.billingDate) > new Date()) {
return { plan: res.plan, isValid: true };
} else {
return false;
return { plan: res.plan, isValid: false };
}
} else {
return false;
return { plan: res.plan, isValid: false };
}
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
return { plan: "no-plan", isValid: false };
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { serverUrl_fn } from "./appinfo";
const parseAppId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = serverUrl_fn()
const serverUrl = serverUrl_fn();
export const SaveFileSize = async (size, imageUrl, tenantId) => {
//checking server url and save file's size
const tenantPtr = {
+8 -5
View File
@@ -26,7 +26,7 @@ body {
.op-bg-info {
@apply bg-info;
}
.op-bg-success{
.op-bg-success {
@apply bg-success;
}
.op-bg-warning {
@@ -43,14 +43,17 @@ body {
.op-text-info {
@apply text-info;
}
.op-text-success{
.op-text-accent {
@apply text-accent;
}
.op-text-success {
@apply text-success;
}
.op-text-warning {
@apply text-warning;
}
.op-border-primary{
@apply border-primary
.op-border-primary {
@apply border-primary;
}
/* CSS for scrollbar customization */
* {
@@ -70,4 +73,4 @@ body {
*::-webkit-scrollbar-thumb {
background-color: gray;
border-radius: 10px;
}
}
+75 -2
View File
@@ -3,6 +3,7 @@ import { isStaging } from "../constant/const";
const plans = [
{
planName: "OPENSIGN™ FREE",
code: { monthly: "freeplan", yearly: "freeplan" },
img: "free.png",
currency: "",
monthlyPrice: "Free",
@@ -52,6 +53,9 @@ const plans = [
},
{
planName: "OPENSIGN™ PROFESSIONAL",
code: isStaging
? { monthly: "pro-weekly", yearly: "pro-yearly" }
: { monthly: "professional-monthly", yearly: "professional-yearly" },
img: "professional.png",
currency: "$",
monthlyPrice: "29.99",
@@ -98,6 +102,7 @@ const plans = [
},
{
planName: "OPENSIGN™ TEAMS",
code: { monthly: "teams-monthly", yearly: "teams-yearly" },
img: "teams.png",
currency: "$",
monthlyPrice: `39.99<sup style="font-size: 17px;">/user</sup>`,
@@ -105,10 +110,10 @@ const plans = [
subtitle: "Exclusive Access to advanced features.",
btn: { text: "Subscribe", color: "op-btn-accent" },
url: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
yearlyUrl: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
target: "_self",
benefits: [
@@ -171,4 +176,72 @@ export const validplan = {
"enterprise-monthly": true,
"enterprise-yearly": true
};
export const paidUrl = (plan) => {
const teamperiod = {
"team-weekly": "monthly",
"team-yearly": "yearly",
"teams-monthly": "monthly",
"teams-yearly": "yearly"
};
const period = teamperiod[plan] || "";
if (period) {
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const user = {
name: extUser?.Name,
email: extUser?.Email,
company: extUser?.Company,
phone: extUser?.Phone
};
// console.log("userDetails ", userDetails);
const fullname = user && user.name ? user.name.split(" ") : "";
const firstname = fullname?.[0]
? "first_name=" + encodeURIComponent(fullname?.[0])
: "";
const lastname = fullname?.[1]
? "&last_name=" + encodeURIComponent(fullname?.[1])
: "";
const name = firstname ? firstname + lastname : "";
const email =
user && user.email ? "&email=" + encodeURIComponent(user.email) : "";
const company =
user && user.company
? "&company_name=" + encodeURIComponent(user.company)
: "";
const phone =
user && user.phone ? "&mobile=" + encodeURIComponent(user.phone) : "";
const allowedUsers = localStorage.getItem("allowedUsers")
? localStorage.getItem("allowedUsers") - 1
: "";
const quantity = allowedUsers
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
quantity +
name +
email +
company +
phone;
if (user) {
localStorage.setItem("userDetails", JSON.stringify(user));
}
const url = {
monthly: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
yearly: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly"
};
const planurl = url[period] + details;
return planurl;
} else {
return "/subscription";
}
};
export default plans;
+12 -4
View File
@@ -11,9 +11,10 @@ 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";
import { fetchSubscription, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { showHeader } from "../redux/reducers/showHeader";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const HomeLayout = () => {
@@ -95,7 +96,14 @@ const HomeLayout = () => {
domain: updateDomain
});
};
const handleNavigation = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
async function checkIsSubscribed() {
if (isEnableSubscription) {
const res = await fetchSubscription();
@@ -107,10 +115,10 @@ const HomeLayout = () => {
setIsUserValid(true);
setIsLoader(false);
} else {
navigate(`/subscription`);
handleNavigation(res.plan);
}
} else {
navigate(`/subscription`);
handleNavigation(res.plan);
}
} else {
setIsUserValid(true);
+1 -5
View File
@@ -215,11 +215,7 @@ const DebugPdf = () => {
};
return (
<div>
{copied && (
<Alert type="success">
<span className="ml-3">Copied</span>
</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} />
+6 -11
View File
@@ -1,7 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { formJson } from "../json/FormJson";
import AddUser from "../components/AddUser";
import Parse from "parse";
import DropboxChooser from "../components/shared/fields/DropboxChoose";
import Alert from "../primitives/Alert";
@@ -37,15 +36,11 @@ function generatePdfName(length) {
function Form() {
const { id } = useParams();
if (id === "lM0xRnM3iE") {
return <AddUser />;
const config = formJson[id];
if (config) {
return <Forms {...config} />;
} else {
const config = formJson[id];
if (config) {
return <Forms {...config} />;
} else {
return <PageNotFound prefix={"Form"} />;
}
return <PageNotFound prefix={"Form"} />;
}
}
@@ -95,8 +90,8 @@ const Forms = (props) => {
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
}
};
+9 -12
View File
@@ -9,6 +9,7 @@ import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
function GenerateToken() {
@@ -18,7 +19,7 @@ function GenerateToken() {
const [apiToken, SetApiToken] = useState("");
const [isLoader, setIsLoader] = useState(true);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
useEffect(() => {
@@ -34,8 +35,8 @@ function GenerateToken() {
const fetchToken = async () => {
try {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
@@ -56,7 +57,7 @@ function GenerateToken() {
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!isSubscribe && isEnableSubscription) {
if (!validplan[isSubscribe.plan] && isEnableSubscription) {
setIsTour(true);
} else {
setIsLoader(true);
@@ -100,11 +101,7 @@ function GenerateToken() {
return (
<React.Fragment>
<Title title={"API Token"} />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{isLoader ? (
<div className="flex justify-center items-center h-screen">
<Loader />
@@ -128,7 +125,7 @@ function GenerateToken() {
<span
id="token"
className={`${
isSubscribe
validplan[isSubscribe.plan]
? ""
: "bg-white/20 pointer-events-none select-none"
} md:text-end py-2 md:py-0`}
@@ -195,9 +192,9 @@ function GenerateToken() {
</div>
</ModalUi>
</div>
{!isSubscribe && isEnableSubscription && (
{!validplan[isSubscribe.plan] && isEnableSubscription && (
<div data-tut="apisubscribe">
<SubscribeCard />
<SubscribeCard plan_code={isSubscribe.plan} />
</div>
)}
</>
+22 -16
View File
@@ -21,6 +21,7 @@ import {
saveLanguageInLocal
} from "../constant/Utils";
import Loader from "../primitives/Loader";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
@@ -80,7 +81,14 @@ function Login() {
}
setState({ ...state, [name]: value });
};
const handlePaidRoute = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
const handleSubmit = async (event) => {
localStorage.removeItem("accesstoken");
event.preventDefault();
@@ -158,9 +166,9 @@ function Login() {
JSON.stringify(LocalUserDetails)
);
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
setState({ ...state, loading: false });
navigate(redirectUrl);
} else if (billingDate) {
@@ -171,11 +179,11 @@ function Login() {
navigate(redirectUrl);
} else {
setState({ ...state, loading: false });
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
@@ -330,9 +338,9 @@ function Login() {
localStorage.setItem("pageType", menu.pageType);
if (isEnableSubscription) {
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
@@ -342,14 +350,14 @@ function Login() {
if (isFreeplan) {
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
}
} else {
if (isFreeplan) {
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
}
} else {
@@ -462,8 +470,8 @@ function Login() {
localStorage.setItem("userDetails", JSON.stringify(userInfo));
const res = await fetchSubscription();
const billingDate = res.billingDate;
const freeplan = res.plan;
if (freeplan === "freeplan") {
const plan = res.plan;
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
@@ -471,10 +479,10 @@ function Login() {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`);
handlePaidRoute(plan);
}
} else {
navigate(`/subscription`);
handlePaidRoute(plan);
}
} else {
// Redirect to the appropriate URL after successful login
@@ -789,9 +797,7 @@ function Login() {
</div>
<SelectLanguage />
{state.alertMsg && (
<Alert type={state.alertType}>
<div className="ml-3">{state.alertMsg}</div>
</Alert>
<Alert type={state.alertType}>{state.alertMsg}</Alert>
)}
</div>
<ModalUi
+13 -2
View File
@@ -31,6 +31,7 @@ import {
handleDownloadPdf,
handleToPrint,
handleDownloadCertificate,
openInNewTab,
getDefaultSignature
} from "../constant/Utils";
import LoaderWithMsg from "../primitives/LoaderWithMsg";
@@ -47,6 +48,7 @@ import { useSelector } from "react-redux";
import SignerListComponent from "../components/pdf/SignerListComponent";
import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
function PdfRequestFiles(props) {
@@ -232,6 +234,15 @@ function PdfRequestFiles(props) {
const currentUser = JSON.parse(localuser);
await handleSendOTP(currentUser?.email);
};
const handleNavigation = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
window.location.href = route;
} else {
openInNewTab(route, "_self");
}
};
async function checkIsSubscribed(extUserId, contactId) {
const isGuestSign = isGuestSignFlow || false;
const res = await fetchSubscription(extUserId, contactId, isGuestSign);
@@ -248,7 +259,7 @@ function PdfRequestFiles(props) {
if (isGuestSign) {
setIsSubscriptionExpired(true);
} else {
window.location.href = "/subscription";
handleNavigation(plan);
}
}
} else if (isGuestSign) {
@@ -262,7 +273,7 @@ function PdfRequestFiles(props) {
if (isGuestSign) {
setIsSubscriptionExpired(true);
} else {
window.location.href = "/subscription";
handleNavigation(res.plan);
}
}
}
+16 -11
View File
@@ -34,7 +34,8 @@ import {
copytoData,
fetchSubscription,
convertPdfArrayBuffer,
getContainerScale
getContainerScale,
openInNewTab
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom";
@@ -53,6 +54,7 @@ import Loader from "../primitives/Loader";
import { useSelector } from "react-redux";
import PdfZoom from "../components/pdf/PdfZoom";
import LottieWithLoader from "../primitives/DotLottieReact";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
function PlaceHolderSign() {
@@ -233,22 +235,29 @@ function PlaceHolderSign() {
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
const handleNavigation = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
return true;
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
}
//function for get document details
@@ -1048,11 +1057,7 @@ function PlaceHolderSign() {
className="flex flex-row justify-between items-center mb-1"
key={ind}
>
{copied && (
<Alert type="success">
<div className="ml-3">{t("copied")}</div>
</Alert>
)}
{copied && <Alert type="success">{t("copied")}</Alert>}
<span className="w-[220px] md:w-[300px] whitespace-nowrap overflow-hidden text-ellipsis ">
{data.signerEmail}
</span>
+24 -6
View File
@@ -53,12 +53,6 @@ const PlanSubscriptions = () => {
? "&mobile=" + encodeURIComponent(userDetails.phone)
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
name +
email +
company +
phone;
useEffect(() => {
setIsLoader(false);
detectLanguage();
@@ -72,6 +66,30 @@ const PlanSubscriptions = () => {
const handleFreePlan = async (item) => {
if (item.url) {
const code = yearlyVisible ? item.code.yearly : item.code.monthly;
const allowedUsers = localStorage.getItem("allowedUsers")
? localStorage.getItem("allowedUsers") - 1
: "";
const teamperiod = {
"team-weekly": "monthly",
"team-yearly": "yearly",
"teams-monthly": "monthly",
"teams-yearly": "yearly"
};
const period = teamperiod[code] || "";
const quantity =
allowedUsers && period
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
quantity +
name +
email +
company +
phone;
const url = yearlyVisible ? item.yearlyUrl + details : item.url + details;
if (user) {
localStorage.setItem("userDetails", JSON.stringify(user));
+12 -3
View File
@@ -3,11 +3,12 @@ import { useLocation, useNavigate } from "react-router-dom";
import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { isEnableSubscription } from "../constant/const";
import { fetchSubscription } from "../constant/Utils";
import { fetchSubscription, openInNewTab } from "../constant/Utils";
import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const SSOVerify = () => {
@@ -50,6 +51,14 @@ const SSOVerify = () => {
console.log("err", err.message);
}
};
const handlePaidRoute = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
const checkExtUser = async (ssosign) => {
const params = { email: ssosign?.email };
try {
@@ -196,10 +205,10 @@ const SSOVerify = () => {
localStorage.removeItem("userDetails");
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
navigate(redirectUrl);
+1 -1
View File
@@ -748,7 +748,7 @@ function SignYourSelf() {
if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject &&
getIsSubscribe
getIsSubscribe.isValid
) {
isCustomCompletionMail = true;
}
+13 -12
View File
@@ -8,9 +8,10 @@ import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
import AddTeam from "../components/AddTeam";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribedTeam } from "../constant/Utils";
import { checkIsSubscribed } from "../constant/Utils";
import SubscribeCard from "../primitives/SubscribeCard";
import Title from "../components/Title";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const heading = ["Sr.No", "Name", "Parent Team", "Active"];
@@ -38,7 +39,7 @@ const TeamList = () => {
const [isActiveModal, setIsActiveModal] = useState({});
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
const [isEditModal, setIsEditModal] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
const startIndex = (currentPage - 1) * recordperPage; // user per page
@@ -103,8 +104,8 @@ const TeamList = () => {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
@@ -225,15 +226,11 @@ const TeamList = () => {
<Loader />
</div>
)}
{isSubscribe && isEnableSubscription && !isLoader && (
{validplan[isSubscribe.plan] && isEnableSubscription && !isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
{t("report-name.Teams")}
@@ -470,9 +467,13 @@ const TeamList = () => {
)}
</>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
{!validplan[isSubscribe.plan] && isEnableSubscription && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAMS"} price={"20"} />
<SubscribeCard
plan={"TEAMS"}
price={"20"}
plan_code={isSubscribe.plan}
/>
</div>
)}
</div>
+16 -5
View File
@@ -25,7 +25,8 @@ import {
textInputWidget,
radioButtonWidget,
fetchSubscription,
getContainerScale
getContainerScale,
openInNewTab
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -40,6 +41,7 @@ import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import Parse from "parse";
import { useSelector } from "react-redux";
import PdfZoom from "../components/pdf/PdfZoom";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const TemplatePlaceholder = () => {
const { t } = useTranslation();
@@ -181,21 +183,30 @@ const TemplatePlaceholder = () => {
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
const handleNavigation = (plan) => {
const route = paidUrl(plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
return true;
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
}
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
@@ -65,12 +65,8 @@ const UpdateExistUserAdmin = () => {
};
return (
<div className="h-screen flex justify-center">
<Title title="Add admin" />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
<Title title={"Add Admin"} />
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{loader ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
+19 -23
View File
@@ -9,8 +9,9 @@ import Tooltip from "../primitives/Tooltip";
import AddUser from "../components/AddUser";
import SubscribeCard from "../primitives/SubscribeCard";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribedTeam } from "../constant/Utils";
import { checkIsSubscribed } from "../constant/Utils";
import Title from "../components/Title";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
// const actions = [];
@@ -26,7 +27,7 @@ const UserList = () => {
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActiveModal, setIsActiveModal] = useState({});
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
const [isAdmin, setIsAdmin] = useState(false);
const recordperPage = 10;
const startIndex = (currentPage - 1) * recordperPage; // user per page
@@ -91,10 +92,10 @@ const UserList = () => {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
} else {
setIsSubscribe(true);
setIsSubscribe({ plan: "teams-yearly", isValid: true });
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
@@ -197,15 +198,11 @@ const UserList = () => {
<Loader />
</div>
)}
{isSubscribe && !isLoader && (
{validplan[isSubscribe.plan] && !isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
{t("report-name.Users")}
@@ -396,17 +393,12 @@ const UserList = () => {
</div>
</div>
)}
<ModalUi
title={t("add-user")}
<AddUser
isOpen={isModal}
handleClose={handleFormModal}
>
<AddUser
setIsAlert={setIsAlert}
handleUserData={handleUserData}
closePopup={handleFormModal}
/>
</ModalUi>
setIsAlert={setIsAlert}
handleUserData={handleUserData}
closePopup={handleFormModal}
/>
</div>
) : (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
@@ -422,9 +414,13 @@ const UserList = () => {
)}
</>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
{!validplan[isSubscribe.plan] && isEnableSubscription && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAMS"} price={"20"} />
<SubscribeCard
plan={"TEAMS"}
price={"20"}
plan_code={isSubscribe.plan}
/>
</div>
)}
</div>
+33 -35
View File
@@ -10,7 +10,6 @@ import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription, isStaging } from "../constant/const";
import {
checkIsSubscribed,
checkIsSubscribedTeam,
copytoData,
handleSendOTP,
openInNewTab
@@ -18,6 +17,7 @@ import {
import Upgrade from "../primitives/Upgrade";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { paidUrl, validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
import { RWebShare } from "react-web-share";
@@ -39,7 +39,7 @@ function UserProfile() {
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isUpgrade, setIsUpgrade] = useState(false);
const [isAlert, setIsAlert] = useState({});
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
const [publicUserName, setPublicUserName] = useState(
extendUser && extendUser?.[0]?.UserName
);
@@ -57,7 +57,7 @@ function UserProfile() {
const [tagLine, setTagLine] = useState(
extendUser && extendUser?.[0]?.Tagline
);
const [isTeam, setIsTeam] = useState(false);
const [isPlan, setIsPlan] = useState({ plan: "", isValid: false });
const getPublicUrl = isStaging
? `https://staging.opensign.me/${extendUser?.[0]?.UserName}`
: `https://opensign.me/${extendUser?.[0]?.UserName}`;
@@ -72,10 +72,9 @@ function UserProfile() {
const jsonSender = JSON.parse(extClass);
const HeaderDocId = jsonSender[0]?.HeaderDocId;
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
const getIsTeam = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
setIsTeam(getIsTeam);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
setIsPlan(subscribe);
}
if (HeaderDocId) {
setIsDisableDocId(HeaderDocId);
@@ -109,13 +108,8 @@ function UserProfile() {
});
if (res) {
setIsLoader(false);
setIsAlert({
type: "danger",
message: t("user-name-exist")
});
setTimeout(() => {
setIsAlert({});
}, 3000);
setIsAlert({ type: "danger", message: t("user-name-exist") });
setTimeout(() => setIsAlert({}), 3000);
return res;
}
} catch (e) {
@@ -320,15 +314,19 @@ function UserProfile() {
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
};
const handlePaidRoute = () => {
const route = paidUrl(isPlan.plan);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
const copytoclipboard = () => {
copytoData(getPublicUrl);
setIsAlert({
type: "success",
message: t("copied")
});
setTimeout(() => {
setIsAlert({});
}, 3000);
setIsAlert({ type: "success", message: t("copied") });
setTimeout(() => setIsAlert({}), 3000);
};
return (
@@ -340,12 +338,11 @@ function UserProfile() {
</div>
) : (
<div className="flex justify-center items-center w-full relative">
{isAlert && (
{isAlert.message && (
<Alert className="z-[1000] fixed top-[10%]" type={isAlert.type}>
{isAlert.message}
</Alert>
)}
<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">
@@ -516,9 +513,7 @@ function UserProfile() {
<span
rel="noreferrer"
target="_blank"
onClick={() => {
openInNewTab(getPublicUrl);
}}
onClick={() => openInNewTab(getPublicUrl)}
className="cursor-pointer underline hover:text-blue-800 w-[200px] md:w-[150px] whitespace-nowrap overflow-hidden text-ellipsis"
>
{isStaging
@@ -527,10 +522,7 @@ function UserProfile() {
</span>
<div className="flex items-center gap-2">
<RWebShare
data={{
url: getPublicUrl,
title: "Sign url"
}}
data={{ url: getPublicUrl, title: "Sign url" }}
>
<button className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm ">
<i className="fa-light fa-share-from-square"></i>{" "}
@@ -581,7 +573,9 @@ function UserProfile() {
<div className="flex justify-between items-center py-2">
<span
className={
isTeam ? "font-semibold" : "font-semibold text-gray-300"
validplan[isPlan.plan]
? "font-semibold"
: "font-semibold text-gray-300"
}
>
{t("disable-documentId")} :{" "}
@@ -590,17 +584,21 @@ function UserProfile() {
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
}
/>
{!isTeam && isEnableSubscription && <Upgrade />}
{!validplan[isPlan.plan] && isEnableSubscription && (
<Upgrade />
)}
</span>
<label
className={`${
isTeam
validplan[isPlan.plan]
? `${editmode ? "cursor-pointer" : ""}`
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
disabled={isTeam ? false : true}
disabled={
validplan[isPlan.plan] && editmode ? false : true
}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
checked={isDisableDocId}
@@ -708,7 +706,7 @@ function UserProfile() {
<p>{t("user-name-limit-char")}</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
onClick={() => handlePaidRoute()}
className="op-btn op-btn-accent"
>
{t("upgrade-now")}
+7 -6
View File
@@ -10,6 +10,7 @@ import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
function Webhook() {
@@ -19,7 +20,7 @@ function Webhook() {
const [webhook, setWebhook] = useState();
const [isLoader, setIsLoader] = useState(true);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
const [error, setError] = useState("");
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
@@ -35,8 +36,8 @@ function Webhook() {
];
const fetchWebhook = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
try {
const extRes = await Parse.Cloud.run("getUserDetails");
@@ -89,7 +90,7 @@ function Webhook() {
};
const handleModal = () => {
if (!isSubscribe && isEnableSubscription) {
if (!validplan[isSubscribe.plan] && isEnableSubscription) {
setIsTour(true);
} else {
setIsModal(!isModal);
@@ -177,9 +178,9 @@ function Webhook() {
</div>
</ModalUi>
</div>
{!isSubscribe && isEnableSubscription && (
{!validplan[isSubscribe.plan] && isEnableSubscription && (
<div data-tut="webhooksubscribe">
<SubscribeCard />
<SubscribeCard plan_code={isSubscribe.plan} />
</div>
)}
</>
+2 -2
View File
@@ -25,8 +25,8 @@ const Alert = ({ children, type, className }) => {
: "z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm"
} `}
>
<div className={`op-alert ${textcolor} flex items-center`}>
<span>{children}</span>
<div className={`op-alert ${textcolor}`}>
<span className="ml-3">{children}</span>
</div>
</div>
)
+117 -110
View File
@@ -11,7 +11,7 @@ import { RWebShare } from "react-web-share";
import Tour from "reactour";
import Parse from "parse";
import {
checkIsSubscribedTeam,
checkIsSubscribed,
copytoData,
fetchUrl,
replaceMailVaribles
@@ -27,6 +27,7 @@ import BulkSendUi from "../components/BulkSendUi";
import Loader from "./Loader";
import Select from "react-select";
import SubscribeCard from "./SubscribeCard";
import { validplan } from "../json/plansArr";
import { serverUrl_fn } from "../constant/appinfo";
import { useTranslation } from "react-i18next";
@@ -355,10 +356,10 @@ const ReportTable = (props) => {
handleBulkSend(item);
} else if (act.action === "sharewith") {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
} else {
setIsSubscribe(true);
setIsSubscribe({ plan: "teams-yearly", isValid: true });
}
if (item?.SharedWith && item?.SharedWith.length > 0) {
// below code is used to get existing sharewith teams and formated them as per react-select
@@ -396,7 +397,7 @@ const ReportTable = (props) => {
Templates: "contracts_Template"
};
try {
const serverUrl = serverUrl_fn()
const serverUrl = serverUrl_fn();
const cls = clsObj[props.ReportName] || "contracts_Document";
const url = serverUrl + `/classes/${cls}/`;
const body =
@@ -1478,114 +1479,120 @@ const ReportTable = (props) => {
{isShareWith[item.objectId] && (
<div className="op-modal op-modal-open">
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
{isSubscribe && isEnableSubscription && (
<>
{item?.Signers?.length > 0 ? (
<div className="h-[150px] flex justify-center items-center mx-2">
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
{validplan[isSubscribe.plan] &&
isEnableSubscription && (
<>
{item?.Signers?.length > 0 ? (
<div className="h-[150px] flex justify-center items-center mx-2">
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<div className="text-base-content text-base text-center">
{t("share-with-alert")}
</div>
</div>
<div className="text-base-content text-base text-center">
{t("share-with-alert")}
</div>
</div>
) : (
<>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{t("share-with")}
</h3>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<form
className="h-full w-full z-[1300] px-2 mt-3"
onSubmit={(e) =>
handleShareWith(e, item)
}
>
<Select
// onSortEnd={onSortEnd}
distance={4}
isMulti
options={teamList}
value={selectedTeam}
onChange={onChange}
closeMenuOnSelect
required={true}
noOptionsMessage={() =>
t("team-not-found")
) : (
<>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{t("share-with")}
</h3>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<form
className="h-full w-full z-[1300] px-2 mt-3"
onSubmit={(e) =>
handleShareWith(e, item)
}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm border-gray-400 focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () =>
"op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () =>
"shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () =>
"p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
<button className="op-btn op-btn-primary ml-[10px] my-3">
{t("submit")}
</button>
</form>
</>
)}
</>
)}
{!isSubscribe && isEnableSubscription && (
<>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<SubscribeCard
plan={"TEAMS"}
price={"20"}
/>
</>
)}
{isSubscribe && !isEnableSubscription && (
<>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{t("share-with")}
</h3>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<div className="px-2 mt-3 w-full h-full">
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
{selectedTeam?.[0]?.label}
>
<Select
// onSortEnd={onSortEnd}
distance={4}
isMulti
options={teamList}
value={selectedTeam}
onChange={onChange}
closeMenuOnSelect
required={true}
noOptionsMessage={() =>
t("team-not-found")
}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm border-gray-400 focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () =>
"op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () =>
"mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () =>
"shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () =>
"p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
<button className="op-btn op-btn-primary ml-[10px] my-3">
{t("submit")}
</button>
</form>
</>
)}
</>
)}
{!validplan[isSubscribe.plan] &&
isEnableSubscription && (
<>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
</div>
<button
onClick={(e) => handleShareWith(e, item)}
className="op-btn op-btn-primary ml-[10px] my-3"
>
{t("submit")}
</button>
</>
)}
<SubscribeCard
plan={"TEAMS"}
price={"20"}
/>
</>
)}
{validplan[isSubscribe.plan] &&
!isEnableSubscription && (
<>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{t("share-with")}
</h3>
<div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})}
>
</div>
<div className="px-2 mt-3 w-full h-full">
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
{selectedTeam?.[0]?.label}
</div>
</div>
<button
onClick={(e) =>
handleShareWith(e, item)
}
className="op-btn op-btn-primary ml-[10px] my-3"
>
{t("submit")}
</button>
</>
)}
</div>
</div>
)}
+12 -2
View File
@@ -1,10 +1,20 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { paidUrl } from "../json/plansArr";
import { openInNewTab } from "../constant/Utils";
const SubscribeCard = ({ plan, price }) => {
const SubscribeCard = ({ plan, price, plan_code }) => {
const { t } = useTranslation();
const navigate = useNavigate();
const handlePaidRoute = () => {
const route = paidUrl(plan_code);
if (route === "/subscription") {
navigate(route);
} else {
openInNewTab(route, "_self");
}
};
return (
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body">
@@ -16,7 +26,7 @@ const SubscribeCard = ({ plan, price }) => {
</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
onClick={() => handlePaidRoute()}
className="op-btn op-btn-accent"
>
{t("upgrade-now")}
@@ -4,7 +4,8 @@ export default async function saveSubscription(request, response) {
const Email = request.body.data?.subscription?.customer?.email;
const Next_billing_date = request.body?.data?.subscription?.next_billing_at;
const planCode = request.body?.data?.subscription?.plan?.plan_code;
const addons = request.body?.data?.subscription?.addons || [];
const existAddon = addons.reduce((acc, curr) => acc + curr.quantity, 1);
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', Email);
@@ -28,6 +29,7 @@ export default async function saveSubscription(request, response) {
updateSubscription.unset('Next_billing_date');
}
updateSubscription.set('PlanCode', planCode);
updateSubscription.set('AllowedUsers', parseInt(existAddon));
await updateSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'update subscription!' });
} else {
@@ -55,6 +57,7 @@ export default async function saveSubscription(request, response) {
createSubscription.unset('Next_billing_date');
}
createSubscription.set('PlanCode', planCode);
createSubscription.set('AllowedUsers', parseInt(existAddon));
await createSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create subscription!' });
}
+4
View File
@@ -60,6 +60,8 @@ import addTeam from './parsefunction/addTeam.js';
import updateTeam from './parsefunction/updateTeam.js';
import getOrgAdmins from './parsefunction/getOrgAdmins.js';
import getAllUserTeamByOrg from './parsefunction/getAllUserTeamByOrg.js';
import AllowedUsers from './parsefunction/AlllowedUsers.js';
import Buyaddon from './parsefunction/BuyAddon.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);
@@ -129,3 +131,5 @@ Parse.Cloud.define('addteam', addTeam);
Parse.Cloud.define('updateteam', updateTeam);
Parse.Cloud.define('getorgadmins', getOrgAdmins);
Parse.Cloud.define('getalluserteambyorg', getAllUserTeamByOrg);
Parse.Cloud.define('allowedusers', AllowedUsers);
Parse.Cloud.define('buyaddon', Buyaddon);
@@ -0,0 +1,44 @@
export default async function AllowedUsers(request) {
const tenantId = request.params.tenantId;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const userCls = new Parse.Query('contracts_Users');
userCls.equalTo('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: _resSub.ExtUserPtr.OrganizationId.objectId,
});
userCls.notEqualTo('IsDisabled', true);
const count = await userCls.count({ useMasterKey: true });
if (count > 0) {
const allowedUser = resSub.get('AllowedUsers') || 0;
const remainUsers = allowedUser - count;
if (remainUsers > 0) {
return remainUsers;
} else {
return 0;
}
} else {
const alloweduser = resSub.get('AllowedUsers') || 0;
return alloweduser;
}
}
} catch (err) {
console.log('err in allowedusers', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -0,0 +1,94 @@
import axios from 'axios';
export default async function Buyaddon(request) {
const tenantId = request.params.tenantId;
const users = request.params.users;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (users && tenantId) {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
// Define the URL
const url = 'https://accounts.zoho.in/oauth/v2/token';
// Convert the data to x-www-form-urlencoded format
const formData = new URLSearchParams();
formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN);
formData.append('client_id', process.env.ZOHO_CLIENT_ID);
formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
if (res.data.access_token) {
const subscriptionId = _resSub.SubscriptionId;
const price = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.price;
const plan_code = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.plan_code;
const addonsArr = _resSub?.SubscriptionDetails?.data?.subscription?.addons || [];
const addon = addonsArr.reduce((acc, curr) => acc + curr.quantity, 0);
const quantity = parseInt(users) + parseInt(addon);
const data = JSON.stringify({
plan: { plan_code: plan_code },
addons: [
{
addon_code: 'extra-users',
addon_description: 'Extra users',
price: price,
quantity: quantity,
},
],
});
await axios.put(
'https://www.zohoapis.in/billing/v1/subscriptions/' + subscriptionId,
data,
{
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
}
);
const hostedpage_id = _resSub.SubscriptionDetails.hostedpage_id;
const userData = await axios.get(
'https://www.zohoapis.in/billing/v1/hostedpages/' + hostedpage_id,
{
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
}
);
const allowedUsers = quantity + 1;
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
updateSub.set('SubscriptionDetails', userData.data);
updateSub.set('AllowedUsers', allowedUsers);
const resupdateSub = await updateSub.save(null, { useMasterKey: true });
return { status: 'success', addon: allowedUsers };
} else {
throw new Parse.Error('400', 'Invalid access token.');
}
}
} catch (err) {
const code = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const msg =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Something went wrong.';
console.log('err in buyaddon', code, msg);
throw new Parse.Error(code, msg);
}
}
}
@@ -18,9 +18,7 @@ export default async function ZohoDetails(request) {
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
// console.log("Access Token:", res.data);
@@ -31,14 +31,17 @@ export default async function saveSubscription(request) {
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
const subscription = await subcriptionCls.first({ useMasterKey: true });
if (subscription) {
const resSubscription = await subcriptionCls.first({ useMasterKey: true });
const addons = subscription?.data?.subscription?.addons || [];
const existAddon = addons.reduce((acc, curr) => acc + curr.quantity, 1);
if (resSubscription) {
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subscription.id;
updateSubscription.id = resSubscription.id;
updateSubscription.set('SubscriptionId', SubscriptionId);
updateSubscription.set('SubscriptionDetails', body);
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
updateSubscription.set('PlanCode', planCode);
updateSubscription.set('AllowedUsers', parseInt(existAddon));
await updateSubscription.save(null, { useMasterKey: true });
return { status: 'update subscription!' };
} else {
@@ -62,6 +65,7 @@ export default async function saveSubscription(request) {
});
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
createSubscription.set('PlanCode', planCode);
createSubscription.set('AllowedUsers', parseInt(existAddon));
await createSubscription.save(null, { useMasterKey: true });
return { status: 'create subscription!' };
}