feat: add share-with opt in templates as well as restrict non-team user

This commit is contained in:
prafull-opensignlabs
2024-07-03 18:01:51 +05:30
parent a246f74e11
commit 57b23d7f97
14 changed files with 814 additions and 565 deletions
@@ -56,11 +56,15 @@ const AddDepartment = (props) => {
objectId: x.objectId
}));
} else {
updatedAncestors.push({
__type: "Pointer",
className: "contracts_Departments",
objectId: formdata.department
});
const AllUser = departmentList.find((x) => x.objectId === "All User");
updatedAncestors = [
AllUser,
{
__type: "Pointer",
className: "contracts_Departments",
objectId: formdata.department
}
];
}
}
try {
+23 -1
View File
@@ -63,7 +63,7 @@ export async function fetchSubscription(
return { plan: "", billingDate: "" };
}
}
//function to get subcripition details from Extand user class
//function to get subcripition details from subscription class
export async function checkIsSubscribed() {
try {
const res = await fetchSubscription();
@@ -84,6 +84,28 @@ export async function checkIsSubscribed() {
}
}
//function to get subcripition details from subscription class
export async function checkIsSubscribedTeam() {
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
} else if (res.billingDate) {
const plan = res.plan === "team-weekly" || res.plan === "team-yearly";
if (plan && 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;
}
}
export const color = [
"#93a3db",
"#e6c3db",
+30
View File
@@ -56,6 +56,36 @@ const plans = [
"Auto reminders"
]
},
{
planName: "OPENSIGN™ TEAM",
currency: "$",
monthlyPrice: "100.99",
yearlyPrice: "329.99",
subtitle: "Exclusive Access to advanced features.",
btnText: "Subscribe",
url:
window.location.origin === "http://localhost:3001"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl:
window.location.origin === "http://localhost:3001"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self",
benefits: [
"Everything in OpenSign™ free, professional",
"Field validations",
"Regular expression validations",
"Organize docs in OpenSign™ Drive",
"Webhooks",
"Zapier integration",
"API Access",
"100 API signatures included",
"DocumentId removal from signed docs",
"Custom email templates",
"Auto reminders"
]
},
{
planName: "OPENSIGN™ ENTERPRISE",
currency: "",
+163 -142
View File
@@ -7,6 +7,9 @@ import Tooltip from "../primitives/Tooltip";
import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
import AddDepartment from "../components/AddDepartment";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribedTeam } from "../constant/Utils";
import SubscribeCard from "../primitives/SubscribeCard";
const heading = ["Sr.No", "Name", "Parent Department", "Is-Active"];
// const actions = [
@@ -32,6 +35,7 @@ const DepartmentList = () => {
const [isActiveModal, setIsActiveModal] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const startIndex = (currentPage - 1) * recordperPage; // user per page
const getPaginationRange = () => {
@@ -92,6 +96,10 @@ const DepartmentList = () => {
async function fetchDepartmentList() {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const department = new Parse.Query("contracts_Departments");
department.equalTo("OrganizationId", {
@@ -173,7 +181,7 @@ const DepartmentList = () => {
return (
<div className="relative">
{isLoader && (
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
@@ -182,86 +190,93 @@ const DepartmentList = () => {
<Loader />
</div>
)}
<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>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
department list{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"department list"} />
</span>
{isSubscribe && isEnableSubscription && !isLoader && (
<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>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
department list{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"department list"} />
</span>
</div>
<div className="cursor-pointer" onClick={() => handleFormModal()}>
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
</div>
</div>
<div className="cursor-pointer" onClick={() => handleFormModal()}>
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
</div>
</div>
<div className={` overflow-x-auto w-full`}>
<table className="op-table border-collapse w-full">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2">{item}</th>
</React.Fragment>
))}
</tr>
</thead>
<tbody className="text-[12px]">
{departmentList?.length > 0 && (
<>
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">{startIndex + index + 1}</th>
)}
<td className="px-4 py-2 font-semibold">{item?.Name}</td>
<td className="px-4 py-2 font-semibold">
{item?.ParentId?.Name || "-"}
</td>
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsActive}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"Department status"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to disable this
department?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleToggleSubmit(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
<div className={` overflow-x-auto w-full`}>
<table className="op-table border-collapse w-full">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2">{item}</th>
</React.Fragment>
))}
</tr>
</thead>
<tbody className="text-[12px]">
{departmentList?.length > 0 && (
<>
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
</td>
{/* <td className="px-3 py-2 text-white flex flex-wrap gap-1">
<td className="px-4 py-2 font-semibold">
{item?.Name}
</td>
<td className="px-4 py-2 font-semibold">
{item?.ParentId?.Name || "-"}
</td>
{item?.Name !== "All User" && (
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsActive}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"Department status"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to disable this
department?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleToggleSubmit(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
)}
</td>
)}
{/* <td className="px-3 py-2 text-white flex flex-wrap gap-1">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
@@ -276,70 +291,76 @@ const DepartmentList = () => {
</button>
))}
</td> */}
</tr>
))}
</>
)}
</tbody>
</table>
</div>
<div className="op-join flex flex-wrap items-center p-2">
{departmentList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{departmentList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
</button>
)}
</div>
{departmentList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl 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>
</tr>
))}
</>
)}
</tbody>
</table>
</div>
)}
<ModalUi
title={"Add Department"}
isOpen={isModal}
handleClose={handleFormModal}
>
<AddDepartment
handleDepartmentInfo={handleDepartmentInfo}
closePopup={handleFormModal}
/>
</ModalUi>
</div>
<div className="op-join flex flex-wrap items-center p-2">
{departmentList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{departmentList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
</button>
)}
</div>
{departmentList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl 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>
)}
<ModalUi
title={"Add Department"}
isOpen={isModal}
handleClose={handleFormModal}
>
<AddDepartment
handleDepartmentInfo={handleDepartmentInfo}
closePopup={handleFormModal}
/>
</ModalUi>
</div>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAM"} />
</div>
)}
</div>
);
};
+156 -148
View File
@@ -123,102 +123,161 @@ function Login() {
if (roles) {
userRoles = roles;
let _currentRole = "";
if (userRoles.length > 0) {
_currentRole = userRoles.find(
(x) => x === extUser.get("UserRole")
);
}
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
"contracts_",
""
);
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem(
"extended_class",
element.extended_class
);
let tenentInfo = [];
const results = [extUser];
if (results) {
let extendedInfo_stringify =
JSON.stringify(results);
const valuesToExclude = [
"contracts_Guest",
"contracts_appeditor"
];
const filterRoles = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (filterRoles.length > 0) {
_currentRole = filterRoles?.[0];
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
"contracts_",
""
);
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
"extended_class",
element.extended_class
);
let extendedInfo = JSON.parse(
extendedInfo_stringify
);
if (extendedInfo.length > 1) {
extendedInfo.forEach((x) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName:
x.TenantId.TenantName || ""
};
tenentInfo.push(obj);
}
});
if (tenentInfo.length) {
dispatch(
showTenant(
let tenentInfo = [];
const results = [extUser];
if (results) {
let extendedInfo_stringify =
JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
);
let extendedInfo = JSON.parse(
extendedInfo_stringify
);
if (extendedInfo.length > 1) {
extendedInfo.forEach((x) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName:
x.TenantId.TenantName || ""
};
tenentInfo.push(obj);
}
});
if (tenentInfo.length) {
dispatch(
showTenant(
tenentInfo[0].tenentName || ""
)
);
localStorage.setItem(
"TenantName",
tenentInfo[0].tenentName || ""
)
);
}
localStorage.setItem("showpopup", true);
localStorage.setItem(
"PageLanding",
element.pageId
);
localStorage.setItem(
"TenantName",
tenentInfo[0].tenentName || ""
"defaultmenuid",
element.menuId
);
}
localStorage.setItem("showpopup", true);
localStorage.setItem(
"PageLanding",
element.pageId
);
localStorage.setItem(
"defaultmenuid",
element.menuId
);
localStorage.setItem(
"pageType",
element.pageType
);
setState({ ...state, loading: false });
navigate("/");
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName:
x.TenantId.TenantName || ""
localStorage.setItem(
"pageType",
element.pageType
);
setState({ ...state, loading: false });
navigate("/");
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName:
x.TenantId.TenantName || ""
};
localStorage.setItem(
"TenantId",
x.TenantId.objectId
);
tenentInfo.push(obj);
}
});
if (tenentInfo.length) {
dispatch(
showTenant(
tenentInfo[0].tenentName || ""
)
);
localStorage.setItem(
"TenantName",
tenentInfo[0].tenentName || ""
);
}
localStorage.setItem(
"PageLanding",
element.pageId
);
localStorage.setItem(
"defaultmenuid",
element.menuId
);
localStorage.setItem(
"pageType",
element.pageType
);
setState({ ...state, loading: false });
if (isEnableSubscription) {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
};
localStorage.setItem(
"TenantId",
x.TenantId.objectId
"userDetails",
JSON.stringify(LocalUserDetails)
);
tenentInfo.push(obj);
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (
new Date(billingDate) > new Date()
) {
localStorage.removeItem(
"userDetails"
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
});
if (tenentInfo.length) {
dispatch(
showTenant(
tenentInfo[0].tenentName || ""
)
);
localStorage.setItem(
"TenantName",
tenentInfo[0].tenentName || ""
);
}
} else {
localStorage.setItem(
"PageLanding",
element.pageId
@@ -234,35 +293,17 @@ function Login() {
setState({ ...state, loading: false });
if (isEnableSubscription) {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
name: _user.name,
email: email,
phone: _user?.phone || ""
// company: results.get("Company"),
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (
new Date(billingDate) > new Date()
) {
localStorage.removeItem(
"userDetails"
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
const billingDate = "";
if (billingDate) {
navigate(`/subscription`, {
replace: true
});
@@ -272,44 +313,9 @@ function Login() {
navigate(redirectUrl);
}
}
} else {
localStorage.setItem(
"PageLanding",
element.pageId
);
localStorage.setItem(
"defaultmenuid",
element.menuId
);
localStorage.setItem(
"pageType",
element.pageType
);
setState({ ...state, loading: false });
if (isEnableSubscription) {
const LocalUserDetails = {
name: _user.name,
email: email,
phone: _user?.phone || ""
// company: results.get("Company"),
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const billingDate = "";
if (billingDate) {
navigate(`/subscription`, {
replace: true
});
}
} else {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
}
}
});
});
}
} else {
setState({ ...state, loading: false });
setIsModal(true);
@@ -719,7 +725,6 @@ function Login() {
.post(url, JSON.stringify(body), { headers: headers1 })
.then((axiosres) => {
const roles = axiosres.data.result;
console.log("roles ", roles);
if (roles && roles.length > 0) {
userRoles = roles;
let _currentRole = "";
@@ -729,7 +734,6 @@ function Login() {
);
if (filterRoles.length > 0) {
_currentRole = filterRoles?.[0];
console.log("_currentRole ", _currentRole);
let SettingsUser = userSettings;
SettingsUser.forEach(async (item) => {
if (item.role === _currentRole) {
@@ -753,6 +757,10 @@ function Login() {
const results = [result];
if (results) {
let extendedInfo_stringify = JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
);
let extendedInfo = JSON.parse(extendedInfo_stringify);
if (extendedInfo.length > 1) {
extendedInfo.forEach((x) => {
+165 -146
View File
@@ -7,6 +7,9 @@ import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
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";
const heading = [
"Sr.No",
"Name",
@@ -28,6 +31,7 @@ const UserList = () => {
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActiveModal, setIsActiveModal] = useState({});
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const recordperPage = 10;
const startIndex = (currentPage - 1) * recordperPage; // user per page
@@ -89,6 +93,10 @@ const UserList = () => {
async function fetchUserList() {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const res = await Parse.Cloud.run("getuserlistbyorg", {
organizationId: extUser.OrganizationId.objectId
@@ -170,7 +178,7 @@ const UserList = () => {
return (
<div className="relative">
{isLoader && (
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
@@ -179,90 +187,95 @@ const UserList = () => {
<Loader />
</div>
)}
<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>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
User list{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"user list from departments"} />
</span>
{isSubscribe && isEnableSubscription && !isLoader && (
<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>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
User list{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"user list from departments"} />
</span>
</div>
<div className="cursor-pointer" onClick={() => handleFormModal()}>
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
</div>
</div>
<div className="cursor-pointer" onClick={() => handleFormModal()}>
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
</div>
</div>
<div className={` overflow-x-auto w-full`}>
<table className="op-table border-collapse w-full">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2">{item}</th>
</React.Fragment>
))}
</tr>
</thead>
<tbody className="text-[12px]">
{userList?.length > 0 && (
<>
{userList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">{startIndex + index + 1}</th>
)}
<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-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<td className="px-4 py-2">
{formatRow(item.DepartmentIds)}
</td>
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsDisabled !== true}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"User status"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to deactivate this user?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleToggleSubmit(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
<div className={` overflow-x-auto w-full`}>
<table className="op-table border-collapse w-full">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2">{item}</th>
</React.Fragment>
))}
</tr>
</thead>
<tbody className="text-[12px]">
{userList?.length > 0 && (
<>
{userList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
</td>
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
<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-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<td className="px-4 py-2">
{formatRow(item.DepartmentIds)}
</td>
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsDisabled !== true}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"User status"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to deactivate this user?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleToggleSubmit(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
)}
</td>
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
@@ -305,70 +318,76 @@ const UserList = () => {
</ModalUi>
)}
</td> */}
</tr>
))}
</>
)}
</tbody>
</table>
</div>
<div className="op-join flex flex-wrap items-center p-2">
{userList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{userList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
</button>
)}
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl 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>
</tr>
))}
</>
)}
</tbody>
</table>
</div>
)}
<ModalUi
title={"Add User"}
isOpen={isModal}
handleClose={handleFormModal}
>
<AddUser
handleUserData={handleUserData}
closePopup={handleFormModal}
/>
</ModalUi>
</div>
<div className="op-join flex flex-wrap items-center p-2">
{userList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{userList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
</button>
)}
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl 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>
)}
<ModalUi
title={"Add User"}
isOpen={isModal}
handleClose={handleFormModal}
>
<AddUser
handleUserData={handleUserData}
closePopup={handleFormModal}
/>
</ModalUi>
</div>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAM"} />
</div>
)}
</div>
);
};
@@ -30,8 +30,7 @@ const LottieWithLoader = () => {
return (
<div>
{!isLoaded && !hasError && (
<div className="loader">
{" "}
<div className="w-[120px] h-[120px] mx-auto">
<Loader />
</div>
)}
@@ -39,9 +38,8 @@ const LottieWithLoader = () => {
{isLoaded && animationSrc && (
<DotLottieReact
src={animationSrc}
loop
autoplay
style={{ display: "block" }}
className="w-[120px] h-[120px] md:w-[200px] md:h-[200px] mx-auto"
/>
)}
</div>
+143 -102
View File
@@ -12,6 +12,7 @@ import Tour from "reactour";
import Parse from "parse";
import { saveAs } from "file-saver";
import {
checkIsSubscribedTeam,
// copytoData,
replaceMailVaribles
} from "../constant/Utils";
@@ -25,6 +26,8 @@ import "react-quill/dist/quill.snow.css";
import BulkSendUi from "../components/BulkSendUi";
import Loader from "./Loader";
import Select from "react-select";
import { isEnableSubscription } from "../constant/const";
import SubscribeCard from "./SubscribeCard";
const ReportTable = (props) => {
const navigate = useNavigate();
@@ -64,6 +67,9 @@ const ReportTable = (props) => {
// const [isPublicProfile, setIsPublicProfile] = useState({});
// const [publicUserName, setIsPublicUserName] = useState("");
const [isViewShare, setIsViewShare] = useState({});
const [isSubscribe, setIsSubscribe] = useState(true);
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props;
// For loop is used to calculate page numbers visible below table
@@ -191,9 +197,7 @@ const ReportTable = (props) => {
} else {
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
try {
const params = {
templateId: item.objectId
};
const params = { templateId: item.objectId };
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
@@ -224,6 +228,19 @@ const ReportTable = (props) => {
});
}
// console.log("extClass ", extClass);
let extUserId = Doc.ExtUserPtr.objectId;
let creatorId = Doc.CreatedBy.objectId;
if (extClass && extClass.length > 0) {
if (Doc.ExtUserPtr?.objectId !== extClass[0].objectId) {
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
if (extClass && extClass.length > 0) {
extUserId = extClass[0].objectId;
creatorId = extClass[0]?.UserId.objectId;
}
}
}
let placeholdersArr = [];
if (Doc.Placeholders?.length > 0) {
placeholdersArr = Doc.Placeholders;
@@ -238,12 +255,12 @@ const ReportTable = (props) => {
ExtUserPtr: {
__type: "Pointer",
className: "contracts_Users",
objectId: Doc.ExtUserPtr.objectId
objectId: extUserId
},
CreatedBy: {
__type: "Pointer",
className: "_User",
objectId: Doc.CreatedBy.objectId
objectId: creatorId
},
Signers: signers,
SendinOrder: Doc?.SendinOrder || false,
@@ -254,7 +271,7 @@ const ReportTable = (props) => {
const res = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}classes/${localStorage.getItem("_appName")}_Document`,
)}classes/contracts_Document`,
data,
{
headers: {
@@ -315,7 +332,7 @@ const ReportTable = (props) => {
}
};
const handleActionBtn = (act, item) => {
const handleActionBtn = async (act, item) => {
if (act.action === "redirect") {
handleURL(item, act);
} else if (act.action === "delete") {
@@ -331,6 +348,10 @@ const ReportTable = (props) => {
} else if (act.action === "bulksend") {
handleBulkSend(item);
} else if (act.action === "sharewith") {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
}
if (item?.SharedWith && item?.SharedWith.length > 0) {
// below code is used to get existing sharewith departments and formated them as per react-select
const formatedList = item?.SharedWith.map((x) => ({
@@ -1331,106 +1352,126 @@ const ReportTable = (props) => {
<td className="px-2 py-2">
<div className="text-base-content flex flex-row gap-x-2 gap-y-1 justify-start items-center">
{props.actions?.length > 0 &&
props.actions.map((act, index) => (
<div
role="button"
data-tut={act?.selector}
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
className={
act.action !== "option"
? `${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm mr-1`
: "text-base-content focus:outline-none text-lg mr-2 relative"
}
>
<i className={act.btnIcon}></i>
{act.btnLabel && (
<span className="uppercase font-medium">
{act.btnLabel}
</span>
)}
{isOption[item.objectId] &&
act.action === "option" && (
<ul className="absolute -right-2 top-6 z-[20] op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
{act.subaction?.map((subact) => (
<li
key={subact.btnId}
onClick={() =>
handleActionBtn(subact, item)
}
title={subact.hoverLabel}
>
<span>
<i
className={`${subact.btnIcon} mr-1.5`}
></i>
{subact.btnLabel && (
<span className="text-[13px] capitalize font-medium">
{subact.btnLabel}
props.actions.map(
(act, index) =>
(item.ExtUserPtr?.objectId ===
extClass?.[0]?.objectId ||
act.btnLabel === "Use") && (
<div
role="button"
data-tut={act?.selector}
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
className={
act.action !== "option"
? `${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm mr-1`
: "text-base-content focus:outline-none text-lg mr-2 relative"
}
>
<i className={act.btnIcon}></i>
{act.btnLabel && (
<span className="uppercase font-medium">
{act.btnLabel}
</span>
)}
{isOption[item.objectId] &&
act.action === "option" && (
<ul className="absolute -right-2 top-6 z-[20] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
{act.subaction?.map((subact) => (
<li
key={subact.btnId}
onClick={() =>
handleActionBtn(subact, item)
}
title={subact.hoverLabel}
>
<span>
<i
className={`${subact.btnIcon} mr-1.5`}
></i>
{subact.btnLabel && (
<span className="text-[13px] capitalize font-medium">
{subact.btnLabel}
</span>
)}
</span>
)}
</span>
</li>
))}
</ul>
)}
</div>
))}
</li>
))}
</ul>
)}
</div>
)
)}
</div>
{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">
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
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={departmentList}
value={selectedDepartments}
onChange={onChange}
closeMenuOnSelect={false}
required={true}
noOptionsMessage={() =>
"Departments not found"
}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm 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 my-3">
Submit
</button>
</form>
{isSubscribe && isEnableSubscription && (
<>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
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={departmentList}
value={selectedDepartments}
onChange={onChange}
closeMenuOnSelect={false}
required={true}
noOptionsMessage={() =>
"Departments 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">
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={"TEAM"} />
</>
)}
</div>
</div>
)}
@@ -1,12 +1,12 @@
import React from "react";
import { useNavigate } from "react-router-dom";
const SubscribeCard = () => {
const SubscribeCard = ({ plan }) => {
const navigate = useNavigate();
return (
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body">
<h2 className="op-card-title">Upgrade to PRO Plan</h2>
<h2 className="op-card-title">Upgrade to {plan ? plan : "PRO"} Plan</h2>
<p>$29.99/month: Sign 100 docs using API. Only $0.15/doc after that.</p>
<div className="op-card-actions justify-end">
<button
+2 -1
View File
@@ -47,6 +47,7 @@ import isextenduser from './parsefunction/isextenduser.js';
import getUserByOrg from './parsefunction/getUserByOrg.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import DepartmentsAftersave from './parsefunction/DepartmentsAftersave.js';
import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.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);
@@ -54,7 +55,7 @@ Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
Parse.Cloud.afterSave('contracts_Departments', DepartmentsAftersave);
Parse.Cloud.afterSave('contracts_Subscriptions', SubscriptionAftersave);
// This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification.
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
@@ -16,22 +16,45 @@ export default async function GetTemplate(request) {
// console.log("userId ",userId)
if (templateId && userId) {
try {
const template = new Parse.Query('contracts_Template');
let template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreateBy');
template.include('CreatedBy');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userRes.data.email);
extUserQuery.include('DepartmentIds');
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
if (_extUser?.DepartmentIds && _extUser.DepartmentIds?.length > 0) {
let departmentArr = [];
_extUser?.DepartmentIds?.forEach(
x => (departmentArr = [...departmentArr, ...x.Ancestors])
);
// Create the first query
const sharedWithQuery = new Parse.Query('contracts_Template');
sharedWithQuery.containedIn('SharedWith', departmentArr);
// Create the second query
const createdByQuery = new Parse.Query('contracts_Template');
createdByQuery.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
template = Parse.Query.or(sharedWithQuery, createdByQuery);
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
}
}
const res = await template.first({ useMasterKey: true });
// console.log("res ", res)
if (res) {
// console.log("res ",res)
const acl = res.getACL();
// console.log("acl", acl.getReadAccess(userId))
if (acl && acl.getReadAccess(userId)) {
return res;
} else {
return { error: "You don't have access of this document!" };
}
return res;
} else {
return { error: "You don't have access of this document!" };
}
@@ -0,0 +1,82 @@
async function addDepartmentAndOrg(extUser) {
try {
const orgCls = new Parse.Object('contracts_Organizations');
orgCls.set('Name', extUser.Company);
orgCls.set('IsActive', true);
const orgRes = await orgCls.save(null, { useMasterKey: true });
const departmentCls = new Parse.Object('contracts_Departments');
departmentCls.set('Name', 'All User');
departmentCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
departmentCls.set('IsActive', true);
const departmentRes = await departmentCls.save(null, { useMasterKey: true });
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUser.objectId;
updateUser.set('UserRole', 'contracts_Admin');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('DepartmentIds', [
{
__type: 'Pointer',
className: 'contracts_Departments',
objectId: departmentRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
} catch (err) {
console.log('err in add department, role, org', err);
}
}
export default async function SubscriptionAftersave(request) {
const oldObj = request.original;
if (!oldObj) {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.include('CreatedBy');
const res = await subscription.get(request.object.id, { useMasterKey: true });
const _res = JSON.parse(JSON.stringify(res));
const user = _res.CreatedBy?.email;
if (user) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', user);
const extUserRes = await extUserQuery.first({ useMasterKey: true });
if (extUserRes) {
const extUser = JSON.parse(JSON.stringify(extUserRes));
if (extUser?.UserRole !== 'contracts_Admin') {
await addDepartmentAndOrg(extUser);
}
}
}
} catch (err) {
console.log('Err in subscriptionaftersave', err);
}
} else {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.include('CreatedBy');
const res = await subscription.get(request.object.id, { useMasterKey: true });
const _res = JSON.parse(JSON.stringify(res));
const user = _res.CreatedBy?.email;
if (user) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', user);
const extUserRes = await extUserQuery.first({ useMasterKey: true });
if (extUserRes) {
const extUser = JSON.parse(JSON.stringify(extUserRes));
if (extUser?.UserRole !== 'contracts_Admin') {
await addDepartmentAndOrg(extUser);
}
}
}
} catch (err) {
console.log('Err in subscriptionaftersave', err);
}
}
}
@@ -42,10 +42,10 @@ export default async function getReport(request) {
$or: [
{ SharedWith: { $in: departmentArr } },
{
CreatedBy: {
ExtUserPtr: {
__type: 'Pointer',
className: '_User',
objectId: userId,
className: 'contracts_Users',
objectId: extUser.id,
},
},
],
@@ -82,7 +82,7 @@ export default async function usersignup(request) {
if (userDetails?.phone) {
partnerQuery.set('ContactNumber', userDetails.phone);
}
partnerQuery.set('TenantName', userDetails.name);
partnerQuery.set('TenantName', userDetails.company);
partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true);
partnerQuery.set('CreatedBy', {