Merge pull request #898 from OpenSignLabs/multi_user

This commit is contained in:
Andrew
2024-07-03 23:32:38 +05:30
committed by GitHub
37 changed files with 2311 additions and 682 deletions
+4
View File
@@ -18,6 +18,8 @@ import LazyPage from "./primitives/LazyPage";
import { isEnableSubscription } from "./constant/const";
import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader";
import TeamList from "./pages/TeamList";
import UserList from "./pages/UserList";
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
@@ -120,6 +122,8 @@ function App() {
</>
)}
<Route element={<HomeLayout />}>
<Route path="/teams" element={<TeamList />} />
<Route path="/users" element={<UserList />} />
<Route
path="/changepassword"
element={<LazyPage Page={ChangePassword} />}
@@ -0,0 +1,237 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
const AddDepartment = (props) => {
const [formdata, setFormdata] = useState({
name: "",
department: ""
});
const [isLoader, setIsLoader] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [departmentList, setDepartmentList] = useState([]);
useEffect(() => {
getDepartmentList();
}, []);
const getDepartmentList = async () => {
try {
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const department = new Parse.Query("contracts_Departments");
department.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: extUser.OrganizationId.objectId
});
department.equalTo("IsActive", true);
const departmentRes = await department.find();
if (departmentRes.length > 0) {
const _departmentRes = JSON.parse(JSON.stringify(departmentRes));
setDepartmentList(_departmentRes);
}
} catch (err) {
console.log("Err in fetch top level departmentlist", err);
}
};
const handleDropdown = (e) => {
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
// Extracting values except for the 'name' key
let updatedAncestors = [];
if (formdata.department) {
const Ancestors = departmentList.find(
(x) => x.objectId === formdata.department
)?.Ancestors;
if (Ancestors && Ancestors.length > 0) {
updatedAncestors = Ancestors.map((x) => ({
__type: "Pointer",
className: "contracts_Departments",
objectId: x.objectId
}));
} else {
const AllUser = departmentList.find((x) => x.objectId === "All Users");
updatedAncestors = [
AllUser,
{
__type: "Pointer",
className: "contracts_Departments",
objectId: formdata.department
}
];
}
}
try {
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsLoader(true);
const department = new Parse.Query("contracts_Departments");
department.equalTo("Name", formdata.name);
if (updatedAncestors.length > 0) {
const ParentId = updatedAncestors[updatedAncestors.length - 1];
department.equalTo("ParentId", ParentId);
}
if (localUser && localUser.OrganizationId) {
department.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: localUser.OrganizationId.objectId
});
}
const isDepartment = await department.first();
if (isDepartment) {
setIsAlert({ type: "info", msg: "Department already exists." });
setIsLoader(false);
} else {
const newDepartment = new Parse.Object("contracts_Departments");
newDepartment.set("Name", formdata.name);
if (updatedAncestors.length > 0) {
const ParentId = updatedAncestors[updatedAncestors.length - 1];
newDepartment.set("ParentId", ParentId);
newDepartment.set("Ancestors", updatedAncestors);
}
if (localUser && localUser.OrganizationId) {
newDepartment.set("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: localUser.OrganizationId.objectId
});
}
const newdepartmentRes = await newDepartment.save();
if (updatedAncestors.length > 0) {
const ParentId = departmentList.find(
(x) => x.objectId === formdata.department
);
props.handleTeamInfo({
objectId: newdepartmentRes.id,
Name: formdata.name,
ParentId: ParentId,
Ancestors: updatedAncestors,
IsActive: true
});
} else {
props.handleTeamInfo({
objectId: newdepartmentRes.id,
Name: formdata.name,
ParentId: "",
Ancestors: "",
IsActive: true
});
}
if (props.closePopup) {
props.closePopup();
}
setFormdata({
name: "",
department: { name: "", objectId: "" }
});
setIsAlert({
type: "success",
msg: "Department created successfully."
});
}
} catch (err) {
console.log("err in save department", err);
setIsAlert({ type: "danger", msg: "Something went wrong." });
} finally {
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500);
setIsLoader(false);
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setFormdata({
name: "",
department: { name: "", objectId: "" }
});
};
const handleChange = (e) => {
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
<Title title={"Add Department"} />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{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"
>
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
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>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Parent Team
</label>
<select
value={formdata.department}
onChange={(e) => handleDropdown(e)}
name="department"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
>
<option defaultValue={""} value={""}>
select
</option>
{departmentList.length > 0 &&
departmentList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</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"
>
Reset
</div>
</div>
</form>
</div>
</div>
);
};
export default AddDepartment;
+242 -85
View File
@@ -1,24 +1,62 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { copytoData } from "../constant/Utils";
function generatePassword(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const AddUser = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [formdata, setFormdata] = useState({
name: "",
phone: "",
email: "",
department: "",
password: "",
role: ""
});
const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [departmentList, setDepartmentList] = useState([]);
const role = ["OrgAdmin", "Manager", "User"];
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => {
getDepartmentList();
}, []);
const getDepartmentList = async () => {
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const department = new Parse.Query("contracts_Departments");
department.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: extUser.OrganizationId.objectId
});
department.equalTo("IsActive", true);
const departmentRes = await department.find();
if (departmentRes.length > 0) {
const _departmentRes = JSON.parse(JSON.stringify(departmentRes));
setDepartmentList(_departmentRes);
}
};
const checkUserExist = async () => {
try {
const res = await Parse.Cloud.run("getUserDetails", {
email: email,
userId: Parse.User.current().id
email: formdata.email
});
if (res) {
return true;
@@ -33,24 +71,43 @@ const AddUser = (props) => {
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsLoader(true);
const res = await checkUserExist();
if (res) {
setIsUserExist(true);
setIsAlert({ type: "danger", msg: "User already exist." });
setIsLoader(false);
setTimeout(() => {
setIsUserExist(false);
setIsAlert({ type: "success", msg: "" });
}, 1000);
} else {
try {
const contactQuery = new Parse.Object("contracts_Users");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_User");
const extUser = new Parse.Object("contracts_Users");
extUser.set("Name", formdata.name);
if (formdata.phone) {
extUser.set("Phone", formdata.phone);
}
extUser.set("Email", formdata.email);
extUser.set("UserRole", `contracts_${formdata.role}`);
if (formdata?.department) {
extUser.set("DepartmentIds", [
{
__type: "Pointer",
className: "contracts_Departments",
objectId: formdata.department
}
]);
}
if (localUser && localUser.OrganizationId) {
extUser.set("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: localUser.OrganizationId.objectId
});
}
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
extUser.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
@@ -60,12 +117,12 @@ const AddUser = (props) => {
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
_user.set("name", formdata.name);
_user.set("username", formdata.email);
_user.set("email", formdata.email);
_user.set("password", formdata.password);
if (formdata.phone) {
_user.set("phone", formdata.phone);
}
const user = await _user.save();
@@ -77,52 +134,58 @@ const AddUser = (props) => {
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
appName: "contracts",
roleName: "contracts_" + formdata.role,
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
extUser.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
extUser.setACL(acl);
const res = await contactQuery.save();
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
if (formdata?.department) {
const department = departmentList.find(
(x) => x.objectId === formdata.department
);
parseData.DepartmentIds = parseData.DepartmentIds.map((y) =>
y.objectId === department.objectId ? department : y
);
}
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
setFormdata({
name: "",
email: "",
phone: "",
department: "",
role: ""
});
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const params = { email: formdata.email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
@@ -131,18 +194,18 @@ const AddUser = (props) => {
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
appName: "contracts",
roleName: "contracts_" + formdata.role,
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
extUser.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
@@ -153,55 +216,85 @@ const AddUser = (props) => {
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
extUser.setACL(acl);
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
if (formdata?.department) {
const department = departmentList.find(
(x) => x.objectId === formdata.department
);
parseData.DepartmentIds = parseData.DepartmentIds.map((y) =>
y.objectId === department.objectId ? department : y
);
}
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
setFormdata({
name: "",
email: "",
phone: "",
department: "",
role: ""
});
}
}
} catch (err) {
// console.log("err", err);
console.log("err", err);
setIsLoader(false);
alert("something went wrong!");
setIsAlert({ type: "danger", msg: "something went wrong." });
} finally {
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
}
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setName("");
setPhone("");
setEmail("");
setFormdata({
name: "",
email: "",
phone: "",
department: "",
role: ""
});
if (props.closePopup) {
props.closePopup();
}
};
const handleChange = (e) => {
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const copytoclipboard = (text) => {
copytoData(text);
setIsAlert({ type: "success", msg: "Copied" });
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500); // Reset copied state after 1.5 seconds
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
<Title title={"Add User"} />
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
<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>
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
<div className="mb-3">
<label
htmlFor="name"
@@ -212,9 +305,9 @@ const AddUser = (props) => {
</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
name="name"
value={formdata.name}
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"
/>
@@ -229,44 +322,108 @@ const AddUser = (props) => {
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
name="email"
value={formdata.email}
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>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
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]"
></i>
</div>
<div className="text-[12px] ml-2 mb-0 text-[red]">
Password will only be generated once; make sure to copy it.
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
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>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span className="text-[red] text-[13px]"> *</span>
Team
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
<select
value={formdata.department}
onChange={(e) => handleChange(e)}
name="department"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button
type="submit"
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
>
<option defaultValue={""} value={""}>
select
</option>
{departmentList.length > 0 &&
departmentList.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
</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"
>
<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="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
className="op-btn op-btn-secondary"
>
Reset
Cancel
</div>
</div>
</form>
+11 -13
View File
@@ -298,6 +298,17 @@ function Header({
) : isPdfRequestFiles ? (
alreadySign ? (
<div className="flex flex-row">
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
>
<i
className="fa-light fa-print py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
{isCompleted && (
<button
type="button"
@@ -313,19 +324,6 @@ function Header({
<span className="hidden lg:block">Certificate</span>
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
>
<i
className="fa-light fa-print py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
+42 -14
View File
@@ -23,29 +23,57 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
const menuItem = async () => {
try {
if (localStorage.getItem("defaultmenuid")) {
const menuId = localStorage.getItem("defaultmenuid") !== "VPh91h0ZHk";
if (menuId) {
setmenuList(sidebarList);
} else {
const addUserForm = {
icon: "fa-light fa-user",
title: "Add User",
target: "_self",
pageType: "form",
description: "",
objectId: "lM0xRnM3iE"
};
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
let userRole = "contracts_Users";
if (extClass && extClass.length > 0) {
userRole = extClass[0].UserRole;
}
if (
userRole === "contracts_Admin" ||
userRole === "contracts_OrgAdmin"
) {
// const addUserForm = {
// icon: "fa-light fa-user",
// title: "Add User",
// target: "_self",
// pageType: "form",
// description: "",
// objectId: "lM0xRnM3iE"
// };
const newSidebarList = sidebarList.map((item) => {
if (item.title === "Settings") {
// Make a shallow copy of the item
const newItem = { ...item };
newItem.children = [
...newItem.children,
{
icon: "fa-light fa-building-memo",
title: "Teams",
target: "_self",
pageType: "",
description: "",
objectId: "teams"
},
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
];
// Insert addUserForm at the second position
newItem.children.splice(1, 0, addUserForm);
// newItem.children.splice(1, 0, addUserForm);
return newItem;
}
return item;
});
setmenuList(newSidebarList);
} else {
setmenuList(sidebarList);
}
}
} catch (e) {
@@ -63,7 +91,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
};
return (
<aside
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[999] shadow-lg hide-scrollbar
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[500] shadow-lg hide-scrollbar
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
>
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
+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",
+17 -3
View File
@@ -21,15 +21,29 @@ export const appInfo = {
objectId: "aIPmIvMzGM",
settings: [
{
role: "contracts_User",
role: "contracts_Admin",
menuId: "VPh91h0ZHk",
pageType: "dashboard",
pageId: "35KBoSgoAK",
extended_class: "contracts_Users"
},
{
role: "contracts_OrgAdmin",
menuId: "VPh91h0ZHk",
pageType: "dashboard",
pageId: "35KBoSgoAK",
extended_class: "contracts_Users"
},
{
role: "contracts_Manager",
menuId: "H9vRfEYKhT",
pageType: "dashboard",
pageId: "35KBoSgoAK",
extended_class: "contracts_Users"
},
{
role: "contracts_Admin",
menuId: "VPh91h0ZHk",
role: "contracts_User",
menuId: "H9vRfEYKhT",
pageType: "dashboard",
pageId: "35KBoSgoAK",
extended_class: "contracts_Users"
+8
View File
@@ -346,6 +346,14 @@ export default function reportJson(id) {
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "1873",
btnLabel: "Share-with",
hoverLabel: "Share-with",
btnIcon: "fa-light fa-share-nodes",
redirectUrl: "",
action: "sharewith"
},
{
btnId: "2434",
btnLabel: "Edit",
+31 -1
View File
@@ -30,7 +30,7 @@ const plans = [
planName: "OPENSIGN™ PROFESSIONAL",
currency: "$",
monthlyPrice: "29.99",
yearlyPrice: "329.99",
yearlyPrice: "10 <sup>/month/user</sup>",
subtitle: "Exclusive Access to advanced features.",
btnText: "Subscribe",
url:
@@ -56,6 +56,36 @@ const plans = [
"Auto reminders"
]
},
{
planName: "OPENSIGN™ TEAMS",
currency: "$",
monthlyPrice: "40",
yearlyPrice: "20 <sup>/month/user</sup>",
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: "",
+1 -1
View File
@@ -251,7 +251,7 @@ const HomeLayout = () => {
};
return (
<div>
<div className="sticky top-0 z-[101]">
<div className="sticky top-0 z-[501]">
{!isLoader && <Header showSidebar={showSidebar} />}
</div>
{isUserValid ? (
+2 -2
View File
@@ -645,8 +645,8 @@ const Forms = (props) => {
</label>
{fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-x text-[13px]">
<div className="break-all ">
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-[13px]">
<div className="break-all">
file selected: {getFileName(fileupload)}
</div>
<div
+230 -268
View File
@@ -110,68 +110,49 @@ function Login() {
"X-Parse-Application-Id": parseAppId,
sessionToken: _user.sessionToken
};
let body = {
appname: localStorage.getItem("_appName")
};
const body = { appname: "contracts" };
await axios
.post(url, JSON.stringify(body), { headers: headers })
.then((axiosRes) => {
.then(async (axiosRes) => {
const roles = axiosRes.data.result;
if (roles) {
userRoles = roles;
let _currentRole = "";
const valuesToExclude = [
"contracts_Guest",
`${localStorage.getItem("_appName")}_appeditor`
];
if (userRoles.length > 1) {
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = rolesfiltered[0];
}
} else {
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = userRoles[0];
} else {
_currentRole = "";
}
}
if (
_currentRole &&
_currentRole !==
`${localStorage.getItem("_appName")}_appeditor`
) {
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
`${localStorage.getItem("_appName")}_`,
""
);
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem(
"extended_class",
element.extended_class
);
const currentUser = Parse.User.current();
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
}).then(
async (result) => {
const currentUser = Parse.User.current();
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
})
.then((extUser) => {
if (roles) {
userRoles = roles;
let _currentRole = "";
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(
"extended_class",
element.extended_class
);
let tenentInfo = [];
const results = [result];
const results = [extUser];
if (results) {
let extendedInfo_stringify =
JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
@@ -332,28 +313,21 @@ function Login() {
navigate(redirectUrl);
}
}
},
(error) => {
const payload = {
sessionToken: user.getSessionToken()
};
handleSubmitbtn(payload);
console.error(
"Error while fetching Follow",
error
);
}
);
});
}
});
} else {
setState({ ...state, loading: false });
setIsModal(true);
}
} else {
setState({ ...state, loading: false });
setIsModal(true);
}
} else {
setState({ ...state, loading: false });
setIsModal(true);
}
})
.catch((error) => {
const payload = {
sessionToken: user.getSessionToken()
};
handleSubmitbtn(payload);
console.error("Error while fetching Follow", error);
});
})
.catch((err) => {
console.log("err", err);
@@ -474,7 +448,7 @@ function Login() {
let _currentRole = "";
const valuesToExclude = [
"contracts_Guest",
`${localStorage.getItem("_appName")}_appeditor`
"contracts_appeditor"
];
if (userRoles.length > 1) {
const rolesfiltered = userRoles.filter(
@@ -493,20 +467,13 @@ function Login() {
_currentRole = "";
}
}
if (
_currentRole &&
_currentRole !==
`${localStorage.getItem("_appName")}_appeditor`
) {
if (_currentRole && _currentRole !== "contracts_appeditor") {
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
`${localStorage.getItem("_appName")}_`,
""
);
let _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
@@ -732,185 +699,126 @@ function Login() {
setState({ ...state, loading: true });
try {
const user = await Parse.User.become(localStorage.getItem("accesstoken"));
let _usss = user.toJSON();
localStorage.setItem("UserInformation", JSON.stringify(_usss));
localStorage.setItem("username", _usss.name);
localStorage.setItem("accesstoken", _usss.sessionToken);
let _user = user.toJSON();
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_usss.ProfilePic) {
localStorage.setItem("profileImg", _usss.ProfilePic);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
let userRoles = [];
let userSettings = appInfo.settings;
if (localStorage.getItem("userSettings")) {
let userSettings = localStorage.getItem("userSettings");
//Get Current user roles
let url = `${localStorage.getItem("baseUrl")}functions/UserGroups`;
const headers1 = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: _user.sessionToken
};
const body = { appname: "contracts" };
//Get Current user roles
let url = `${localStorage.getItem("baseUrl")}functions/UserGroups`;
const headers1 = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: _usss.sessionToken
};
let body = {
appname: localStorage.getItem("_appName")
};
await axios
.post(url, JSON.stringify(body), { headers: headers1 })
.then((axiosres) => {
const roles = axiosres.data.result;
if (roles) {
userRoles = roles;
let _currentRole = "";
if (userRoles.length > 1) {
if (
userRoles[0] ===
`${localStorage.getItem("_appName")}_appeditor`
) {
_currentRole = userRoles[1];
} else {
const rolesfiltered = userRoles.filter(
(x) => x !== "contracts_Guest"
await axios
.post(url, JSON.stringify(body), { headers: headers1 })
.then((axiosres) => {
const roles = axiosres.data.result;
if (roles && roles.length > 0) {
userRoles = roles;
let _currentRole = "";
const valuesToExclude = ["contracts_Guest", "contracts_appeditor"];
const filterRoles = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (filterRoles.length > 0) {
_currentRole = filterRoles?.[0];
let SettingsUser = userSettings;
SettingsUser.forEach(async (item) => {
if (item.role === _currentRole) {
let _role = _currentRole.replace(`${appInfo.appname}_`, "");
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem("extended_class", item.extended_class);
const currentUser = Parse.User.current();
const userSettings = appInfo.settings;
const setting = userSettings.find(
(x) => x.role === _currentRole
);
if (rolesfiltered.length > 0) {
_currentRole = rolesfiltered[0];
} else {
setThirdpartyLoader(false);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg:
"Does not have permissions to access this application!"
});
setTimeout(function () {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: ""
});
}, 2000);
}
// _currentRole = userRoles[0];
}
} else {
_currentRole = userRoles[0];
}
if (_currentRole && _currentRole !== "contracts_Guest") {
let SettingsUser = JSON.parse(userSettings);
SettingsUser.forEach(async (item) => {
if (item.role === _currentRole) {
let _role = _currentRole.replace(`${appInfo.appname}_`, "");
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem("extended_class", item.extended_class);
const currentUser = Parse.User.current();
const userSettings = appInfo.settings;
const setting = userSettings.find(
(x) => x.role === _currentRole
);
const redirectUrl =
location?.state?.from ||
`/${setting.pageType}/${setting.pageId}`;
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
}).then(
async (result) => {
let tenentInfo = [];
const results = [result];
if (results) {
let extendedInfo_stringify = JSON.stringify(results);
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);
}
});
localStorage.setItem("showpopup", true);
localStorage.setItem("PageLanding", item.pageId);
localStorage.setItem("defaultmenuid", item.menuId);
localStorage.setItem("pageType", item.pageType);
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);
}
});
localStorage.setItem("PageLanding", setting.pageId);
localStorage.setItem(
"defaultmenuid",
setting.menuId
);
localStorage.setItem("pageType", setting.pageType);
if (isEnableSubscription) {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
const redirectUrl =
location?.state?.from ||
`/${setting.pageType}/${setting.pageId}`;
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
}).then(
async (result) => {
let tenentInfo = [];
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) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName: x.TenantId.TenantName || ""
};
tenentInfo.push(obj);
}
});
localStorage.setItem("showpopup", true);
localStorage.setItem("PageLanding", item.pageId);
localStorage.setItem("defaultmenuid", item.menuId);
localStorage.setItem("pageType", item.pageType);
navigate("/");
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
let obj = {
tenentId: x.TenantId.objectId,
tenentName: x.TenantId.TenantName || ""
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
"TenantId",
x.TenantId.objectId
);
const res = await fetchSubscription();
const billingDate = res.billingDate;
const freeplan = res.plan;
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`);
}
} else {
navigate(`/subscription`);
}
} else {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
tenentInfo.push(obj);
}
}
} else {
});
localStorage.setItem("PageLanding", setting.pageId);
localStorage.setItem("defaultmenuid", setting.menuId);
localStorage.setItem("pageType", setting.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.get("Company"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const billingDate = "";
if (billingDate) {
const res = await fetchSubscription();
const billingDate = res.billingDate;
const freeplan = res.plan;
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`);
}
} else {
navigate(`/subscription`);
}
} else {
@@ -918,41 +826,95 @@ function Login() {
navigate(redirectUrl);
}
}
},
(error) => {
} else {
localStorage.setItem("PageLanding", setting.pageId);
localStorage.setItem("defaultmenuid", setting.menuId);
localStorage.setItem("pageType", setting.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.get("Company"),
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const billingDate = "";
if (billingDate) {
navigate(`/subscription`);
}
} else {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
}
},
(error) => {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "You don`t have access to this application!"
});
setTimeout(function () {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "You don`t have access to this application!"
alertMsg: ""
});
setTimeout(function () {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: ""
});
}, 2000);
localStorage.setItem("accesstoken", null);
console.error("Error while fetching Follow", error);
}
);
}
});
} else {
setState({ ...state, loading: false });
handleCloseModal();
}
}, 2000);
localStorage.setItem("accesstoken", null);
console.error("Error while fetching Follow", error);
}
);
}
});
} else {
console.log("User Role Not Found.");
setState({ ...state, loading: false });
handleCloseModal();
setThirdpartyLoader(false);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg:
"Does not have permissions to access this application!"
});
setTimeout(function () {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: ""
});
}, 2000);
}
})
.catch((err) => {
setState({ ...state, loading: false });
console.log("err", err);
});
}
} else {
console.log("User Role Not Found.");
setThirdpartyLoader(false);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "Does not have permissions to access this application!"
});
setTimeout(function () {
setState({
...state,
alertType: "danger",
alertMsg: ""
});
}, 2000);
}
})
.catch((err) => {
setState({ ...state, loading: false });
console.log("err", err);
});
} catch (error) {
setState({ ...state, loading: false });
console.log("err", error);
+1 -1
View File
@@ -265,7 +265,7 @@ const ManageSign = () => {
<div className="relative">
<div>
{image ? (
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box">
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden">
<img
alt="signature"
src={image}
+13 -13
View File
@@ -1582,6 +1582,19 @@ function PdfRequestFiles() {
)}
{!isCompleted?.message && (
<div className="flex mt-4 gap-1 px-[15px]">
<button
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
>
<i
className="fa-light fa-print"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
onClick={() =>
@@ -1600,19 +1613,6 @@ function PdfRequestFiles() {
Certificate
</span>
</button>
<button
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
>
<i
className="fa-light fa-print"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary"
+17 -4
View File
@@ -16,7 +16,7 @@ const listItemStyle = {
const PlanSubscriptions = () => {
const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(false);
const [yearlyVisible, setYearlyVisible] = useState(true);
const [isLoader, setIsLoader] = useState(true);
const extUser =
localStorage.getItem("Extand_Class") &&
@@ -132,9 +132,22 @@ const PlanSubscriptions = () => {
<div>
<span className="text-3xl">
{item.currency && <small>{item.currency}</small>}
{yearlyVisible
? item?.yearlyPrice
: item.monthlyPrice}
{yearlyVisible ? (
<>
{item?.yearlyPrice.includes("<") ? (
<div
className="inline-block"
dangerouslySetInnerHTML={{
__html: item?.yearlyPrice
}}
/>
) : (
<span>{item?.yearlyPrice}</span>
)}
</>
) : (
item.monthlyPrice
)}
</span>
<p className="font-semibold pt-2 text-sm">
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
+365
View File
@@ -0,0 +1,365 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { useLocation } from "react-router-dom";
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 Team", "Active"];
// const actions = [
// {
// btnId: "1231",
// hoverLabel: "Edit",
// btnColor: "op-btn-primary",
// btnIcon: "fa-light fa-pen",
// redirectUrl: "draftDocument",
// action: "redirect"
// }
// ];
const TeamList = () => {
const recordperPage = 10;
const [teamList, setTeamList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isModal, setIsModal] = useState(false);
const location = useLocation();
const isDashboard =
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
const [currentPage, setCurrentPage] = useState(1);
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 = () => {
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
const pages = [];
const totalPages = Math.ceil(teamList.length / recordperPage);
if (totalPages <= totalPageNumbers) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
const leftSiblingIndex = Math.max(currentPage - 1, 1);
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
const showLeftDots = leftSiblingIndex > 2;
const showRightDots = rightSiblingIndex < totalPages - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPages;
if (!showLeftDots && showRightDots) {
let leftItemCount = 3;
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
pages.push(...leftRange);
pages.push("...");
pages.push(totalPages);
} else if (showLeftDots && !showRightDots) {
let rightItemCount = 3;
let rightRange = Array.from(
{ length: rightItemCount },
(_, i) => totalPages - rightItemCount + i + 1
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...rightRange);
} else if (showLeftDots && showRightDots) {
let middleRange = Array.from(
{ length: 3 },
(_, i) => leftSiblingIndex + i
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...middleRange);
pages.push("...");
pages.push(lastPageIndex);
}
}
return pages;
};
const pageNumbers = getPaginationRange();
useEffect(() => {
fetchTeamList();
}, []);
async function fetchTeamList() {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const teamCls = new Parse.Query("contracts_Departments");
teamCls.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: extUser.OrganizationId.objectId
});
teamCls.descending("createdAt");
const teamRes = await teamCls.find();
if (teamCls.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamRes));
setTeamList(_teamRes);
}
} catch (err) {
console.log("Err in fetch teamlist", err);
setIsAlert({ type: "danger", msg: "Something went wrong." });
} finally {
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500);
setIsLoader(false);
}
}
const handleFormModal = () => {
setIsModal(!isModal);
};
// Get current list
const indexOfLastDoc = currentPage * recordperPage;
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
const currentList = teamList?.slice(indexOfFirstDoc, indexOfLastDoc);
const handleClose = () => {
setIsActiveModal({});
};
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
// const handleActionBtn = (act, item) => {
// // if (act.action === "delete") {
// // setIsDeleteModal({ [item.objectId]: true });
// // }
// };
const handleToggleBtn = (team) => {
setIsActiveModal({ [team.objectId]: true });
};
const handleToggleSubmit = async (team) => {
const index = teamList.findIndex((obj) => obj.objectId === team.objectId);
if (index !== -1) {
setIsActiveModal({});
setIsActLoader({ [team.objectId]: true });
const newArray = [...teamList];
const IsActive = newArray[index].IsActive;
newArray[index] = { ...newArray[index], IsActive: !IsActive };
setTeamList(newArray);
try {
const teamCls = new Parse.Object("contracts_Departments");
teamCls.id = team.objectId;
teamCls.set("IsActive", !IsActive);
await teamCls.save();
setIsAlert({
type: "success",
msg: "Team disabled successfully."
});
} catch (err) {
setIsAlert({ type: "danger", msg: "something went wrong." });
console.log("err in disable team", err);
} finally {
setIsActLoader({});
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
}
}
};
const handleTeamInfo = (team) => {
setTeamList((prev) => [team, ...prev]);
};
return (
<div className="relative">
{isLoader && (
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
{Object.keys(isActLoader)?.length > 0 && (
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
<Loader />
</div>
)}
{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">
Teams{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"Teams"} />
</span>
</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]">
{teamList?.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>
{item?.Name !== "All Users" && (
<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={"Team status"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to disable this team?
</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
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
className={`${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm w-[50px]`}
>
<i className={act.btnIcon}></i>
</button>
))}
</td> */}
</tr>
))}
</>
)}
</tbody>
</table>
</div>
<div className="op-join flex flex-wrap items-center p-2">
{teamList.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>
))}
{teamList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
</button>
)}
</div>
{teamList?.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 Team"}
isOpen={isModal}
handleClose={handleFormModal}
>
<AddDepartment
handleTeamInfo={handleTeamInfo}
closePopup={handleFormModal}
/>
</ModalUi>
</div>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAMS"} price={"20"} />
</div>
)}
</div>
);
};
export default TeamList;
+387
View File
@@ -0,0 +1,387 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { useLocation } from "react-router-dom";
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", "Email", "Phone", "Role", "Team", "Active"];
// const actions = [];
const UserList = () => {
const [userList, setUserList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isModal, setIsModal] = useState(false);
const location = useLocation();
const isDashboard =
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
const [currentPage, setCurrentPage] = useState(1);
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
const getPaginationRange = () => {
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
const pages = [];
const totalPages = Math.ceil(userList.length / recordperPage);
if (totalPages <= totalPageNumbers) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
const leftSiblingIndex = Math.max(currentPage - 1, 1);
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
const showLeftDots = leftSiblingIndex > 2;
const showRightDots = rightSiblingIndex < totalPages - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPages;
if (!showLeftDots && showRightDots) {
let leftItemCount = 3;
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
pages.push(...leftRange);
pages.push("...");
pages.push(totalPages);
} else if (showLeftDots && !showRightDots) {
let rightItemCount = 3;
let rightRange = Array.from(
{ length: rightItemCount },
(_, i) => totalPages - rightItemCount + i + 1
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...rightRange);
} else if (showLeftDots && showRightDots) {
let middleRange = Array.from(
{ length: 3 },
(_, i) => leftSiblingIndex + i
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...middleRange);
pages.push("...");
pages.push(lastPageIndex);
}
}
return pages;
};
const pageNumbers = getPaginationRange();
useEffect(() => {
fetchUserList();
}, []);
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
});
const _userRes = JSON.parse(JSON.stringify(res));
setUserList(_userRes);
} catch (err) {
console.log("Err in fetch userlist", err);
setIsAlert({ type: "danger", msg: "Something went wrong." });
} finally {
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500);
setIsLoader(false);
}
}
const handleFormModal = () => {
setIsModal(!isModal);
};
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
// const handleActionBtn = (act, item) => {
// if (act.action === "delete") {
// setIsDeleteModal({ [item.objectId]: true });
// }
// };
const handleUserData = (userData) => {
setUserList((prev) => [userData, ...prev]);
};
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
// if no data available it will show hyphen "-"
const formatRow = (row) => {
if (Array.isArray(row)) {
let updateArr = row.map((x) => x.Name);
return updateArr.join(", ");
} else if (typeof row === "object" && row !== null) {
return row?.Name || "-";
} else {
return "-";
}
};
const handleClose = () => {
setIsActiveModal({});
};
const handleToggleSubmit = async (user) => {
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
if (index !== -1) {
setIsActiveModal({});
setIsActLoader({ [user.objectId]: true });
const newArray = [...userList];
const IsDisabled = newArray[index]?.IsDisabled;
newArray[index] = { ...newArray[index], IsDisabled: !IsDisabled };
setUserList(newArray);
try {
const extUser = new Parse.Object("contracts_Users");
extUser.id = user.objectId;
extUser.set("IsDisabled", !IsDisabled);
await extUser.save();
setIsAlert({
type: "success",
msg: "User disabled successfully."
});
} catch (err) {
setIsAlert({ type: "danger", msg: "something went wrong." });
console.log("err in disable team", err);
} finally {
setIsActLoader({});
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
}
}
};
const handleToggleBtn = (user) => {
setIsActiveModal({ [user.objectId]: true });
};
return (
<div className="relative">
{isLoader && (
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
{Object.keys(isActLoader)?.length > 0 && (
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
<Loader />
</div>
)}
{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">
Users{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"users from Teams"} />
</span>
</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>
)}
</td>
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
className={`${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm`}
>
<i className={act.btnIcon}></i>
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete User"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this user?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</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>
</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={"TEAMS"} price={"20"} />
</div>
)}
</div>
);
};
export default UserList;
@@ -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>
+230 -54
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";
@@ -24,6 +25,9 @@ import ReactQuill from "react-quill";
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();
@@ -52,6 +56,10 @@ const ReportTable = (props) => {
const [templateDeatils, setTemplateDetails] = useState({});
const [placeholders, setPlaceholders] = useState([]);
const [isLoader, setIsLoader] = useState({});
const [isShareWith, setIsShareWith] = useState({});
const [departmentList, setDepartmentList] = useState([]);
const [selectedDepartments, setSelectedDepartments] = useState([]);
const onChange = (selectedOptions) => setSelectedDepartments(selectedOptions);
// const [selectedPublicRole, setSelectedPublicRole] = useState("");
// const [isCelebration, setIsCelebration] = useState(false);
// const [currentLists, setCurrentLists] = useState([]);
@@ -59,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
@@ -125,9 +136,37 @@ const ReportTable = (props) => {
// below useEffect reset currenpage to 1 if user change route
useEffect(() => {
checkTourStatus();
fetchDepartmentList();
return () => setCurrentPage(1);
// eslint-disable-next-line
}, []);
// `fetchDepartmentList` is used to fetch department list for share with functionality
const fetchDepartmentList = async () => {
if (props.ReportName === "Templates") {
try {
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const department = new Parse.Query("contracts_Departments");
department.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: extUser.OrganizationId.objectId
});
department.equalTo("IsActive", true);
const departmentRes = await department.find();
if (departmentRes.length > 0) {
const _departmentRes = JSON.parse(JSON.stringify(departmentRes));
const formatedList = _departmentRes.map((x) => ({
label: x.Name,
value: x.objectId
}));
setDepartmentList(formatedList);
}
} catch (err) {
console.log("Err in fetch top level departmentlist", err);
}
}
};
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
@@ -158,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,
@@ -191,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;
@@ -205,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,
@@ -221,7 +271,7 @@ const ReportTable = (props) => {
const res = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}classes/${localStorage.getItem("_appName")}_Document`,
)}classes/contracts_Document`,
data,
{
headers: {
@@ -282,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") {
@@ -297,6 +347,20 @@ const ReportTable = (props) => {
setIsResendMail({ [item.objectId]: true });
} 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) => ({
label: x.Name,
value: x.objectId
}));
setSelectedDepartments(formatedList);
}
setIsShareWith({ [item.objectId]: true });
}
};
// Get current list
@@ -915,6 +979,41 @@ const ReportTable = (props) => {
// setTimeout(() => setIsAlert(false), 1500);
// };
// `handleShareWith` is used to save departments in sharedWith field
const handleShareWith = async (e, template) => {
e.preventDefault();
e.stopPropagation();
setIsShareWith({});
setActLoader({ [template.objectId]: true });
try {
const templateCls = new Parse.Object("contracts_Template");
templateCls.id = template.objectId;
const departmentArr = selectedDepartments.map((x) => ({
__type: "Pointer",
className: "contracts_Departments",
objectId: x.value
}));
templateCls.set("SharedWith", departmentArr);
const res = await templateCls.save();
if (res) {
setIsAlert(true);
setAlertMsg({
type: "success",
message: "Template Shared successfully."
});
}
} catch (err) {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Something went wrong, Please try again later."
});
} finally {
setActLoader({});
setTimeout(() => setIsAlert(false), 1500);
}
};
return (
<div className="relative">
{Object.keys(actLoader)?.length > 0 && (
@@ -1253,55 +1352,132 @@ 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="fixed right-14 top-auto z-[70] 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">
{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
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={"TEAMS"}
price={"20"}
/>
</>
)}
</div>
</div>
)}
{isViewShare[item.objectId] && (
<ModalUi
isOpen
@@ -1,13 +1,16 @@
import React from "react";
import { useNavigate } from "react-router-dom";
const SubscribeCard = () => {
const SubscribeCard = ({ plan, price }) => {
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>
<p>$29.99/month: Sign 100 docs using API. Only $0.15/doc after that.</p>
<h2 className="op-card-title">Upgrade to {plan ? plan : "PRO"} Plan</h2>
<p>
${price ? price : "29.99"}/month: Sign 100 docs using API. Only
$0.15/doc after that.
</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
+2 -2
View File
@@ -143,13 +143,13 @@ export const updateMailCount = async extUserId => {
await contractUser.save(null, { useMasterKey: true });
} else {
// Create new entry if not found
const ContractsUsers = Parse.Object.extend('contracts_users');
const ContractsUsers = Parse.Object.extend('contracts_Users');
const newContractUser = new ContractsUsers();
newContractUser.set('EmailCount', 1);
await newContractUser.save(null, { useMasterKey: true });
}
} catch (error) {
console.log('Error updating EmailCount in contracts_users: ' + error.message);
console.log('Error updating EmailCount in contracts_Users: ' + error.message);
}
};
+8 -1
View File
@@ -44,13 +44,18 @@ import GetPublicUserName from './parsefunction/GetPublicUserName.js';
import GetPublicTemplate from './parsefunction/GetPublicTemplate.js';
import ssoSignin from './parsefunction/ssoSignin.js';
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);
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);
@@ -98,3 +103,5 @@ Parse.Cloud.define('getpublicusername', GetPublicUserName);
Parse.Cloud.define('getpublictemplate', GetPublicTemplate);
Parse.Cloud.define('ssosign', ssoSignin);
Parse.Cloud.define('isextenduser', isextenduser);
Parse.Cloud.define('getuserbyorg', getUserByOrg);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
@@ -3,7 +3,9 @@
//-- Export Modules
import 'dotenv/config.js';
import axios from 'axios';
const appId = process.env.APP_ID;
const masterKey = process.env.MASTER_KEY;
const serverUrl = process.env.SERVER_URL;
export async function addUserToGroups(request) {
try {
var roleName = request.params.roleName;
@@ -36,11 +38,11 @@ export async function addUserToGroups(request) {
function getAccessType(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/classes/w_appinfo?where={"appname":"' + appName + '"}',
url: serverUrl + '/classes/w_appinfo?where={"appname":"' + appName + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': masterKey,
},
};
@@ -77,33 +79,37 @@ export async function addUserToGroups(request) {
} else {
//--function to get the userid from session token
function getuserid(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/users/me',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
};
try {
return new Promise(function (resolve, reject) {
const options = {
url: serverUrl + '/users/me',
method: 'get',
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
};
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
} catch (err) {
console.log('err ', err);
}
}
var userData = await getuserid(request);
if (userData.objectId == undefined) {
@@ -112,7 +118,7 @@ export async function addUserToGroups(request) {
var chkuserid = userData.objectId;
//console.log("chkuserid "+chkuserid);
var url =
process.env.SERVER_URL +
serverUrl +
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
chkuserid +
'"},"name": {"$regex": "' +
@@ -126,7 +132,7 @@ export async function addUserToGroups(request) {
url: url,
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Application-Id': appId,
},
};
@@ -174,26 +180,26 @@ export async function addUserToGroups(request) {
//--after validation call adduserToRole function
async function adduserToRole() {
var roleNam = roleName;
var roleid = await getroleobjId(roleNam);
console.log('roleid');
console.log(roleid);
var response = await adduserid(roleid);
/*process.stdin.resume();
// listen to the event
process.on('SIGTERM', () => {
process.emit('cleanup');
})*/
return response;
try {
var roleNam = roleName;
var roleid = await getroleobjId(roleNam);
console.log('roleid');
console.log(roleid);
var response = await adduserid(roleid);
return response;
} catch (err) {
console.log('err in addusertorole', err);
}
}
//--function to get the role objId
function getroleobjId(roleNam) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles?where={"name":"' + roleNam + '"}',
url: serverUrl + '/roles?where={"name":"' + roleNam + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Application-Id': appId,
},
};
axios(options)
@@ -225,11 +231,11 @@ export async function addUserToGroups(request) {
function adduserid(roleid) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles/' + roleid,
url: serverUrl + '/roles/' + roleid,
method: 'PUT',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': masterKey,
'Content-Type': 'application/json',
},
data: user,
@@ -0,0 +1,30 @@
export default async function DepartmentsAftersave(req) {
if (!req.original) {
try {
const Ancestors = req.object.get('Ancestors');
let updatedAncestors;
if (Ancestors && Ancestors.length > 0) {
updatedAncestors = [
...Ancestors,
{
__type: 'Pointer',
className: 'contracts_Departments',
objectId: req.object.id,
},
];
} else {
updatedAncestors = [
{
__type: 'Pointer',
className: 'contracts_Departments',
objectId: req.object.id,
},
];
}
req.object.set('Ancestors', updatedAncestors);
await req.object.save(null, { useMasterKey: true });
} catch (err) {
console.log('Err in department aftersave', err);
}
}
}
@@ -17,13 +17,13 @@ async function DocumentBeforesave(request) {
await contractUser.save(null, { useMasterKey: true });
} else {
// Create new entry if not found
const ContractsUsers = Parse.Object.extend('contracts_users');
const ContractsUsers = Parse.Object.extend('contracts_Users');
const newContractUser = new ContractsUsers();
newContractUser.set('DocumentCount', 1);
await newContractUser.save(null, { useMasterKey: true });
}
} catch (error) {
console.log('Error updating document count in contracts_users: ' + error.message);
console.log('Error updating document count in contracts_Users: ' + error.message);
}
if (document?.get('Signers') && document.get('Signers').length > 0) {
document.set('DocSentAt', new Date());
@@ -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 Users');
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);
}
}
}
@@ -15,13 +15,13 @@ async function TemplateBeforeSave(request) {
await contractUser.save(null, { useMasterKey: true });
} else {
// Create new entry if not found
const ContractsUsers = Parse.Object.extend('contracts_users');
const ContractsUsers = Parse.Object.extend('contracts_Users');
const newContractUser = new ContractsUsers();
newContractUser.set('TemplateCount', 1);
await newContractUser.save(null, { useMasterKey: true });
}
} catch (error) {
console.log('Error updating template count in contracts_users: ' + error.message);
console.log('Error updating template count in contracts_Users: ' + error.message);
}
}
} catch (err) {
@@ -8,7 +8,7 @@ export default async function getReport(request) {
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const masterKey = process.env.MASTER_KEY;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
@@ -23,12 +23,49 @@ export default async function getReport(request) {
if (json) {
const { params, keys } = json;
const orderBy = '-updatedAt';
const strParams = JSON.stringify(params);
const strKeys = keys.join();
let strParams = JSON.stringify(params);
if (reportId == '6TeaPr321t') {
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])
);
strParams = JSON.stringify({
...params,
$or: [
{ SharedWith: { $in: departmentArr } },
{
ExtUserPtr: {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
},
},
],
});
} else {
strParams = JSON.stringify({
...params,
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: userId,
},
});
}
}
}
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
'X-Parse-Master-Key': masterKey,
};
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
const res = await axios.get(url, { headers: headers });
@@ -0,0 +1,27 @@
export default async function getUserByOrg(req) {
const OrganizationId = req.params.organizationId;
const orgPtr = {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: OrganizationId,
};
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.include('DepartmentIds');
extUser.equalTo('OrganizationId', orgPtr);
const userRes = await extUser.first({ useMasterKey: true });
if (userRes.length > 0) {
const _userRes = JSON.parse(JSON.stringify(userRes));
return _userRes;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('err in getuserlist', err);
throw err;
}
}
}
@@ -0,0 +1,28 @@
export default async function getUserListByOrg(req) {
const OrganizationId = req.params.organizationId;
const orgPtr = {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: OrganizationId,
};
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('OrganizationId', orgPtr);
extUser.include('DepartmentIds');
extUser.descending('createdAt');
const userRes = await extUser.find({ useMasterKey: true });
if (userRes.length > 0) {
const _userRes = JSON.parse(JSON.stringify(userRes));
return _userRes;
} else {
return [];
}
} catch (err) {
console.log('err in getuserlist', err);
throw err;
}
}
}
@@ -18,8 +18,7 @@ export default async function getapitoken(request) {
}
}
} catch (err) {
console.log('Err', err);
console.log('err', err);
console.log('Err in getapitoken', err);
if (err.code == 209) {
return { error: 'Invalid session token' };
} else {
@@ -318,11 +318,6 @@ export default function reportJson(id, userId) {
params: {
Type: { $ne: 'Folder' },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
keys: [
'Name',
@@ -335,6 +330,7 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Placeholders',
'IsPublic',
'SharedWith.Name',
],
};
default:
@@ -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', {
+2 -5
View File
@@ -204,10 +204,7 @@ app.use('/v1', v1);
// Parse Server plays nicely with the rest of your web routes
app.get('/', function (req, res) {
// res.statusCode = 200;
// res.setHeader('Content-Type', 'text/plain');
// res.end('I dream of being a website. Please star the parse-server repo on GitHub!');
res.status(200).send('open-sign-server is running !!!');
res.status(200).send('opensign-server is running !!!');
});
if (!process.env.TESTING) {
@@ -217,7 +214,7 @@ if (!process.env.TESTING) {
httpServer.keepAliveTimeout = 100000; // in milliseconds
httpServer.headersTimeout = 100000; // in milliseconds
httpServer.listen(port, '0.0.0.0', function () {
console.log('parse-server-example running on port ' + port + '.');
console.log('opensign-server running on port ' + port + '.');
const isWindows = process.platform === 'win32';
// console.log('isWindows', isWindows);
+124 -128
View File
@@ -29,8 +29,8 @@
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^6.9.13",
"parse": "^5.1.0",
"nodemailer": "^6.9.14",
"parse": "^5.2.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^7.1.0",
"parse-server-api-mail-adapter": "^3.1.1",
@@ -39,13 +39,12 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.24.7",
"eslint": "^9.3.0",
"eslint": "^9.6.0",
"jasmine": "^5.1.0",
"mongodb-runner": "^5.6.2",
"nodemon": "^3.1.3",
"nodemon": "^3.1.4",
"nyc": "15.1.0",
"prettier": "^3.3.2",
"uglify-js": "^3.17.4"
"prettier": "^3.3.2"
},
"engines": {
"node": ">=12.22.10 <21"
@@ -1637,6 +1636,19 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz",
"integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==",
"dependencies": {
"@eslint/object-schema": "^2.1.4",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
@@ -1671,9 +1683,17 @@
}
},
"node_modules/@eslint/js": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz",
"integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==",
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz",
"integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/object-schema": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
"integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -2072,19 +2092,6 @@
"node": ">=12"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
"integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
"dependencies": {
"@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
"engines": {
"node": ">=10.10.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2100,7 +2107,8 @@
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"peer": true
},
"node_modules/@humanwhocodes/retry": {
"version": "0.3.0",
@@ -4111,9 +4119,9 @@
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
"integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"bin": {
"acorn": "bin/acorn"
},
@@ -5463,15 +5471,15 @@
}
},
"node_modules/eslint": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz",
"integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==",
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz",
"integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/config-array": "^0.17.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "9.3.0",
"@humanwhocodes/config-array": "^0.13.0",
"@eslint/js": "9.6.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
@@ -5482,8 +5490,8 @@
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.0.1",
"eslint-visitor-keys": "^4.0.0",
"espree": "^10.0.1",
"esquery": "^1.4.2",
"espree": "^10.1.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
@@ -5509,7 +5517,7 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
"url": "https://eslint.org/donate"
}
},
"node_modules/eslint-scope": {
@@ -5656,11 +5664,11 @@
}
},
"node_modules/espree": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
"integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
"integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
"dependencies": {
"acorn": "^8.11.3",
"acorn": "^8.12.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.0.0"
},
@@ -7139,10 +7147,27 @@
"resolved": "https://registry.npmjs.org/intersect/-/intersect-1.0.1.tgz",
"integrity": "sha512-qsc720yevCO+4NydrJWgEWKccAQwTOvj2m73O/VBA6iUL2HGZJ9XqBiyraNrBXX/W1IAjdpXdRZk24sq8TzBRg=="
},
"node_modules/ip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/ip-address/node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/ip-address/node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
@@ -8745,17 +8770,17 @@
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ=="
},
"node_modules/nodemailer": {
"version": "6.9.13",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz",
"integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==",
"version": "6.9.14",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz",
"integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nodemon": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.3.tgz",
"integrity": "sha512-m4Vqs+APdKzDFpuaL9F9EVOF85+h070FnkHVEoU4+rmT6Vw0bmNl7s61VEkY/cJkL7RCv1p4urnUDUMrS5rk2w==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz",
"integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
@@ -9144,15 +9169,15 @@
}
},
"node_modules/parse": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse/-/parse-5.1.0.tgz",
"integrity": "sha512-46gVRe1JHsh21Ht0/Ko6PeMDl6wELLMYxnZPFD6iZm2EWsWnzi2txNGE6PvnIv+G7yOufZIOD0BCZLYOFl3toA==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse/-/parse-5.2.0.tgz",
"integrity": "sha512-FoD3kcLAQCw/2J1984sl3GUBzbHE2tA9mUcyw/EBWZ46WVZTzV+kjnA5tttXyzN4uodt21wSluzjbGnyLqreKw==",
"dependencies": {
"@babel/runtime-corejs3": "7.23.2",
"@babel/runtime-corejs3": "7.24.6",
"idb-keyval": "6.2.1",
"react-native-crypto-js": "1.0.0",
"uuid": "9.0.1",
"ws": "8.16.0",
"uuid": "10.0.0",
"ws": "8.17.1",
"xmlhttprequest": "1.8.0"
},
"engines": {
@@ -9491,18 +9516,6 @@
"semver": "bin/semver.js"
}
},
"node_modules/parse-server/node_modules/@babel/runtime-corejs3": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.6.tgz",
"integrity": "sha512-tbC3o8uHK9xMgMsvUm9qGqxVpbv6yborMBLbDteHIc7JDNHsTV0vDMQ5j1O1NXvO+BDELtL9KgoWYaUVIVGt8w==",
"dependencies": {
"core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/parse-server/node_modules/@eslint/eslintrc": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
@@ -9638,12 +9651,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"peer": true
},
"node_modules/parse-server/node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"optional": true
},
"node_modules/parse-server/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -9875,47 +9882,11 @@
}
}
},
"node_modules/parse-server/node_modules/parse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse/-/parse-5.2.0.tgz",
"integrity": "sha512-FoD3kcLAQCw/2J1984sl3GUBzbHE2tA9mUcyw/EBWZ46WVZTzV+kjnA5tttXyzN4uodt21wSluzjbGnyLqreKw==",
"dependencies": {
"@babel/runtime-corejs3": "7.24.6",
"idb-keyval": "6.2.1",
"react-native-crypto-js": "1.0.0",
"uuid": "10.0.0",
"ws": "8.17.1",
"xmlhttprequest": "1.8.0"
},
"engines": {
"node": ">=18 <21"
},
"optionalDependencies": {
"crypto-js": "4.2.0"
}
},
"node_modules/parse-server/node_modules/parse/node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/parse-server/node_modules/path-to-regexp": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
},
"node_modules/parse-server/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/parse-server/node_modules/semver": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
@@ -10002,16 +9973,33 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/parse/node_modules/@babel/runtime-corejs3": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.6.tgz",
"integrity": "sha512-tbC3o8uHK9xMgMsvUm9qGqxVpbv6yborMBLbDteHIc7JDNHsTV0vDMQ5j1O1NXvO+BDELtL9KgoWYaUVIVGt8w==",
"dependencies": {
"core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/parse/node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"optional": true
},
"node_modules/parse/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/parse/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -10020,6 +10008,26 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/parse/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -11242,15 +11250,15 @@
}
},
"node_modules/socks": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
"integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
"dependencies": {
"ip": "^2.0.0",
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.13.0",
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
@@ -11941,18 +11949,6 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"dev": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/unbzip2-stream": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+5 -6
View File
@@ -38,8 +38,8 @@
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^6.9.13",
"parse": "^5.1.0",
"nodemailer": "^6.9.14",
"parse": "^5.2.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^7.1.0",
"parse-server-api-mail-adapter": "^3.1.1",
@@ -49,13 +49,12 @@
"type": "module",
"devDependencies": {
"@babel/eslint-parser": "^7.24.7",
"eslint": "^9.3.0",
"eslint": "^9.6.0",
"jasmine": "^5.1.0",
"mongodb-runner": "^5.6.2",
"nodemon": "^3.1.3",
"nodemon": "^3.1.4",
"nyc": "15.1.0",
"prettier": "^3.3.2",
"uglify-js": "^3.17.4"
"prettier": "^3.3.2"
},
"engines": {
"node": ">=12.22.10 <21"