From 2c715a77a2d0a2a8ddd03379ec9723cf926c5cc0 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Thu, 27 Jun 2024 19:00:55 +0530 Subject: [PATCH 01/10] feat: add departmentlist and userlist page --- apps/OpenSign/src/App.js | 5 + apps/OpenSign/src/components/AddUser.js | 180 +++++--- apps/OpenSign/src/json/menuJson.js | 16 + apps/OpenSign/src/pages/DepartmentList.js | 262 ++++++++++++ apps/OpenSign/src/pages/Login.js | 390 ++++++++---------- apps/OpenSign/src/pages/Managesign.js | 2 +- apps/OpenSign/src/pages/UserList.js | 278 +++++++++++++ apps/OpenSignServer/Utils.js | 4 +- apps/OpenSignServer/cloud/main.js | 4 + .../cloud/parsefunction/DocumentBeforesave.js | 4 +- .../cloud/parsefunction/TemplateBeforesave.js | 4 +- .../cloud/parsefunction/getUserByOrgId.js | 26 ++ .../cloud/parsefunction/getUserList.js | 26 ++ .../cloud/parsefunction/getapitoken.js | 3 +- 14 files changed, 914 insertions(+), 290 deletions(-) create mode 100644 apps/OpenSign/src/pages/DepartmentList.js create mode 100644 apps/OpenSign/src/pages/UserList.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js create mode 100644 apps/OpenSignServer/cloud/parsefunction/getUserList.js diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 70e707b01..1fce1146d 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -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 DepartmentList from "./pages/DepartmentList"; +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,9 @@ function App() { )} }> + } /> + } /> + } diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 088e9e786..19a233d3c 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import Parse from "parse"; import axios from "axios"; import Title from "./Title"; @@ -6,19 +6,44 @@ import Alert from "../primitives/Alert"; import Loader from "../primitives/Loader"; const AddUser = (props) => { - const [name, setName] = useState(""); - const [phone, setPhone] = useState(""); - const [email, setEmail] = useState(""); + const [formdata, setFormdata] = useState({ + name: "", + phone: "", + email: "", + department: "", + role: "" + }); const [isLoader, setIsLoader] = useState(false); const [isUserExist, setIsUserExist] = useState(false); + const [departmentList, setDepartmentList] = useState([]); const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); + useEffect(() => { + getDepartmentList(); + }, []); + + const getDepartmentList = async () => { + const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + console.log("extUser ", extUser); + const department = new Parse.Query("contracts_Departments"); + department.equalTo("OrganizationId", { + __type: "Pointer", + className: "contracts_Organizations", + objectId: extUser.OrganizationId.objectId + }); + const departmentRes = await department.find(); + if (departmentRes.length > 0) { + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + setDepartmentList(_departmentRes); + } + }; const checkUserExist = async () => { + const user = Parse.User.current(); try { const res = await Parse.Cloud.run("getUserDetails", { - email: email, - userId: Parse.User.current().id + email: user.get("email"), + userId: user.id }); if (res) { return true; @@ -43,14 +68,16 @@ const AddUser = (props) => { }, 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_User"); if (localStorage.getItem("TenantId")) { - contactQuery.set("TenantId", { + extUser.set("TenantId", { __type: "Pointer", className: "partners_Tenant", objectId: localStorage.getItem("TenantId") @@ -60,12 +87,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.email); + if (formdata.phone) { + _user.set("phone", formdata.phone); } const user = await _user.save(); @@ -83,29 +110,24 @@ const AddUser = (props) => { }; 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(); } @@ -114,15 +136,19 @@ const AddUser = (props) => { } 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 user = Parse.User.current(); + const params = { email: user.get("email") }; const userRes = await Parse.Cloud.run("getUserId", params); const roleurl = `${parseBaseUrl}functions/AddUserToRole`; const headers = { @@ -137,12 +163,12 @@ const AddUser = (props) => { }; 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,16 +179,10 @@ 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(); } @@ -170,9 +190,13 @@ const AddUser = (props) => { props.handleUserData(parseData); } setIsLoader(false); - setName(""); - setPhone(""); - setEmail(""); + setFormdata({ + name: "", + email: "", + phone: "", + department: "", + role: "" + }); } } } catch (err) { @@ -185,13 +209,20 @@ const AddUser = (props) => { // Define a function to handle the "add yourself" checkbox const handleReset = () => { - setName(""); - setPhone(""); - setEmail(""); + setFormdata({ + name: "", + email: "", + phone: "", + department: "", + role: "" + }); + }; + const handleChange = (e) => { + setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value })); }; return ( -
+
{isUserExist && <Alert type="danger">User already exists!</Alert>} {isLoader && ( @@ -201,7 +232,7 @@ const AddUser = (props) => { )} <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" @@ -213,8 +244,8 @@ const AddUser = (props) => { <input type="text" id="name" - value={name} - onChange={(e) => setName(e.target.value)} + 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" /> @@ -230,8 +261,8 @@ const AddUser = (props) => { <input type="email" id="email" - value={email} - onChange={(e) => setEmail(e.target.value)} + 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" /> @@ -242,29 +273,48 @@ const AddUser = (props) => { className="block text-xs text-gray-700 font-semibold" > Phone - <span className="text-[red] text-[13px]"> *</span> + {/* <span className="text-[red] text-[13px]"> *</span> */} </label> <input type="text" id="phone" - value={phone} - onChange={(e) => setPhone(e.target.value)} - required + 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="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" + <div className="mb-3"> + <label + htmlFor="phone" + className="block text-xs text-gray-700 font-semibold" > + Department + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <select + value={formdata.department} + onChange={(e) => handleChange(e)} + id="department" + className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" + > + <option>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="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 </div> diff --git a/apps/OpenSign/src/json/menuJson.js b/apps/OpenSign/src/json/menuJson.js index c9b576221..2735fbcf6 100644 --- a/apps/OpenSign/src/json/menuJson.js +++ b/apps/OpenSign/src/json/menuJson.js @@ -154,6 +154,22 @@ const sidebarList = [ pageType: "webhook", description: "", objectId: "" + }, + { + icon: "fa-light fa-building-memo", + title: "Departments", + target: "_self", + pageType: "", + description: "", + objectId: "departments" + }, + { + icon: "fa-light fa-building-memo", + title: "Users", + target: "_self", + pageType: "", + description: "", + objectId: "users" } ] } diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js new file mode 100644 index 000000000..afd05f337 --- /dev/null +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -0,0 +1,262 @@ +import React, { useEffect, useState } from "react"; +import Parse from "parse"; +import Alert from "../primitives/Alert"; +import Loader from "../primitives/Loader"; +import { useLocation, useNavigate } from "react-router-dom"; +import Tooltip from "../primitives/Tooltip"; +import ModalUi from "../primitives/ModalUi"; +import pad from "../assets/images/pad.svg"; + +const heading = ["Sr.No", "Name"]; +const actions = []; + +const DepartmentList = () => { + const recordperPage = 10; + const [departmentList, setDepartmentList] = useState([]); + const [isLoader, setIsLoader] = useState(false); + const [isModal, setIsModal] = useState(false); + const navigate = useNavigate(); + const location = useLocation(); + const isDashboard = + location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; + const [currentPage, setCurrentPage] = useState(1); + const [isDeleteModal, setIsDeleteModal] = useState(false); + const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); + 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(departmentList / 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(() => { + fetchDepartmentList(); + }, []); + async function fetchDepartmentList() { + try { + setIsLoader(true); + const organization = JSON.parse(localStorage.getItem("Extand_Class")); + const department = new Parse.Query("contracts_Departments"); + department.equalTo("OrganizationId", organization[0].objectId); + const departmentRes = await department.find(); + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + setDepartmentList(_departmentRes); + } catch (err) { + console.log("Err ", err); + } finally { + setIsLoader(false); + } + } + const handleFormModal = () => { + setIsModal(!isModal); + }; + // Get current list + const indexOfLastDoc = currentPage * recordperPage; + const indexOfFirstDoc = indexOfLastDoc - recordperPage; + const currentList = departmentList?.slice(indexOfFirstDoc, indexOfLastDoc); + const handleDelete = () => {}; + const handleClose = () => {}; + + // Change page + const paginateFront = () => setCurrentPage(currentPage + 1); + const paginateBack = () => setCurrentPage(currentPage - 1); + const handleActionBtn = () => {}; + + return ( + <div className="relative"> + {isLoader && ( + <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30"> + <Loader /> + </div> + )} + <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> + {isAlert && <Alert type={isAlert.type}>{isAlert.message}</Alert>} + + <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> + <div className="font-light"> + department list{" "} + <span className="text-xs md:text-[13px] font-normal"> + <Tooltip message={"department list"} /> + </span> + </div> + <div className="cursor-pointer" onClick={() => handleFormModal()}> + <i className="fa-light fa-square-plus text-accent text-[40px]"></i> + </div> + </div> + <div className={` 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> + ))} + + {actions?.length > 0 && ( + <th className="px-4 py-2 text-transparent pointer-events-none"> + Action + </th> + )} + </tr> + </thead> + <tbody className="text-[12px]"> + {departmentList?.length > 0 && ( + <> + {currentList.map((item, index) => ( + <tr className="border-y-[1px]" key={index}> + {heading.includes("Sr.No") && ( + <th className="px-4 py-2">{startIndex + index + 1}</th> + )} + <td className="px-4 py-2 font-semibold">{item?.Name} </td> + <td className="px-4 py-2 ">{item?.Email || "-"}</td> + <td className="px-4 py-2">{item?.Phone || "-"}</td> + <td className="px-3 py-2 text-white grid grid-cols-2"> + {actions?.length > 0 && + actions.map((act, index) => ( + <button + key={index} + onClick={() => handleActionBtn(act, item)} + 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 Contact"} + handleClose={handleClose} + > + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + Are you sure you want to delete this contact? + </div> + <hr className="bg-[#ccc] mt-4 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleDelete(item)} + className="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"> + {departmentList.length > recordperPage && ( + <button + onClick={() => paginateBack()} + className="op-join-item op-btn op-btn-sm" + > + Prev + </button> + )} + {pageNumbers.map((x, i) => ( + <button + key={i} + onClick={() => setCurrentPage(x)} + disabled={x === "..."} + className={`${ + x === currentPage ? "op-btn-active" : "" + } op-join-item op-btn op-btn-sm`} + > + {x} + </button> + ))} + {departmentList.length > recordperPage && ( + <button + onClick={() => paginateFront()} + className="op-join-item op-btn op-btn-sm" + > + Next + </button> + )} + </div> + {departmentList?.length <= 0 && ( + <div + className={`${ + isDashboard ? "h-[317px]" : "" + } flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`} + > + <div className="w-[60px] h-[60px] overflow-hidden"> + <img + className="w-full h-full object-contain" + src={pad} + alt="img" + /> + </div> + <div className="text-sm font-semibold">No Data Available</div> + </div> + )} + </div> + </div> + ); +}; + +export default DepartmentList; diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index d788dbc07..68172336f 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -110,193 +110,115 @@ 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")}_`, - "" + const currentUser = Parse.User.current(); + await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }) + .then((extUser) => { + if (roles) { + userRoles = roles; + let _currentRole = ""; + if (userRoles.length > 0) { + _currentRole = userRoles.find( + (x) => x === extUser.get("UserRole") ); - 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) => { - 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); - } - }); - if (tenentInfo.length) { - dispatch( - showTenant( - tenentInfo[0].tenentName || "" - ) - ); - localStorage.setItem( - "TenantName", - tenentInfo[0].tenentName || "" - ); + } + userSettings.forEach(async (element) => { + const redirectUrl = + location?.state?.from || + `/${element.pageType}/${element.pageId}`; + if (element.role === _currentRole) { + let _role = _currentRole.replace( + "contracts_", + "" + ); + localStorage.setItem("_user_role", _role); + // Get TenentID from Extendend Class + localStorage.setItem( + "extended_class", + element.extended_class + ); + let tenentInfo = []; + const results = [extUser]; + if (results) { + let extendedInfo_stringify = + JSON.stringify(results); + localStorage.setItem( + "Extand_Class", + extendedInfo_stringify + ); + let extendedInfo = JSON.parse( + extendedInfo_stringify + ); + if (extendedInfo.length > 1) { + extendedInfo.forEach((x) => { + if (x.TenantId) { + let obj = { + tenentId: x.TenantId.objectId, + tenentName: + x.TenantId.TenantName || "" + }; + tenentInfo.push(obj); } + }); + if (tenentInfo.length) { + dispatch( + showTenant( + tenentInfo[0].tenentName || "" + ) + ); + localStorage.setItem( + "TenantName", + tenentInfo[0].tenentName || "" + ); + } - localStorage.setItem("showpopup", true); - localStorage.setItem( - "PageLanding", - element.pageId - ); - localStorage.setItem( - "defaultmenuid", - element.menuId - ); - localStorage.setItem( - "pageType", - element.pageType - ); - setState({ ...state, loading: false }); - navigate("/"); - } else { - extendedInfo.forEach((x) => { - if (x.TenantId) { - let obj = { - tenentId: x.TenantId.objectId, - tenentName: - x.TenantId.TenantName || "" - }; - localStorage.setItem( - "TenantId", - x.TenantId.objectId - ); - tenentInfo.push(obj); - } - }); - if (tenentInfo.length) { - dispatch( - showTenant( - tenentInfo[0].tenentName || "" - ) - ); - localStorage.setItem( - "TenantName", - tenentInfo[0].tenentName || "" - ); - } - localStorage.setItem( - "PageLanding", - element.pageId - ); - localStorage.setItem( - "defaultmenuid", - element.menuId - ); - localStorage.setItem( - "pageType", - element.pageType - ); - setState({ ...state, loading: false }); - if (isEnableSubscription) { - const LocalUserDetails = { - name: results[0].get("Name"), - email: results[0].get("Email"), - phone: results[0]?.get("Phone") || "", - company: results[0].get("Company") + localStorage.setItem("showpopup", true); + localStorage.setItem( + "PageLanding", + element.pageId + ); + localStorage.setItem( + "defaultmenuid", + element.menuId + ); + localStorage.setItem( + "pageType", + element.pageType + ); + setState({ ...state, loading: false }); + navigate("/"); + } else { + extendedInfo.forEach((x) => { + if (x.TenantId) { + let obj = { + tenentId: x.TenantId.objectId, + tenentName: + x.TenantId.TenantName || "" }; localStorage.setItem( - "userDetails", - JSON.stringify(LocalUserDetails) + "TenantId", + x.TenantId.objectId ); - const res = await fetchSubscription(); - const freeplan = res.plan; - const billingDate = res.billingDate; - if (freeplan === "freeplan") { - navigate(redirectUrl); - } else if (billingDate) { - if ( - new Date(billingDate) > new Date() - ) { - localStorage.removeItem( - "userDetails" - ); - // Redirect to the appropriate URL after successful login - navigate(redirectUrl); - } else { - navigate(`/subscription`, { - replace: true - }); - } - } else { - navigate(`/subscription`, { - replace: true - }); - } - } else { - // Redirect to the appropriate URL after successful login - navigate(redirectUrl); + tenentInfo.push(obj); } + }); + if (tenentInfo.length) { + dispatch( + showTenant( + tenentInfo[0].tenentName || "" + ) + ); + localStorage.setItem( + "TenantName", + tenentInfo[0].tenentName || "" + ); } - } else { localStorage.setItem( "PageLanding", element.pageId @@ -312,17 +234,35 @@ function Login() { setState({ ...state, loading: false }); if (isEnableSubscription) { const LocalUserDetails = { - name: _user.name, - email: email, - phone: _user?.phone || "" - // company: results.get("Company"), + name: results[0].get("Name"), + email: results[0].get("Email"), + 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 freeplan = res.plan; + const billingDate = res.billingDate; + if (freeplan === "freeplan") { + navigate(redirectUrl); + } else if (billingDate) { + if ( + new Date(billingDate) > new Date() + ) { + localStorage.removeItem( + "userDetails" + ); + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } + } else { navigate(`/subscription`, { replace: true }); @@ -332,28 +272,56 @@ function Login() { navigate(redirectUrl); } } - }, - (error) => { - const payload = { - sessionToken: user.getSessionToken() - }; - handleSubmitbtn(payload); - console.error( - "Error while fetching Follow", - error + } else { + localStorage.setItem( + "PageLanding", + element.pageId ); + localStorage.setItem( + "defaultmenuid", + element.menuId + ); + localStorage.setItem( + "pageType", + element.pageType + ); + setState({ ...state, loading: false }); + if (isEnableSubscription) { + const LocalUserDetails = { + name: _user.name, + email: email, + phone: _user?.phone || "" + // company: results.get("Company"), + }; + localStorage.setItem( + "userDetails", + JSON.stringify(LocalUserDetails) + ); + const billingDate = ""; + if (billingDate) { + navigate(`/subscription`, { + replace: true + }); + } + } else { + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } } - ); - } - }); - } else { - setState({ ...state, loading: false }); - setIsModal(true); - } - } 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 +442,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 +461,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 @@ -767,10 +728,7 @@ function Login() { userRoles = roles; let _currentRole = ""; if (userRoles.length > 1) { - if ( - userRoles[0] === - `${localStorage.getItem("_appName")}_appeditor` - ) { + if (userRoles[0] === "contracts_appeditor") { _currentRole = userRoles[1]; } else { const rolesfiltered = userRoles.filter( diff --git a/apps/OpenSign/src/pages/Managesign.js b/apps/OpenSign/src/pages/Managesign.js index 187254b35..8b90fb14e 100644 --- a/apps/OpenSign/src/pages/Managesign.js +++ b/apps/OpenSign/src/pages/Managesign.js @@ -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} diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js new file mode 100644 index 000000000..f0b2b695c --- /dev/null +++ b/apps/OpenSign/src/pages/UserList.js @@ -0,0 +1,278 @@ +import React, { useEffect, useState } from "react"; +import Parse from "parse"; +import Alert from "../primitives/Alert"; +import Loader from "../primitives/Loader"; +import { useLocation, useNavigate } from "react-router-dom"; +import ModalUi from "../primitives/ModalUi"; +import pad from "../assets/images/pad.svg"; +import Tooltip from "../primitives/Tooltip"; +import AddSigner from "../components/AddSigner"; +import AddUser from "../components/AddUser"; +const heading = ["Sr.No", "Name", "Email", "Phone", "Departments"]; +const actions = []; +const UserList = () => { + const [userList, setUserList] = useState([]); + const [isLoader, setIsLoader] = useState(false); + const [isModal, setIsModal] = useState(false); + const navigate = useNavigate(); + const location = useLocation(); + const isDashboard = + location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; + const [currentPage, setCurrentPage] = useState(1); + const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); + const [isDeleteModal, setIsDeleteModal] = 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); + const organization = JSON.parse(localStorage.getItem("Extand_Class")); + const res = await Parse.Cloud.run("getuserlist", { + organizationId: organization[0].objectId + }); + + console.log("res ", res); + const _userRes = JSON.parse(JSON.stringify(res)); + setUserList(_userRes); + } catch (err) { + console.log("Err ", err); + } finally { + setIsLoader(false); + } + } + const handleFormModal = () => { + setIsModal(!isModal); + }; + + const handleDelete = () => {}; + const handleClose = () => {}; + + // Change page + const paginateFront = () => setCurrentPage(currentPage + 1); + const paginateBack = () => setCurrentPage(currentPage - 1); + const handleActionBtn = () => {}; + + const handleUserData = (userData) => { + console.log("userData", userData); + }; + return ( + <div className="relative"> + {isLoader && ( + <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30"> + <Loader /> + </div> + )} + <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> + {isAlert && <Alert type={isAlert.type}>{isAlert.msg}</Alert>} + + <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> + <div className="font-light"> + User list{" "} + <span className="text-xs md:text-[13px] font-normal"> + <Tooltip message={"user list from departments"} /> + </span> + </div> + <div className="cursor-pointer" onClick={() => handleFormModal()}> + <i className="fa-light fa-square-plus text-accent text-[40px]"></i> + </div> + </div> + <div className={` 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> + ))} + + {actions?.length > 0 && ( + <th className="px-4 py-2 text-transparent pointer-events-none"> + Action + </th> + )} + </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-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 Contact"} + handleClose={handleClose} + > + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + Are you sure you want to delete this contact? + </div> + <hr className="bg-[#ccc] mt-4 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleDelete(item)} + className="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} + > + {/* <AddSigner + handleUserData={handleUserData} + closePopup={handleFormModal} + /> */} + <AddUser + handleUserData={handleUserData} + closePopup={handleFormModal} + /> + </ModalUi> + </div> + </div> + ); +}; + +export default UserList; diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 3076085c3..dcfa1b707 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -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); } }; diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 59a903b1b..31a72b969 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -44,6 +44,8 @@ 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 getUserByOrgId from './parsefunction/getuserbyOrgId.js'; +import getUserList from './parsefunction/getUserList.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); @@ -98,3 +100,5 @@ Parse.Cloud.define('getpublicusername', GetPublicUserName); Parse.Cloud.define('getpublictemplate', GetPublicTemplate); Parse.Cloud.define('ssosign', ssoSignin); Parse.Cloud.define('isextenduser', isextenduser); +Parse.Cloud.define('getuserbyorgid', getUserByOrgId); +Parse.Cloud.define('getuserlist', getUserList); diff --git a/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js index e7f251ca4..3d9f85454 100644 --- a/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js +++ b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js @@ -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()); diff --git a/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js b/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js index b2ba6e3d3..79ac1c209 100644 --- a/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js +++ b/apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js @@ -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) { diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js b/apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js new file mode 100644 index 000000000..5ac2c7067 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js @@ -0,0 +1,26 @@ +export default async function getUserByOrgId(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); + 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; + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserList.js b/apps/OpenSignServer/cloud/parsefunction/getUserList.js new file mode 100644 index 000000000..afdae2933 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/getUserList.js @@ -0,0 +1,26 @@ +export default async function getUserList(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); + 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; + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getapitoken.js b/apps/OpenSignServer/cloud/parsefunction/getapitoken.js index 570679f57..192a1d0fd 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getapitoken.js +++ b/apps/OpenSignServer/cloud/parsefunction/getapitoken.js @@ -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 { From 7b2b71fea5110f64b600bbb99a2ca3265735a550 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 28 Jun 2024 12:48:50 +0530 Subject: [PATCH 02/10] feat: add user from, show deparments, users in pages --- apps/OpenSign/src/components/AddDepartment.js | 370 ++++++++++++++++++ apps/OpenSign/src/components/AddUser.js | 64 ++- apps/OpenSign/src/pages/DepartmentList.js | 69 +++- apps/OpenSign/src/pages/UserList.js | 61 ++- apps/OpenSignServer/cloud/main.js | 8 +- .../{getUserByOrgId.js => getUserByOrg.js} | 3 +- .../{getUserList.js => getUserListByOrg.js} | 3 +- 7 files changed, 520 insertions(+), 58 deletions(-) create mode 100644 apps/OpenSign/src/components/AddDepartment.js rename apps/OpenSignServer/cloud/parsefunction/{getUserByOrgId.js => getUserByOrg.js} (89%) rename apps/OpenSignServer/cloud/parsefunction/{getUserList.js => getUserListByOrg.js} (88%) diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js new file mode 100644 index 000000000..9a3592283 --- /dev/null +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -0,0 +1,370 @@ +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"; + +const AddDepartment = (props) => { + const [formdata, setFormdata] = useState({ + name: "", + phone: "", + email: "", + department: "", + role: "" + }); + const [isLoader, setIsLoader] = useState(false); + const [isUserExist, setIsUserExist] = useState(false); + const [departmentList, setDepartmentList] = useState([]); + const role = ["OrgAdmin", "Manager", "User", "Guest"]; + const parseBaseUrl = localStorage.getItem("baseUrl"); + const parseAppId = localStorage.getItem("parseAppId"); + + useEffect(() => { + getDepartmentList(); + }, []); + + const getDepartmentList = async () => { + const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + console.log("extUser ", extUser); + const department = new Parse.Query("contracts_Departments"); + department.equalTo("OrganizationId", { + __type: "Pointer", + className: "contracts_Organizations", + objectId: extUser.OrganizationId.objectId + }); + department.doesNotExist("DepartmentParentId"); + department.doesNotExist("Ancestors"); + const departmentRes = await department.find(); + if (departmentRes.length > 0) { + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + setDepartmentList(_departmentRes); + } + }; + const checkUserExist = async () => { + const user = Parse.User.current(); + try { + const res = await Parse.Cloud.run("getUserDetails", { + email: user.get("email"), + userId: user.id + }); + if (res) { + return true; + } else { + return false; + } + } catch (err) { + console.log("err", err); + } + }; + console.log("formdata", formdata); + // Define a function to handle form submission + 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); + setIsLoader(false); + setTimeout(() => { + setIsUserExist(false); + }, 1000); + } else { + try { + 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}`); + 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")) { + extUser.set("TenantId", { + __type: "Pointer", + className: "partners_Tenant", + objectId: localStorage.getItem("TenantId") + }); + } + + try { + const _users = Parse.Object.extend("User"); + const _user = new _users(); + _user.set("name", formdata.name); + _user.set("username", formdata.email); + _user.set("email", formdata.email); + _user.set("password", formdata.email); + if (formdata.phone) { + _user.set("phone", formdata.phone); + } + + const user = await _user.save(); + if (user) { + const roleurl = `${parseBaseUrl}functions/AddUserToRole`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + sessionToken: localStorage.getItem("accesstoken") + }; + const body = { + appName: "contracts", + roleName: "contracts_" + formdata.role, + userId: user.id + }; + await axios.post(roleurl, body, { headers: headers }); + const currentUser = Parse.User.current(); + extUser.set( + "CreatedBy", + Parse.User.createWithoutData(currentUser.id) + ); + + 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); + + extUser.setACL(acl); + + const res = await extUser.save(); + + const parseData = JSON.parse(JSON.stringify(res)); + + if (props.closePopup) { + props.closePopup(); + } + if (props.handleUserData) { + props.handleUserData(parseData); + } + + setIsLoader(false); + setFormdata({ + name: "", + email: "", + phone: "", + department: "", + role: "" + }); + } + } catch (err) { + console.log("err ", err); + if (err.code === 202) { + const user = Parse.User.current(); + const params = { email: user.get("email") }; + const userRes = await Parse.Cloud.run("getUserId", params); + const roleurl = `${parseBaseUrl}functions/AddUserToRole`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": parseAppId, + sessionToken: localStorage.getItem("accesstoken") + }; + const body = { + appName: "contracts", + roleName: "contracts_" + formdata.role, + userId: userRes.id + }; + await axios.post(roleurl, body, { headers: headers }); + const currentUser = Parse.User.current(); + extUser.set( + "CreatedBy", + Parse.User.createWithoutData(currentUser.id) + ); + + extUser.set("UserId", { + __type: "Pointer", + className: "_User", + objectId: userRes.id + }); + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + acl.setPublicWriteAccess(true); + acl.setReadAccess(currentUser.id, true); + acl.setWriteAccess(currentUser.id, true); + + extUser.setACL(acl); + const res = await extUser.save(); + + const parseData = JSON.parse(JSON.stringify(res)); + if (props.closePopup) { + props.closePopup(); + } + if (props.handleUserData) { + props.handleUserData(parseData); + } + setIsLoader(false); + setFormdata({ + name: "", + email: "", + phone: "", + department: "", + role: "" + }); + } + } + } catch (err) { + console.log("err", err); + setIsLoader(false); + alert("something went wrong!"); + } + } + }; + + // Define a function to handle the "add yourself" checkbox + const handleReset = () => { + setFormdata({ + name: "", + email: "", + phone: "", + department: "", + role: "" + }); + }; + 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 User"} /> + {isUserExist && <Alert type="danger">User already exists!</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="email" + className="block text-xs text-gray-700 font-semibold" + > + Email + <span className="text-[red] text-[13px]"> *</span> + </label> + <input + type="email" + 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="phone" + className="block text-xs text-gray-700 font-semibold" + > + Phone + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <input + type="text" + name="phone" + 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" + > + Department + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <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" + > + <option disabled>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 + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <select + value={formdata.role} + onChange={(e) => handleChange(e)} + name="role" + className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" + > + {role.length > 0 && + role.map((x) => ( + <option key={x} value={x}> + {x} + </option> + ))} + </select> + </div> + <div className="flex items-center mt-3 gap-2 text-white"> + <button type="submit" className="op-btn op-btn-primary"> + Submit + </button> + <div + type="button" + onClick={() => handleReset()} + className="op-btn op-btn-secondary" + > + Reset + </div> + </div> + </form> + </div> + </div> + ); +}; + +export default AddDepartment; diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 19a233d3c..6d3d611df 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -16,6 +16,7 @@ const AddUser = (props) => { const [isLoader, setIsLoader] = useState(false); const [isUserExist, setIsUserExist] = useState(false); const [departmentList, setDepartmentList] = useState([]); + const role = ["OrgAdmin", "Manager", "User", "Guest"]; const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); @@ -54,10 +55,13 @@ const AddUser = (props) => { console.log("err", err); } }; + console.log("formdata", formdata); // Define a function to handle form submission 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) { @@ -74,7 +78,21 @@ const AddUser = (props) => { extUser.set("Phone", formdata.phone); } extUser.set("Email", formdata.email); - extUser.set("UserRole", "contracts_User"); + extUser.set("UserRole", `contracts_${formdata.role}`); + 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")) { extUser.set("TenantId", { @@ -104,8 +122,8 @@ 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 }); @@ -157,8 +175,8 @@ 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 }); @@ -200,7 +218,7 @@ const AddUser = (props) => { } } } catch (err) { - // console.log("err", err); + console.log("err", err); setIsLoader(false); alert("something went wrong!"); } @@ -226,7 +244,7 @@ const AddUser = (props) => { <Title title={"Add User"} /> {isUserExist && <Alert type="danger">User already exists!</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> )} @@ -243,7 +261,7 @@ const AddUser = (props) => { </label> <input type="text" - id="name" + name="name" value={formdata.name} onChange={(e) => handleChange(e)} required @@ -260,7 +278,7 @@ const AddUser = (props) => { </label> <input type="email" - id="email" + name="email" value={formdata.email} onChange={(e) => handleChange(e)} required @@ -277,7 +295,7 @@ const AddUser = (props) => { </label> <input type="text" - id="phone" + name="phone" value={formdata.phone} onChange={(e) => handleChange(e)} // required @@ -295,10 +313,10 @@ const AddUser = (props) => { <select value={formdata.department} onChange={(e) => handleChange(e)} - id="department" + name="department" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" > - <option>select</option> + <option disabled>select</option> {departmentList.length > 0 && departmentList.map((x) => ( <option key={x.objectId} value={x.objectId}> @@ -307,6 +325,28 @@ const AddUser = (props) => { ))} </select> </div> + <div className="mb-3"> + <label + htmlFor="phone" + className="block text-xs text-gray-700 font-semibold" + > + Role + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <select + value={formdata.role} + onChange={(e) => handleChange(e)} + name="role" + className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" + > + {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 diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js index afd05f337..22e6875d7 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -2,20 +2,36 @@ import React, { useEffect, useState } from "react"; import Parse from "parse"; import Alert from "../primitives/Alert"; import Loader from "../primitives/Loader"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import Tooltip from "../primitives/Tooltip"; import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; -const heading = ["Sr.No", "Name"]; -const actions = []; +const heading = ["Sr.No", "Name", "Actions"]; +const actions = [ + { + btnId: "1231", + hoverLabel: "Edit", + btnColor: "op-btn-primary", + btnIcon: "fa-light fa-pen", + redirectUrl: "draftDocument", + action: "redirect" + }, + { + btnId: "2142", + hoverLabel: "Delete", + btnColor: "op-btn-secondary", + btnIcon: "fa-light fa-trash", + redirectUrl: "", + action: "delete" + } +]; const DepartmentList = () => { const recordperPage = 10; const [departmentList, setDepartmentList] = useState([]); const [isLoader, setIsLoader] = useState(false); const [isModal, setIsModal] = useState(false); - const navigate = useNavigate(); const location = useLocation(); const isDashboard = location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; @@ -27,7 +43,7 @@ const DepartmentList = () => { const getPaginationRange = () => { const totalPageNumbers = 7; // Adjust this value to show more/less page numbers const pages = []; - const totalPages = Math.ceil(departmentList / recordperPage); + const totalPages = Math.ceil(departmentList.length / recordperPage); if (totalPages <= totalPageNumbers) { for (let i = 1; i <= totalPages; i++) { pages.push(i); @@ -82,15 +98,25 @@ const DepartmentList = () => { async function fetchDepartmentList() { try { setIsLoader(true); - const organization = JSON.parse(localStorage.getItem("Extand_Class")); + const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; const department = new Parse.Query("contracts_Departments"); - department.equalTo("OrganizationId", organization[0].objectId); + department.equalTo("OrganizationId", { + __type: "Pointer", + className: "contracts_Organizations", + objectId: extUser.OrganizationId.objectId + }); const departmentRes = await department.find(); - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - setDepartmentList(_departmentRes); + if (departmentRes.length > 0) { + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + setDepartmentList(_departmentRes); + } } catch (err) { - console.log("Err ", err); + console.log("Err in fetch departmentlist", err); + setIsAlert({ type: "danger", msg: "Something went wrong." }); } finally { + setTimeout(() => { + setIsAlert({ type: "success", msg: "" }); + }, 1500); setIsLoader(false); } } @@ -102,17 +128,22 @@ const DepartmentList = () => { const indexOfFirstDoc = indexOfLastDoc - recordperPage; const currentList = departmentList?.slice(indexOfFirstDoc, indexOfLastDoc); const handleDelete = () => {}; - const handleClose = () => {}; + const handleClose = () => { + setIsDeleteModal({}); + }; // Change page const paginateFront = () => setCurrentPage(currentPage + 1); const paginateBack = () => setCurrentPage(currentPage - 1); - const handleActionBtn = () => {}; - + const handleActionBtn = (act, item) => { + if (act.action === "delete") { + setIsDeleteModal({ [item.objectId]: true }); + } + }; return ( <div className="relative"> {isLoader && ( - <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30"> + <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box"> <Loader /> </div> )} @@ -156,9 +187,7 @@ const DepartmentList = () => { <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-3 py-2 text-white grid grid-cols-2"> + <td className="px-3 py-2 text-white flex flex-wrap gap-1"> {actions?.length > 0 && actions.map((act, index) => ( <button @@ -167,7 +196,7 @@ const DepartmentList = () => { title={act.hoverLabel} className={`${ act?.btnColor ? act.btnColor : "" - } op-btn op-btn-sm`} + } op-btn op-btn-sm w-[50px]`} > <i className={act.btnIcon}></i> </button> @@ -175,12 +204,12 @@ const DepartmentList = () => { {isDeleteModal[item.objectId] && ( <ModalUi isOpen - title={"Delete Contact"} + title={"Delete Department"} handleClose={handleClose} > <div className="m-[20px]"> <div className="text-lg font-normal text-black"> - Are you sure you want to delete this contact? + Are you sure you want to delete this department? </div> <hr className="bg-[#ccc] mt-4 " /> <div className="flex items-center mt-3 gap-2 text-white"> diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index f0b2b695c..1ee09b963 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -2,19 +2,17 @@ import React, { useEffect, useState } from "react"; import Parse from "parse"; import Alert from "../primitives/Alert"; import Loader from "../primitives/Loader"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import Tooltip from "../primitives/Tooltip"; -import AddSigner from "../components/AddSigner"; import AddUser from "../components/AddUser"; -const heading = ["Sr.No", "Name", "Email", "Phone", "Departments"]; +const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Departments"]; const actions = []; const UserList = () => { const [userList, setUserList] = useState([]); const [isLoader, setIsLoader] = useState(false); const [isModal, setIsModal] = useState(false); - const navigate = useNavigate(); const location = useLocation(); const isDashboard = location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; @@ -82,17 +80,19 @@ const UserList = () => { async function fetchUserList() { try { setIsLoader(true); - const organization = JSON.parse(localStorage.getItem("Extand_Class")); - const res = await Parse.Cloud.run("getuserlist", { - organizationId: organization[0].objectId + const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + const res = await Parse.Cloud.run("getuserlistbyorg", { + organizationId: extUser.OrganizationId.objectId }); - - console.log("res ", res); const _userRes = JSON.parse(JSON.stringify(res)); setUserList(_userRes); } catch (err) { - console.log("Err ", err); + console.log("Err in fetch userlist", err); + setIsAlert({ type: "danger", msg: "Something went wrong." }); } finally { + setTimeout(() => { + setIsAlert({ type: "success", msg: "" }); + }, 1500); setIsLoader(false); } } @@ -101,20 +101,39 @@ const UserList = () => { }; const handleDelete = () => {}; - const handleClose = () => {}; + const handleClose = () => { + setIsDeleteModal({}); + }; // Change page const paginateFront = () => setCurrentPage(currentPage + 1); const paginateBack = () => setCurrentPage(currentPage - 1); - const handleActionBtn = () => {}; - + const handleActionBtn = (act, item) => { + if (act.action === "delete") { + setIsDeleteModal({ [item.objectId]: true }); + } + }; const handleUserData = (userData) => { console.log("userData", userData); }; + // `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 "-"; + } + }; return ( <div className="relative"> {isLoader && ( - <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30"> + <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box"> <Loader /> </div> )} @@ -160,6 +179,12 @@ const UserList = () => { <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-3 py-2 text-white grid grid-cols-2"> {actions?.length > 0 && actions.map((act, index) => ( @@ -177,12 +202,12 @@ const UserList = () => { {isDeleteModal[item.objectId] && ( <ModalUi isOpen - title={"Delete Contact"} + 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 contact? + 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"> @@ -261,10 +286,6 @@ const UserList = () => { isOpen={isModal} handleClose={handleFormModal} > - {/* <AddSigner - handleUserData={handleUserData} - closePopup={handleFormModal} - /> */} <AddUser handleUserData={handleUserData} closePopup={handleFormModal} diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 31a72b969..b33269895 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -44,8 +44,8 @@ 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 getUserByOrgId from './parsefunction/getuserbyOrgId.js'; -import getUserList from './parsefunction/getUserList.js'; +import getUserByOrg from './parsefunction/getUserByOrg.js'; +import getUserListByOrg from './parsefunction/getUserListByOrg.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); @@ -100,5 +100,5 @@ Parse.Cloud.define('getpublicusername', GetPublicUserName); Parse.Cloud.define('getpublictemplate', GetPublicTemplate); Parse.Cloud.define('ssosign', ssoSignin); Parse.Cloud.define('isextenduser', isextenduser); -Parse.Cloud.define('getuserbyorgid', getUserByOrgId); -Parse.Cloud.define('getuserlist', getUserList); +Parse.Cloud.define('getuserbyorg', getUserByOrg); +Parse.Cloud.define('getuserlistbyorg', getUserListByOrg); diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js b/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js similarity index 89% rename from apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js rename to apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js index 5ac2c7067..060c68134 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserByOrgId.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js @@ -1,4 +1,4 @@ -export default async function getUserByOrgId(req) { +export default async function getUserByOrg(req) { const OrganizationId = req.params.organizationId; const orgPtr = { __type: 'Pointer', @@ -10,6 +10,7 @@ export default async function getUserByOrgId(req) { } 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) { diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserList.js b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js similarity index 88% rename from apps/OpenSignServer/cloud/parsefunction/getUserList.js rename to apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js index afdae2933..7c5b44f06 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserList.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js @@ -1,4 +1,4 @@ -export default async function getUserList(req) { +export default async function getUserListByOrg(req) { const OrganizationId = req.params.organizationId; const orgPtr = { __type: 'Pointer', @@ -11,6 +11,7 @@ export default async function getUserList(req) { try { const extUser = new Parse.Query('contracts_Users'); extUser.equalTo('OrganizationId', orgPtr); + extUser.include('DepartmentIds'); const userRes = await extUser.find({ useMasterKey: true }); if (userRes.length > 0) { const _userRes = JSON.parse(JSON.stringify(userRes)); From 17b9fc772e0f1c4ba6384508c2be3438c3962d7b Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 28 Jun 2024 21:12:58 +0530 Subject: [PATCH 03/10] feat: add department and add user form in reports --- apps/OpenSign/src/App.js | 3 + apps/OpenSign/src/components/AddDepartment.js | 411 +++++++----------- apps/OpenSign/src/components/AddUser.js | 8 +- apps/OpenSign/src/pages/DepartmentList.js | 131 +++--- apps/OpenSign/src/pages/UserList.js | 6 - apps/OpenSignServer/index.js | 7 +- 6 files changed, 254 insertions(+), 312 deletions(-) diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 1fce1146d..5154e248b 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -20,6 +20,7 @@ import SSOVerify from "./pages/SSOVerify"; import Loader from "./primitives/Loader"; import DepartmentList from "./pages/DepartmentList"; import UserList from "./pages/UserList"; +import AddDepartment from "./components/AddDepartment"; const DebugPdf = lazy(() => import("./pages/DebugPdf")); const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const GuestLogin = lazy(() => import("./pages/GuestLogin")); @@ -123,6 +124,8 @@ function App() { )} <Route element={<HomeLayout />}> <Route path="/departments" element={<DepartmentList />} /> + <Route path="/departmentform" element={<AddDepartment />} /> + <Route path="/users" element={<UserList />} /> <Route diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index 9a3592283..14b911924 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -1,6 +1,5 @@ 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"; @@ -8,222 +7,167 @@ import Loader from "../primitives/Loader"; const AddDepartment = (props) => { const [formdata, setFormdata] = useState({ name: "", - phone: "", - email: "", - department: "", - role: "" + department: "" }); 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", "Guest"]; - const parseBaseUrl = localStorage.getItem("baseUrl"); - const parseAppId = localStorage.getItem("parseAppId"); - + const [parentDepartments, setParentDepartments] = useState([]); + const [level, setLevel] = useState(1); useEffect(() => { getDepartmentList(); }, []); const getDepartmentList = async () => { - const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - console.log("extUser ", extUser); - const department = new Parse.Query("contracts_Departments"); - department.equalTo("OrganizationId", { - __type: "Pointer", - className: "contracts_Organizations", - objectId: extUser.OrganizationId.objectId - }); - department.doesNotExist("DepartmentParentId"); - department.doesNotExist("Ancestors"); - const departmentRes = await department.find(); - if (departmentRes.length > 0) { - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - setDepartmentList(_departmentRes); - } - }; - const checkUserExist = async () => { - const user = Parse.User.current(); try { - const res = await Parse.Cloud.run("getUserDetails", { - email: user.get("email"), - userId: user.id + 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 }); - if (res) { - return true; - } else { - return false; + department.doesNotExist("ParentId"); + department.doesNotExist("Ancestors"); + const departmentRes = await department.find(); + if (departmentRes.length > 0) { + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + // console.log("_departmentRes ", _departmentRes); + setDepartmentList(_departmentRes); } } catch (err) { - console.log("err", err); + console.log("Err in fetch top level departmentlist", err); } }; + + const fetchDepartmentsbyPtr = async (departmentPtr) => { + setLevel((prev) => prev + 1); + try { + const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + const department = new Parse.Query("contracts_Departments"); + department.equalTo("ParentId", departmentPtr); + department.equalTo("OrganizationId", { + __type: "Pointer", + className: "contracts_Organizations", + objectId: extUser.OrganizationId.objectId + }); + + const departmentRes = await department.find(); + if (departmentRes.length > 0) { + const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); + // console.log("sub", ["DD_" + level] ,_departmentRes) + const departmentName = _departmentRes?.[0]?.ParentId?.Name; + setParentDepartments((prev) => [ + ...prev, + { + ["DD_" + level]: { name: departmentName, opt: _departmentRes } + } + ]); + } + } catch (err) { + console.log("Err in fetch departmentlist", err); + } + }; + const handleDropdown = (e) => { + setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value })); + const departmentPtr = { + __type: "Pointer", + className: "contracts_Departments", + objectId: e.target.value + }; + // const index = parentDepartments.findIndex((x) => x[e.target.name]); + // setParentDepartments((prev) => prev.slice(0, index +1)); + // console.log("index ", index); + fetchDepartmentsbyPtr(departmentPtr); + }; console.log("formdata", formdata); // Define a function to handle form submission const handleSubmit = async (e) => { e.preventDefault(); e.stopPropagation(); - const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + // Extracting values except for the 'name' key + const ancestors = Object.entries(formdata) + .filter(([key]) => key !== "name") + .map(([key, value]) => value); - setIsLoader(true); - const res = await checkUserExist(); - if (res) { - setIsUserExist(true); - setIsLoader(false); - setTimeout(() => { - setIsUserExist(false); - }, 1000); - } else { - try { - const extUser = new Parse.Object("contracts_Users"); - extUser.set("Name", formdata.name); - if (formdata.phone) { - extUser.set("Phone", formdata.phone); + try { + const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + const ParentId = { + __type: "Pointer", + className: "contracts_Departments", + objectId: "" + }; + setIsLoader(true); + const department = new Parse.Query("contracts_Departments"); + department.equalTo("Name", formdata.name); + if (ancestors.length > 0) { + ParentId.objectId = ""; + 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 (ancestors.length > 0) { + newDepartment.set("ParentId", ParentId); } - extUser.set("Email", formdata.email); - extUser.set("UserRole", `contracts_${formdata.role}`); - extUser.set("DepartmentIds", [ - { - __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department - } - ]); if (localUser && localUser.OrganizationId) { - extUser.set("OrganizationId", { + newDepartment.set("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", objectId: localUser.OrganizationId.objectId }); } - - if (localStorage.getItem("TenantId")) { - extUser.set("TenantId", { - __type: "Pointer", - className: "partners_Tenant", - objectId: localStorage.getItem("TenantId") + const newdepartmentRes = await newDepartment.save(); + if (ancestors.length > 0) { + newDepartment.set("ParentId", ParentId); + props.handleDepartmentInfo({ + objectId: newdepartmentRes.id, + Name: formdata.name, + ParentId: ParentId, + Ancestors: ancestors, + IsActive: true + }); + } else { + props.handleDepartmentInfo({ + objectId: newdepartmentRes.id, + Name: formdata.name, + ParentId: "", + Ancestors: "", + IsActive: true }); } - try { - const _users = Parse.Object.extend("User"); - const _user = new _users(); - _user.set("name", formdata.name); - _user.set("username", formdata.email); - _user.set("email", formdata.email); - _user.set("password", formdata.email); - if (formdata.phone) { - _user.set("phone", formdata.phone); - } - - const user = await _user.save(); - if (user) { - const roleurl = `${parseBaseUrl}functions/AddUserToRole`; - const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId, - sessionToken: localStorage.getItem("accesstoken") - }; - const body = { - appName: "contracts", - roleName: "contracts_" + formdata.role, - userId: user.id - }; - await axios.post(roleurl, body, { headers: headers }); - const currentUser = Parse.User.current(); - extUser.set( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - - 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); - - extUser.setACL(acl); - - const res = await extUser.save(); - - const parseData = JSON.parse(JSON.stringify(res)); - - if (props.closePopup) { - props.closePopup(); - } - if (props.handleUserData) { - props.handleUserData(parseData); - } - - setIsLoader(false); - setFormdata({ - name: "", - email: "", - phone: "", - department: "", - role: "" - }); - } - } catch (err) { - console.log("err ", err); - if (err.code === 202) { - const user = Parse.User.current(); - const params = { email: user.get("email") }; - const userRes = await Parse.Cloud.run("getUserId", params); - const roleurl = `${parseBaseUrl}functions/AddUserToRole`; - const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": parseAppId, - sessionToken: localStorage.getItem("accesstoken") - }; - const body = { - appName: "contracts", - roleName: "contracts_" + formdata.role, - userId: userRes.id - }; - await axios.post(roleurl, body, { headers: headers }); - const currentUser = Parse.User.current(); - extUser.set( - "CreatedBy", - Parse.User.createWithoutData(currentUser.id) - ); - - extUser.set("UserId", { - __type: "Pointer", - className: "_User", - objectId: userRes.id - }); - const acl = new Parse.ACL(); - acl.setPublicReadAccess(true); - acl.setPublicWriteAccess(true); - acl.setReadAccess(currentUser.id, true); - acl.setWriteAccess(currentUser.id, true); - - extUser.setACL(acl); - const res = await extUser.save(); - - const parseData = JSON.parse(JSON.stringify(res)); - if (props.closePopup) { - props.closePopup(); - } - if (props.handleUserData) { - props.handleUserData(parseData); - } - setIsLoader(false); - setFormdata({ - name: "", - email: "", - phone: "", - department: "", - role: "" - }); - } + if (props.closePopup) { + props.closePopup(); } - } catch (err) { - console.log("err", err); - setIsLoader(false); - alert("something went wrong!"); + 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); } }; @@ -231,10 +175,7 @@ const AddDepartment = (props) => { const handleReset = () => { setFormdata({ name: "", - email: "", - phone: "", - department: "", - role: "" + department: { name: "", objectId: "" } }); }; const handleChange = (e) => { @@ -243,8 +184,12 @@ const AddDepartment = (props) => { return ( <div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]"> - <Title title={"Add User"} /> - {isUserExist && <Alert type="danger">User already exists!</Alert>} + <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 /> @@ -270,40 +215,7 @@ const AddDepartment = (props) => { 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" - > - Email - <span className="text-[red] text-[13px]"> *</span> - </label> - <input - type="email" - 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="phone" - className="block text-xs text-gray-700 font-semibold" - > - Phone - {/* <span className="text-[red] text-[13px]"> *</span> */} - </label> - <input - type="text" - name="phone" - 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" @@ -314,11 +226,11 @@ const AddDepartment = (props) => { </label> <select value={formdata.department} - onChange={(e) => handleChange(e)} + 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 disabled>select</option> + <option>select</option> {departmentList.length > 0 && departmentList.map((x) => ( <option key={x.objectId} value={x.objectId}> @@ -327,28 +239,33 @@ const AddDepartment = (props) => { ))} </select> </div> - <div className="mb-3"> - <label - htmlFor="phone" - className="block text-xs text-gray-700 font-semibold" - > - Role - {/* <span className="text-[red] text-[13px]"> *</span> */} - </label> - <select - value={formdata.role} - onChange={(e) => handleChange(e)} - name="role" - className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" - > - {role.length > 0 && - role.map((x) => ( - <option key={x} value={x}> - {x} + {parentDepartments?.map((x, i) => ( + <div className="mb-3" key={"DD_" + (i + 1)}> + <label + htmlFor="phone" + className="block text-xs text-gray-700 font-semibold" + > + {x["DD_" + (i + 1)]?.name} department + {/* <span className="text-[red] text-[13px]"> *</span> */} + </label> + <select + value={formdata["DD_" + (i + 1)]} + onChange={(e) => handleDropdown(e)} + name={"DD_" + (i + 1)} + className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" + > + <option>select</option> + {x["DD_" + (i + 1)]?.opt?.map((subdepartment) => ( + <option + key={subdepartment.objectId} + value={subdepartment.objectId} + > + {subdepartment.Name} </option> ))} - </select> - </div> + </select> + </div> + ))} <div className="flex items-center mt-3 gap-2 text-white"> <button type="submit" className="op-btn op-btn-primary"> Submit diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 6d3d611df..4e9e7cdaf 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -26,7 +26,6 @@ const AddUser = (props) => { const getDepartmentList = async () => { const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - console.log("extUser ", extUser); const department = new Parse.Query("contracts_Departments"); department.equalTo("OrganizationId", { __type: "Pointer", @@ -55,7 +54,6 @@ const AddUser = (props) => { console.log("err", err); } }; - console.log("formdata", formdata); // Define a function to handle form submission const handleSubmit = async (e) => { e.preventDefault(); @@ -308,13 +306,14 @@ const AddUser = (props) => { className="block text-xs text-gray-700 font-semibold" > Department - {/* <span className="text-[red] text-[13px]"> *</span> */} + <span className="text-[red] text-[13px]"> *</span> </label> <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 > <option disabled>select</option> {departmentList.length > 0 && @@ -331,13 +330,14 @@ const AddUser = (props) => { className="block text-xs text-gray-700 font-semibold" > Role - {/* <span className="text-[red] text-[13px]"> *</span> */} + <span className="text-[red] text-[13px]"> *</span> </label> <select value={formdata.role} onChange={(e) => handleChange(e)} name="role" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" + required > {role.length > 0 && role.map((x) => ( diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js index 22e6875d7..cac6b0498 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -6,26 +6,19 @@ 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"; -const heading = ["Sr.No", "Name", "Actions"]; -const actions = [ - { - btnId: "1231", - hoverLabel: "Edit", - btnColor: "op-btn-primary", - btnIcon: "fa-light fa-pen", - redirectUrl: "draftDocument", - action: "redirect" - }, - { - btnId: "2142", - hoverLabel: "Delete", - btnColor: "op-btn-secondary", - btnIcon: "fa-light fa-trash", - redirectUrl: "", - action: "delete" - } -]; +const heading = ["Sr.No", "Name", "Parent Department", "Status"]; +// const actions = [ +// { +// btnId: "1231", +// hoverLabel: "Edit", +// btnColor: "op-btn-primary", +// btnIcon: "fa-light fa-pen", +// redirectUrl: "draftDocument", +// action: "redirect" +// } +// ]; const DepartmentList = () => { const recordperPage = 10; @@ -36,7 +29,7 @@ const DepartmentList = () => { const isDashboard = location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; const [currentPage, setCurrentPage] = useState(1); - const [isDeleteModal, setIsDeleteModal] = useState(false); + const [isActiveModal, setIsActiveModal] = useState(false); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const startIndex = (currentPage - 1) * recordperPage; // user per page @@ -127,19 +120,39 @@ const DepartmentList = () => { const indexOfLastDoc = currentPage * recordperPage; const indexOfFirstDoc = indexOfLastDoc - recordperPage; const currentList = departmentList?.slice(indexOfFirstDoc, indexOfLastDoc); - const handleDelete = () => {}; + const handleClose = () => { - setIsDeleteModal({}); + 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 handleActionBtn = (act, item) => { + // // if (act.action === "delete") { + // // setIsDeleteModal({ [item.objectId]: true }); + // // } + // }; + const handleToggleBtn = (department) => { + setIsActiveModal({ [department.objectId]: true }); + }; + const handleToggleSubmit = (department) => { + const index = departmentList.findIndex( + (obj) => obj.objectId === department.objectId + ); + if (index !== -1) { + const newArray = [...departmentList]; + newArray[index] = { + ...newArray[index], + IsActive: !newArray[index].IsActive + }; + setDepartmentList(newArray); + setIsActiveModal({}); } }; + const handleDepartmentInfo = (department) => { + setDepartmentList((prev) => [department, ...prev]); + }; return ( <div className="relative"> {isLoader && ( @@ -170,12 +183,6 @@ const DepartmentList = () => { <th className="px-4 py-2">{item}</th> </React.Fragment> ))} - - {actions?.length > 0 && ( - <th className="px-4 py-2 text-transparent pointer-events-none"> - Action - </th> - )} </tr> </thead> <tbody className="text-[12px]"> @@ -186,35 +193,34 @@ const DepartmentList = () => { {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-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> - ))} - {isDeleteModal[item.objectId] && ( + <td className="px-4 py-2 font-semibold">{item?.Name}</td> + <td className="px-4 py-2 font-semibold"> + {item?.ParentId?.Name || "-"} + </td> + <td className="px-4 py-2 font-semibold"> + <label className="cursor-pointer relative block items-center mb-0"> + <input + type="checkbox" + className="op-toggle transition-all op-toggle-secondary" + checked={item?.IsActive} + onChange={() => handleToggleBtn(item)} + /> + </label> + {isActiveModal[item.objectId] && ( <ModalUi isOpen - title={"Delete Department"} + title={"Department status"} handleClose={handleClose} > <div className="m-[20px]"> <div className="text-lg font-normal text-black"> - Are you sure you want to delete this department? + Are you sure you want to disable this + department? </div> <hr className="bg-[#ccc] mt-4 " /> <div className="flex items-center mt-3 gap-2 text-white"> <button - onClick={() => handleDelete(item)} + onClick={() => handleToggleSubmit(item)} className="op-btn op-btn-primary" > Yes @@ -230,6 +236,21 @@ const DepartmentList = () => { </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> ))} </> @@ -283,6 +304,16 @@ const DepartmentList = () => { <div className="text-sm font-semibold">No Data Available</div> </div> )} + <ModalUi + title={"Add Department"} + isOpen={isModal} + handleClose={handleFormModal} + > + <AddDepartment + handleDepartmentInfo={handleDepartmentInfo} + closePopup={handleFormModal} + /> + </ModalUi> </div> </div> ); diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 1ee09b963..a0a688a7e 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -160,12 +160,6 @@ const UserList = () => { <th className="px-4 py-2">{item}</th> </React.Fragment> ))} - - {actions?.length > 0 && ( - <th className="px-4 py-2 text-transparent pointer-events-none"> - Action - </th> - )} </tr> </thead> <tbody className="text-[12px]"> diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 9fccf17cd..670b7d64c 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -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); From d46ba8f30739e671b38962ad1b9bf72549285fcb Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 1 Jul 2024 16:47:01 +0530 Subject: [PATCH 04/10] feat: add create department, user form as well as reports for them --- apps/OpenSign/src/components/AddDepartment.js | 122 ++++++------------ .../src/components/sidebar/Sidebar.js | 56 ++++++-- apps/OpenSign/src/json/menuJson.js | 16 --- apps/OpenSign/src/layout/HomeLayout.js | 2 +- apps/OpenSign/src/pages/DepartmentList.js | 45 +++++-- apps/OpenSign/src/pages/UserList.js | 119 ++++++++++++++--- apps/OpenSignServer/cloud/main.js | 2 + .../cloud/parsefunction/AddUserToRole.js | 102 ++++++++------- .../parsefunction/DepartmentsAftersave.js | 30 +++++ .../cloud/parsefunction/getUserListByOrg.js | 1 + 10 files changed, 302 insertions(+), 193 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index 14b911924..beeecb5df 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -12,8 +12,6 @@ const AddDepartment = (props) => { const [isLoader, setIsLoader] = useState(false); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const [departmentList, setDepartmentList] = useState([]); - const [parentDepartments, setParentDepartments] = useState([]); - const [level, setLevel] = useState(1); useEffect(() => { getDepartmentList(); }, []); @@ -27,12 +25,9 @@ const AddDepartment = (props) => { className: "contracts_Organizations", objectId: extUser.OrganizationId.objectId }); - department.doesNotExist("ParentId"); - department.doesNotExist("Ancestors"); const departmentRes = await department.find(); if (departmentRes.length > 0) { const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - // console.log("_departmentRes ", _departmentRes); setDepartmentList(_departmentRes); } } catch (err) { @@ -40,68 +35,47 @@ const AddDepartment = (props) => { } }; - const fetchDepartmentsbyPtr = async (departmentPtr) => { - setLevel((prev) => prev + 1); - try { - const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - const department = new Parse.Query("contracts_Departments"); - department.equalTo("ParentId", departmentPtr); - department.equalTo("OrganizationId", { - __type: "Pointer", - className: "contracts_Organizations", - objectId: extUser.OrganizationId.objectId - }); - - const departmentRes = await department.find(); - if (departmentRes.length > 0) { - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - // console.log("sub", ["DD_" + level] ,_departmentRes) - const departmentName = _departmentRes?.[0]?.ParentId?.Name; - setParentDepartments((prev) => [ - ...prev, - { - ["DD_" + level]: { name: departmentName, opt: _departmentRes } - } - ]); - } - } catch (err) { - console.log("Err in fetch departmentlist", err); - } - }; const handleDropdown = (e) => { setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value })); - const departmentPtr = { - __type: "Pointer", - className: "contracts_Departments", - objectId: e.target.value - }; - // const index = parentDepartments.findIndex((x) => x[e.target.name]); - // setParentDepartments((prev) => prev.slice(0, index +1)); - // console.log("index ", index); - fetchDepartmentsbyPtr(departmentPtr); }; - console.log("formdata", formdata); // Define a function to handle form submission const handleSubmit = async (e) => { e.preventDefault(); e.stopPropagation(); // Extracting values except for the 'name' key - const ancestors = Object.entries(formdata) - .filter(([key]) => key !== "name") - .map(([key, value]) => value); - + 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 + })), + { + __type: "Pointer", + className: "contracts_Departments", + objectId: formdata.department + } + ]; + } else { + updatedAncestors.push({ + __type: "Pointer", + className: "contracts_Departments", + objectId: formdata.department + }); + } + } try { const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - const ParentId = { - __type: "Pointer", - className: "contracts_Departments", - objectId: "" - }; setIsLoader(true); const department = new Parse.Query("contracts_Departments"); department.equalTo("Name", formdata.name); - if (ancestors.length > 0) { - ParentId.objectId = ""; + if (updatedAncestors.length > 0) { + const ParentId = updatedAncestors[updatedAncestors.length - 1]; department.equalTo("ParentId", ParentId); } if (localUser && localUser.OrganizationId) { @@ -118,8 +92,10 @@ const AddDepartment = (props) => { } else { const newDepartment = new Parse.Object("contracts_Departments"); newDepartment.set("Name", formdata.name); - if (ancestors.length > 0) { + 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", { @@ -129,13 +105,15 @@ const AddDepartment = (props) => { }); } const newdepartmentRes = await newDepartment.save(); - if (ancestors.length > 0) { - newDepartment.set("ParentId", ParentId); + if (updatedAncestors.length > 0) { + const ParentId = departmentList.find( + (x) => x.objectId === formdata.department + ); props.handleDepartmentInfo({ objectId: newdepartmentRes.id, Name: formdata.name, ParentId: ParentId, - Ancestors: ancestors, + Ancestors: updatedAncestors, IsActive: true }); } else { @@ -222,7 +200,6 @@ const AddDepartment = (props) => { className="block text-xs text-gray-700 font-semibold" > Department - {/* <span className="text-[red] text-[13px]"> *</span> */} </label> <select value={formdata.department} @@ -239,33 +216,6 @@ const AddDepartment = (props) => { ))} </select> </div> - {parentDepartments?.map((x, i) => ( - <div className="mb-3" key={"DD_" + (i + 1)}> - <label - htmlFor="phone" - className="block text-xs text-gray-700 font-semibold" - > - {x["DD_" + (i + 1)]?.name} department - {/* <span className="text-[red] text-[13px]"> *</span> */} - </label> - <select - value={formdata["DD_" + (i + 1)]} - onChange={(e) => handleDropdown(e)} - name={"DD_" + (i + 1)} - className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" - > - <option>select</option> - {x["DD_" + (i + 1)]?.opt?.map((subdepartment) => ( - <option - key={subdepartment.objectId} - value={subdepartment.objectId} - > - {subdepartment.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 diff --git a/apps/OpenSign/src/components/sidebar/Sidebar.js b/apps/OpenSign/src/components/sidebar/Sidebar.js index 8e01910df..e8b587b3f 100644 --- a/apps/OpenSign/src/components/sidebar/Sidebar.js +++ b/apps/OpenSign/src/components/sidebar/Sidebar.js @@ -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: "Departments", + target: "_self", + pageType: "", + description: "", + objectId: "departments" + }, + { + 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"> diff --git a/apps/OpenSign/src/json/menuJson.js b/apps/OpenSign/src/json/menuJson.js index 2735fbcf6..c9b576221 100644 --- a/apps/OpenSign/src/json/menuJson.js +++ b/apps/OpenSign/src/json/menuJson.js @@ -154,22 +154,6 @@ const sidebarList = [ pageType: "webhook", description: "", objectId: "" - }, - { - icon: "fa-light fa-building-memo", - title: "Departments", - target: "_self", - pageType: "", - description: "", - objectId: "departments" - }, - { - icon: "fa-light fa-building-memo", - title: "Users", - target: "_self", - pageType: "", - description: "", - objectId: "users" } ] } diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index 188d991b7..7c3073048 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -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 ? ( diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js index cac6b0498..6f3e09e17 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -8,7 +8,7 @@ import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import AddDepartment from "../components/AddDepartment"; -const heading = ["Sr.No", "Name", "Parent Department", "Status"]; +const heading = ["Sr.No", "Name", "Parent Department", "IsActive"]; // const actions = [ // { // btnId: "1231", @@ -31,6 +31,7 @@ const DepartmentList = () => { const [currentPage, setCurrentPage] = useState(1); const [isActiveModal, setIsActiveModal] = useState(false); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); + const [isActLoader, setIsActLoader] = useState({}); const startIndex = (currentPage - 1) * recordperPage; // user per page const getPaginationRange = () => { @@ -98,6 +99,7 @@ const DepartmentList = () => { className: "contracts_Organizations", objectId: extUser.OrganizationId.objectId }); + department.descending("createdAt"); const departmentRes = await department.find(); if (departmentRes.length > 0) { const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); @@ -136,18 +138,33 @@ const DepartmentList = () => { const handleToggleBtn = (department) => { setIsActiveModal({ [department.objectId]: true }); }; - const handleToggleSubmit = (department) => { + const handleToggleSubmit = async (department) => { const index = departmentList.findIndex( (obj) => obj.objectId === department.objectId ); if (index !== -1) { - const newArray = [...departmentList]; - newArray[index] = { - ...newArray[index], - IsActive: !newArray[index].IsActive - }; - setDepartmentList(newArray); setIsActiveModal({}); + setIsActLoader({ [department.objectId]: true }); + const newArray = [...departmentList]; + const IsActive = newArray[index].IsActive; + newArray[index] = { ...newArray[index], IsActive: !IsActive }; + setDepartmentList(newArray); + try { + const departmentCls = new Parse.Object("contracts_Departments"); + departmentCls.id = department.objectId; + departmentCls.set("IsActive", !IsActive); + await departmentCls.save(); + setIsAlert({ + type: "success", + msg: "Department disabled successfully." + }); + } catch (err) { + setIsAlert({ type: "danger", msg: "something went wrong." }); + console.log("err in disable department", err); + } finally { + setIsActLoader({}); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } } }; const handleDepartmentInfo = (department) => { @@ -160,9 +177,17 @@ const DepartmentList = () => { <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> + )} <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> - {isAlert && <Alert type={isAlert.type}>{isAlert.message}</Alert>} - + {isAlert.msg && ( + <Alert type={isAlert.type}> + <div className="ml-3">{isAlert.message}</div> + </Alert> + )} <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <div className="font-light"> department list{" "} diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index a0a688a7e..597fe626c 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -7,8 +7,16 @@ import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import Tooltip from "../primitives/Tooltip"; import AddUser from "../components/AddUser"; -const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Departments"]; -const actions = []; +const heading = [ + "Sr.No", + "Name", + "Email", + "Phone", + "Role", + "Departments", + "IsActive" +]; +// const actions = []; const UserList = () => { const [userList, setUserList] = useState([]); const [isLoader, setIsLoader] = useState(false); @@ -18,7 +26,8 @@ const UserList = () => { location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; const [currentPage, setCurrentPage] = useState(1); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); - const [isDeleteModal, setIsDeleteModal] = useState(false); + const [isActiveModal, setIsActiveModal] = useState({}); + const [isActLoader, setIsActLoader] = useState({}); const recordperPage = 10; const startIndex = (currentPage - 1) * recordperPage; // user per page @@ -100,21 +109,16 @@ const UserList = () => { setIsModal(!isModal); }; - const handleDelete = () => {}; - const handleClose = () => { - setIsDeleteModal({}); - }; - // 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 handleActionBtn = (act, item) => { + // if (act.action === "delete") { + // setIsDeleteModal({ [item.objectId]: true }); + // } + // }; const handleUserData = (userData) => { - console.log("userData", 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 "," @@ -130,6 +134,39 @@ const UserList = () => { 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 department", err); + } finally { + setIsActLoader({}); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } + } + }; + const handleToggleBtn = (user) => { + setIsActiveModal({ [user.objectId]: true }); + }; return ( <div className="relative"> {isLoader && ( @@ -137,9 +174,17 @@ const UserList = () => { <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> + )} <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> - {isAlert && <Alert type={isAlert.type}>{isAlert.msg}</Alert>} - + {isAlert.msg && ( + <Alert type={isAlert.type}> + <div className="ml-3">{isAlert.message}</div> + </Alert> + )} <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <div className="font-light"> User list{" "} @@ -179,7 +224,45 @@ const UserList = () => { <td className="px-4 py-2"> {formatRow(item.DepartmentIds)} </td> - <td className="px-3 py-2 text-white grid grid-cols-2"> + <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 @@ -221,7 +304,7 @@ const UserList = () => { </div> </ModalUi> )} - </td> + </td> */} </tr> ))} </> diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index b33269895..a92824195 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -46,12 +46,14 @@ 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'; // 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); // 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); diff --git a/apps/OpenSignServer/cloud/parsefunction/AddUserToRole.js b/apps/OpenSignServer/cloud/parsefunction/AddUserToRole.js index 38012b1a4..6a488ca73 100755 --- a/apps/OpenSignServer/cloud/parsefunction/AddUserToRole.js +++ b/apps/OpenSignServer/cloud/parsefunction/AddUserToRole.js @@ -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, diff --git a/apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js b/apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js new file mode 100644 index 000000000..3feb37466 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js @@ -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); + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js index 7c5b44f06..b5fa1a6ab 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js @@ -12,6 +12,7 @@ export default async function getUserListByOrg(req) { 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)); From abd7cf9b71f5d31cf857996668b1a21fac299dc2 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 2 Jul 2024 00:27:29 +0530 Subject: [PATCH 05/10] fix: duplicate entries in ancestors array and login issue --- apps/OpenSign/src/components/AddDepartment.js | 17 +- apps/OpenSign/src/components/AddUser.js | 70 +++- apps/OpenSign/src/constant/appinfo.js | 20 +- apps/OpenSign/src/pages/DepartmentList.js | 4 +- apps/OpenSign/src/pages/Form.js | 4 +- apps/OpenSign/src/pages/Login.js | 356 +++++++++--------- apps/OpenSign/src/pages/UserList.js | 6 +- 7 files changed, 265 insertions(+), 212 deletions(-) diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index beeecb5df..6f22c5073 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -49,18 +49,11 @@ const AddDepartment = (props) => { (x) => x.objectId === formdata.department )?.Ancestors; if (Ancestors && Ancestors.length > 0) { - updatedAncestors = [ - ...Ancestors.map((x) => ({ - __type: "Pointer", - className: "contracts_Departments", - objectId: x.objectId - })), - { - __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department - } - ]; + updatedAncestors = Ancestors.map((x) => ({ + __type: "Pointer", + className: "contracts_Departments", + objectId: x.objectId + })); } else { updatedAncestors.push({ __type: "Pointer", diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 4e9e7cdaf..27e7d1a83 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -4,6 +4,19 @@ 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 [formdata, setFormdata] = useState({ @@ -11,12 +24,13 @@ const AddUser = (props) => { 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", "Guest"]; + const role = ["OrgAdmin", "Manager", "User"]; const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); @@ -25,6 +39,7 @@ const AddUser = (props) => { }, []); 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", { @@ -59,14 +74,14 @@ const AddUser = (props) => { e.preventDefault(); e.stopPropagation(); const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - + console.log("formdata.department ", formdata.department); 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 { @@ -106,7 +121,7 @@ const AddUser = (props) => { _user.set("name", formdata.name); _user.set("username", formdata.email); _user.set("email", formdata.email); - _user.set("password", formdata.email); + _user.set("password", formdata.password); if (formdata.phone) { _user.set("phone", formdata.phone); } @@ -218,7 +233,9 @@ const AddUser = (props) => { } catch (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); } } }; @@ -232,15 +249,30 @@ const AddUser = (props) => { department: "", role: "" }); + if (props.closePopup) { + props.closePopup(); + } }; const handleChange = (e) => { + console.log("e", e.target.name, e.target.value); 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-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 rounded-box"> <Loader /> @@ -283,6 +315,24 @@ const AddUser = (props) => { 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 bg-base-200 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 be generated one time; make sure to copy it. + </div> + </div> <div className="mb-3"> <label htmlFor="phone" @@ -315,7 +365,7 @@ const AddUser = (props) => { className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" required > - <option disabled>select</option> + <option>select</option> {departmentList.length > 0 && departmentList.map((x) => ( <option key={x.objectId} value={x.objectId}> @@ -356,7 +406,7 @@ const AddUser = (props) => { onClick={() => handleReset()} className="op-btn op-btn-secondary" > - Reset + Cancel </div> </div> </form> diff --git a/apps/OpenSign/src/constant/appinfo.js b/apps/OpenSign/src/constant/appinfo.js index 5467b55b9..474501e20 100644 --- a/apps/OpenSign/src/constant/appinfo.js +++ b/apps/OpenSign/src/constant/appinfo.js @@ -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" diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js index 6f3e09e17..6bd204546 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -8,7 +8,7 @@ import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import AddDepartment from "../components/AddDepartment"; -const heading = ["Sr.No", "Name", "Parent Department", "IsActive"]; +const heading = ["Sr.No", "Name", "Parent Department", "Is-Active"]; // const actions = [ // { // btnId: "1231", @@ -185,7 +185,7 @@ const DepartmentList = () => { <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.message}</div> + <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]"> diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index e05dfcac7..d35d1cd23 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -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 diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 68172336f..583d065ac 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -693,182 +693,124 @@ 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] === "contracts_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; + console.log("roles ", roles); + 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]; + console.log("_currentRole ", _currentRole); + 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); + 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 { @@ -876,41 +818,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); diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 597fe626c..5f337c918 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -13,8 +13,8 @@ const heading = [ "Email", "Phone", "Role", - "Departments", - "IsActive" + "Department", + "Is-Active" ]; // const actions = []; const UserList = () => { @@ -182,7 +182,7 @@ const UserList = () => { <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.message}</div> + <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]"> From 00d900c2b2d36f2a7dff86fa5e06fa23b26a4f7b Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 2 Jul 2024 12:30:50 +0530 Subject: [PATCH 06/10] fix: department not visible realtime in users reports, remove inactive department from dropdown --- apps/OpenSign/src/components/AddDepartment.js | 1 + apps/OpenSign/src/components/AddUser.js | 41 ++- apps/OpenSignServer/package-lock.json | 252 +++++++++--------- apps/OpenSignServer/package.json | 11 +- 4 files changed, 159 insertions(+), 146 deletions(-) diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index 6f22c5073..74e5f0911 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -25,6 +25,7 @@ const AddDepartment = (props) => { 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)); diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 27e7d1a83..757117c74 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -14,7 +14,6 @@ function generatePassword(length) { for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } - return result; } @@ -47,6 +46,7 @@ const AddUser = (props) => { 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)); @@ -74,7 +74,6 @@ const AddUser = (props) => { e.preventDefault(); e.stopPropagation(); const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - console.log("formdata.department ", formdata.department); setIsLoader(true); const res = await checkUserExist(); if (res) { @@ -92,13 +91,15 @@ const AddUser = (props) => { } extUser.set("Email", formdata.email); extUser.set("UserRole", `contracts_${formdata.role}`); - extUser.set("DepartmentIds", [ - { - __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department - } - ]); + if (formdata?.department) { + extUser.set("DepartmentIds", [ + { + __type: "Pointer", + className: "contracts_Departments", + objectId: formdata.department + } + ]); + } if (localUser && localUser.OrganizationId) { extUser.set("OrganizationId", { __type: "Pointer", @@ -163,6 +164,14 @@ const AddUser = (props) => { 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); } @@ -178,8 +187,7 @@ const AddUser = (props) => { } catch (err) { console.log("err ", err); if (err.code === 202) { - const user = Parse.User.current(); - const params = { email: user.get("email") }; + const params = { email: formdata.email }; const userRes = await Parse.Cloud.run("getUserId", params); const roleurl = `${parseBaseUrl}functions/AddUserToRole`; const headers = { @@ -218,6 +226,15 @@ const AddUser = (props) => { 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); @@ -254,7 +271,6 @@ const AddUser = (props) => { } }; const handleChange = (e) => { - console.log("e", e.target.name, e.target.value); setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value })); }; @@ -389,6 +405,7 @@ const AddUser = (props) => { className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" required > + <option>select</option> {role.length > 0 && role.map((x) => ( <option key={x} value={x}> diff --git a/apps/OpenSignServer/package-lock.json b/apps/OpenSignServer/package-lock.json index e5d13f7d4..7a812dd2c 100644 --- a/apps/OpenSignServer/package-lock.json +++ b/apps/OpenSignServer/package-lock.json @@ -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", diff --git a/apps/OpenSignServer/package.json b/apps/OpenSignServer/package.json index 2948704ce..b6313da76 100644 --- a/apps/OpenSignServer/package.json +++ b/apps/OpenSignServer/package.json @@ -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" From 832e5c21815f44f62833dd58b32d6b7a9031e7d8 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 2 Jul 2024 13:25:28 +0530 Subject: [PATCH 07/10] fix: multiple user get added with same email --- apps/OpenSign/src/components/AddDepartment.js | 4 +++- apps/OpenSign/src/components/AddUser.js | 15 +++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index 74e5f0911..9b151ff3c 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -201,7 +201,9 @@ const AddDepartment = (props) => { name="department" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" > - <option>select</option> + <option defaultValue={""} value={""}> + select + </option> {departmentList.length > 0 && departmentList.map((x) => ( <option key={x.objectId} value={x.objectId}> diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 757117c74..983d7cd8a 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -54,11 +54,9 @@ const AddUser = (props) => { } }; const checkUserExist = async () => { - const user = Parse.User.current(); try { const res = await Parse.Cloud.run("getUserDetails", { - email: user.get("email"), - userId: user.id + email: formdata.email }); if (res) { return true; @@ -372,7 +370,6 @@ const AddUser = (props) => { className="block text-xs text-gray-700 font-semibold" > Department - <span className="text-[red] text-[13px]"> *</span> </label> <select value={formdata.department} @@ -381,7 +378,9 @@ const AddUser = (props) => { className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" required > - <option>select</option> + <option defaultValue={""} value={""}> + select + </option> {departmentList.length > 0 && departmentList.map((x) => ( <option key={x.objectId} value={x.objectId}> @@ -396,16 +395,16 @@ const AddUser = (props) => { className="block text-xs text-gray-700 font-semibold" > Role - <span className="text-[red] text-[13px]"> *</span> </label> <select value={formdata.role} onChange={(e) => handleChange(e)} name="role" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" - required > - <option>select</option> + <option defaultValue={""} value={""}> + select + </option> {role.length > 0 && role.map((x) => ( <option key={x} value={x}> From 8a2960511c9a3f8a60bb12fe833f3c38fc8ebfab Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 2 Jul 2024 20:54:30 +0530 Subject: [PATCH 08/10] feat: add share with functionality of template --- apps/OpenSign/src/json/ReportJson.js | 8 ++ .../src/primitives/GetReportDisplay.js | 134 +++++++++++++++++- .../cloud/parsefunction/getReport.js | 43 +++++- .../cloud/parsefunction/reportsJson.js | 6 +- 4 files changed, 182 insertions(+), 9 deletions(-) diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js index bd35f9312..a33990b46 100644 --- a/apps/OpenSign/src/json/ReportJson.js +++ b/apps/OpenSign/src/json/ReportJson.js @@ -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 won’t 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", diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 259bad27c..f8e40ac22 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -24,6 +24,7 @@ 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"; const ReportTable = (props) => { const navigate = useNavigate(); @@ -52,6 +53,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([]); @@ -125,9 +130,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 @@ -297,6 +330,16 @@ const ReportTable = (props) => { setIsResendMail({ [item.objectId]: true }); } else if (act.action === "bulksend") { handleBulkSend(item); + } else if (act.action === "sharewith") { + 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 +958,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 && ( @@ -1276,7 +1354,7 @@ const ReportTable = (props) => { )} {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 "> + <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} @@ -1302,6 +1380,60 @@ const ReportTable = (props) => { </div> ))} </div> + {isShareWith[item.objectId] && ( + <div className="op-modal op-modal-open"> + <div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative"> + <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> + Share with + </h3> + <div + className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" + onClick={() => setIsShareWith({})} + > + ✕ + </div> + <form + className="h-full w-full z-[1300] px-2 mt-3" + onSubmit={(e) => handleShareWith(e, item)} + > + <Select + // onSortEnd={onSortEnd} + distance={4} + isMulti + options={departmentList} + value={selectedDepartments} + onChange={onChange} + closeMenuOnSelect={false} + required={true} + noOptionsMessage={() => + "Departments not found" + } + unstyled + classNames={{ + control: () => + "op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]", + valueContainer: () => + "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", + multiValue: () => + "op-badge op-badge-primary h-full text-[11px]", + multiValueLabel: () => "mb-[2px]", + menu: () => + "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", + menuList: () => + "shadow-md rounded-lg overflow-hidden", + option: () => + "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", + noOptionsMessage: () => + "p-2 bg-base-200 rounded-lg m-1 p-2" + }} + /> + <button className="op-btn op-btn-primary my-3"> + Submit + </button> + </form> + </div> + </div> + )} {isViewShare[item.objectId] && ( <ModalUi isOpen diff --git a/apps/OpenSignServer/cloud/parsefunction/getReport.js b/apps/OpenSignServer/cloud/parsefunction/getReport.js index cd101f7c2..c1b5b1b8b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getReport.js +++ b/apps/OpenSignServer/cloud/parsefunction/getReport.js @@ -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 } }, + { + CreatedBy: { + __type: 'Pointer', + className: '_User', + objectId: userId, + }, + }, + ], + }); + } 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 }); diff --git a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js index 37b53b95a..7954df977 100644 --- a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js +++ b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js @@ -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: From 57b23d7f97aafdd9250662d0932b2f7e0bb5a399 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Wed, 3 Jul 2024 18:01:51 +0530 Subject: [PATCH 09/10] feat: add share-with opt in templates as well as restrict non-team user --- apps/OpenSign/src/components/AddDepartment.js | 14 +- apps/OpenSign/src/constant/Utils.js | 24 +- apps/OpenSign/src/json/plansArr.js | 30 ++ apps/OpenSign/src/pages/DepartmentList.js | 305 +++++++++-------- apps/OpenSign/src/pages/Login.js | 304 ++++++++--------- apps/OpenSign/src/pages/UserList.js | 311 ++++++++++-------- .../OpenSign/src/primitives/DotLottieReact.js | 6 +- .../src/primitives/GetReportDisplay.js | 245 ++++++++------ apps/OpenSign/src/primitives/SubscribeCard.js | 4 +- apps/OpenSignServer/cloud/main.js | 3 +- .../cloud/parsefunction/GetTemplate.js | 43 ++- .../parsefunction/SubscriptionAftersave.js | 82 +++++ .../cloud/parsefunction/getReport.js | 6 +- .../cloud/parsefunction/usersignup.js | 2 +- 14 files changed, 814 insertions(+), 565 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index 9b151ff3c..b9de1d4b3 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -56,11 +56,15 @@ const AddDepartment = (props) => { objectId: x.objectId })); } else { - updatedAncestors.push({ - __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department - }); + const AllUser = departmentList.find((x) => x.objectId === "All User"); + updatedAncestors = [ + AllUser, + { + __type: "Pointer", + className: "contracts_Departments", + objectId: formdata.department + } + ]; } } try { diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 3976d8b1c..beefad8e6 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -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", diff --git a/apps/OpenSign/src/json/plansArr.js b/apps/OpenSign/src/json/plansArr.js index 3cc7b1696..7a347caa0 100644 --- a/apps/OpenSign/src/json/plansArr.js +++ b/apps/OpenSign/src/json/plansArr.js @@ -56,6 +56,36 @@ const plans = [ "Auto reminders" ] }, + { + planName: "OPENSIGN™ TEAM", + currency: "$", + monthlyPrice: "100.99", + yearlyPrice: "329.99", + subtitle: "Exclusive Access to advanced features.", + btnText: "Subscribe", + url: + window.location.origin === "http://localhost:3001" + ? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly" + : "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly", + yearlyUrl: + window.location.origin === "http://localhost:3001" + ? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly" + : "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly", + target: "_self", + benefits: [ + "Everything in OpenSign™ free, professional", + "Field validations", + "Regular expression validations", + "Organize docs in OpenSign™ Drive", + "Webhooks", + "Zapier integration", + "API Access", + "100 API signatures included", + "DocumentId removal from signed docs", + "Custom email templates", + "Auto reminders" + ] + }, { planName: "OPENSIGN™ ENTERPRISE", currency: "", diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/DepartmentList.js index 6bd204546..7b9fe3d9a 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/DepartmentList.js @@ -7,6 +7,9 @@ import Tooltip from "../primitives/Tooltip"; import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import AddDepartment from "../components/AddDepartment"; +import { isEnableSubscription } from "../constant/const"; +import { checkIsSubscribedTeam } from "../constant/Utils"; +import SubscribeCard from "../primitives/SubscribeCard"; const heading = ["Sr.No", "Name", "Parent Department", "Is-Active"]; // const actions = [ @@ -32,6 +35,7 @@ const DepartmentList = () => { const [isActiveModal, setIsActiveModal] = useState(false); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const [isActLoader, setIsActLoader] = useState({}); + const [isSubscribe, setIsSubscribe] = useState(false); const startIndex = (currentPage - 1) * recordperPage; // user per page const getPaginationRange = () => { @@ -92,6 +96,10 @@ const DepartmentList = () => { async function fetchDepartmentList() { try { setIsLoader(true); + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribedTeam(); + setIsSubscribe(getIsSubscribe); + } const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; const department = new Parse.Query("contracts_Departments"); department.equalTo("OrganizationId", { @@ -173,7 +181,7 @@ const DepartmentList = () => { return ( <div className="relative"> {isLoader && ( - <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box"> + <div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box"> <Loader /> </div> )} @@ -182,86 +190,93 @@ const DepartmentList = () => { <Loader /> </div> )} - <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> - {isAlert.msg && ( - <Alert type={isAlert.type}> - <div className="ml-3">{isAlert.msg}</div> - </Alert> - )} - <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> - <div className="font-light"> - department list{" "} - <span className="text-xs md:text-[13px] font-normal"> - <Tooltip message={"department list"} /> - </span> + {isSubscribe && isEnableSubscription && !isLoader && ( + <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> + {isAlert.msg && ( + <Alert type={isAlert.type}> + <div className="ml-3">{isAlert.msg}</div> + </Alert> + )} + <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> + <div className="font-light"> + department list{" "} + <span className="text-xs md:text-[13px] font-normal"> + <Tooltip message={"department list"} /> + </span> + </div> + <div className="cursor-pointer" onClick={() => handleFormModal()}> + <i className="fa-light fa-square-plus text-accent text-[40px]"></i> + </div> </div> - <div className="cursor-pointer" onClick={() => handleFormModal()}> - <i className="fa-light fa-square-plus text-accent text-[40px]"></i> - </div> - </div> - <div className={` overflow-x-auto w-full`}> - <table className="op-table border-collapse w-full"> - <thead className="text-[14px]"> - <tr className="border-y-[1px]"> - {heading?.map((item, index) => ( - <React.Fragment key={index}> - <th className="px-4 py-2">{item}</th> - </React.Fragment> - ))} - </tr> - </thead> - <tbody className="text-[12px]"> - {departmentList?.length > 0 && ( - <> - {currentList.map((item, index) => ( - <tr className="border-y-[1px]" key={index}> - {heading.includes("Sr.No") && ( - <th className="px-4 py-2">{startIndex + index + 1}</th> - )} - <td className="px-4 py-2 font-semibold">{item?.Name}</td> - <td className="px-4 py-2 font-semibold"> - {item?.ParentId?.Name || "-"} - </td> - <td className="px-4 py-2 font-semibold"> - <label className="cursor-pointer relative block items-center mb-0"> - <input - type="checkbox" - className="op-toggle transition-all op-toggle-secondary" - checked={item?.IsActive} - onChange={() => handleToggleBtn(item)} - /> - </label> - {isActiveModal[item.objectId] && ( - <ModalUi - isOpen - title={"Department status"} - handleClose={handleClose} - > - <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> - Are you sure you want to disable this - department? - </div> - <hr className="bg-[#ccc] mt-4 " /> - <div className="flex items-center mt-3 gap-2 text-white"> - <button - onClick={() => handleToggleSubmit(item)} - className="op-btn op-btn-primary" - > - Yes - </button> - <button - onClick={handleClose} - className="op-btn op-btn-secondary" - > - No - </button> - </div> - </div> - </ModalUi> + <div className={` overflow-x-auto w-full`}> + <table className="op-table border-collapse w-full"> + <thead className="text-[14px]"> + <tr className="border-y-[1px]"> + {heading?.map((item, index) => ( + <React.Fragment key={index}> + <th className="px-4 py-2">{item}</th> + </React.Fragment> + ))} + </tr> + </thead> + <tbody className="text-[12px]"> + {departmentList?.length > 0 && ( + <> + {currentList.map((item, index) => ( + <tr className="border-y-[1px]" key={index}> + {heading.includes("Sr.No") && ( + <th className="px-4 py-2"> + {startIndex + index + 1} + </th> )} - </td> - {/* <td className="px-3 py-2 text-white flex flex-wrap gap-1"> + <td className="px-4 py-2 font-semibold"> + {item?.Name} + </td> + <td className="px-4 py-2 font-semibold"> + {item?.ParentId?.Name || "-"} + </td> + {item?.Name !== "All User" && ( + <td className="px-4 py-2 font-semibold"> + <label className="cursor-pointer relative block items-center mb-0"> + <input + type="checkbox" + className="op-toggle transition-all op-toggle-secondary" + checked={item?.IsActive} + onChange={() => handleToggleBtn(item)} + /> + </label> + {isActiveModal[item.objectId] && ( + <ModalUi + isOpen + title={"Department status"} + handleClose={handleClose} + > + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + Are you sure you want to disable this + department? + </div> + <hr className="bg-[#ccc] mt-4 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleToggleSubmit(item)} + className="op-btn op-btn-primary" + > + Yes + </button> + <button + onClick={handleClose} + className="op-btn op-btn-secondary" + > + No + </button> + </div> + </div> + </ModalUi> + )} + </td> + )} + {/* <td className="px-3 py-2 text-white flex flex-wrap gap-1"> {actions?.length > 0 && actions.map((act, index) => ( <button @@ -276,70 +291,76 @@ const DepartmentList = () => { </button> ))} </td> */} - </tr> - ))} - </> - )} - </tbody> - </table> - </div> - <div className="op-join flex flex-wrap items-center p-2"> - {departmentList.length > recordperPage && ( - <button - onClick={() => paginateBack()} - className="op-join-item op-btn op-btn-sm" - > - Prev - </button> - )} - {pageNumbers.map((x, i) => ( - <button - key={i} - onClick={() => setCurrentPage(x)} - disabled={x === "..."} - className={`${ - x === currentPage ? "op-btn-active" : "" - } op-join-item op-btn op-btn-sm`} - > - {x} - </button> - ))} - {departmentList.length > recordperPage && ( - <button - onClick={() => paginateFront()} - className="op-join-item op-btn op-btn-sm" - > - Next - </button> - )} - </div> - {departmentList?.length <= 0 && ( - <div - className={`${ - isDashboard ? "h-[317px]" : "" - } flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`} - > - <div className="w-[60px] h-[60px] overflow-hidden"> - <img - className="w-full h-full object-contain" - src={pad} - alt="img" - /> - </div> - <div className="text-sm font-semibold">No Data Available</div> + </tr> + ))} + </> + )} + </tbody> + </table> </div> - )} - <ModalUi - title={"Add Department"} - isOpen={isModal} - handleClose={handleFormModal} - > - <AddDepartment - handleDepartmentInfo={handleDepartmentInfo} - closePopup={handleFormModal} - /> - </ModalUi> - </div> + <div className="op-join flex flex-wrap items-center p-2"> + {departmentList.length > recordperPage && ( + <button + onClick={() => paginateBack()} + className="op-join-item op-btn op-btn-sm" + > + Prev + </button> + )} + {pageNumbers.map((x, i) => ( + <button + key={i} + onClick={() => setCurrentPage(x)} + disabled={x === "..."} + className={`${ + x === currentPage ? "op-btn-active" : "" + } op-join-item op-btn op-btn-sm`} + > + {x} + </button> + ))} + {departmentList.length > recordperPage && ( + <button + onClick={() => paginateFront()} + className="op-join-item op-btn op-btn-sm" + > + Next + </button> + )} + </div> + {departmentList?.length <= 0 && ( + <div + className={`${ + isDashboard ? "h-[317px]" : "" + } flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`} + > + <div className="w-[60px] h-[60px] overflow-hidden"> + <img + className="w-full h-full object-contain" + src={pad} + alt="img" + /> + </div> + <div className="text-sm font-semibold">No Data Available</div> + </div> + )} + <ModalUi + title={"Add Department"} + isOpen={isModal} + handleClose={handleFormModal} + > + <AddDepartment + handleDepartmentInfo={handleDepartmentInfo} + closePopup={handleFormModal} + /> + </ModalUi> + </div> + )} + {!isSubscribe && isEnableSubscription && !isLoader && ( + <div data-tut="apisubscribe"> + <SubscribeCard plan={"TEAM"} /> + </div> + )} </div> ); }; diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 583d065ac..8a41d5c86 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -123,102 +123,161 @@ function Login() { if (roles) { userRoles = roles; let _currentRole = ""; - if (userRoles.length > 0) { - _currentRole = userRoles.find( - (x) => x === extUser.get("UserRole") - ); - } - userSettings.forEach(async (element) => { - const redirectUrl = - location?.state?.from || - `/${element.pageType}/${element.pageId}`; - if (element.role === _currentRole) { - let _role = _currentRole.replace( - "contracts_", - "" - ); - localStorage.setItem("_user_role", _role); - // Get TenentID from Extendend Class - localStorage.setItem( - "extended_class", - element.extended_class - ); - let tenentInfo = []; - const results = [extUser]; - if (results) { - let extendedInfo_stringify = - JSON.stringify(results); + const valuesToExclude = [ + "contracts_Guest", + "contracts_appeditor" + ]; + const filterRoles = userRoles.filter( + (x) => !valuesToExclude.includes(x) + ); + if (filterRoles.length > 0) { + _currentRole = filterRoles?.[0]; + userSettings.forEach(async (element) => { + const redirectUrl = + location?.state?.from || + `/${element.pageType}/${element.pageId}`; + if (element.role === _currentRole) { + let _role = _currentRole.replace( + "contracts_", + "" + ); + localStorage.setItem("_user_role", _role); + // Get TenentID from Extendend Class localStorage.setItem( - "Extand_Class", - extendedInfo_stringify + "extended_class", + element.extended_class ); - let extendedInfo = JSON.parse( - extendedInfo_stringify - ); - if (extendedInfo.length > 1) { - extendedInfo.forEach((x) => { - if (x.TenantId) { - let obj = { - tenentId: x.TenantId.objectId, - tenentName: - x.TenantId.TenantName || "" - }; - tenentInfo.push(obj); - } - }); - if (tenentInfo.length) { - dispatch( - showTenant( + let tenentInfo = []; + const results = [extUser]; + if (results) { + let extendedInfo_stringify = + JSON.stringify(results); + + localStorage.setItem( + "Extand_Class", + extendedInfo_stringify + ); + let extendedInfo = JSON.parse( + extendedInfo_stringify + ); + if (extendedInfo.length > 1) { + extendedInfo.forEach((x) => { + if (x.TenantId) { + let obj = { + tenentId: x.TenantId.objectId, + tenentName: + x.TenantId.TenantName || "" + }; + tenentInfo.push(obj); + } + }); + if (tenentInfo.length) { + dispatch( + showTenant( + tenentInfo[0].tenentName || "" + ) + ); + localStorage.setItem( + "TenantName", tenentInfo[0].tenentName || "" - ) + ); + } + + localStorage.setItem("showpopup", true); + localStorage.setItem( + "PageLanding", + element.pageId ); localStorage.setItem( - "TenantName", - tenentInfo[0].tenentName || "" + "defaultmenuid", + element.menuId ); - } - - localStorage.setItem("showpopup", true); - localStorage.setItem( - "PageLanding", - element.pageId - ); - localStorage.setItem( - "defaultmenuid", - element.menuId - ); - localStorage.setItem( - "pageType", - element.pageType - ); - setState({ ...state, loading: false }); - navigate("/"); - } else { - extendedInfo.forEach((x) => { - if (x.TenantId) { - let obj = { - tenentId: x.TenantId.objectId, - tenentName: - x.TenantId.TenantName || "" + localStorage.setItem( + "pageType", + element.pageType + ); + setState({ ...state, loading: false }); + navigate("/"); + } else { + extendedInfo.forEach((x) => { + if (x.TenantId) { + let obj = { + tenentId: x.TenantId.objectId, + tenentName: + x.TenantId.TenantName || "" + }; + localStorage.setItem( + "TenantId", + x.TenantId.objectId + ); + tenentInfo.push(obj); + } + }); + if (tenentInfo.length) { + dispatch( + showTenant( + tenentInfo[0].tenentName || "" + ) + ); + localStorage.setItem( + "TenantName", + tenentInfo[0].tenentName || "" + ); + } + localStorage.setItem( + "PageLanding", + element.pageId + ); + localStorage.setItem( + "defaultmenuid", + element.menuId + ); + localStorage.setItem( + "pageType", + element.pageType + ); + setState({ ...state, loading: false }); + if (isEnableSubscription) { + const LocalUserDetails = { + name: results[0].get("Name"), + email: results[0].get("Email"), + phone: results[0]?.get("Phone") || "", + company: results[0].get("Company") }; localStorage.setItem( - "TenantId", - x.TenantId.objectId + "userDetails", + JSON.stringify(LocalUserDetails) ); - tenentInfo.push(obj); + const res = await fetchSubscription(); + const freeplan = res.plan; + const billingDate = res.billingDate; + if (freeplan === "freeplan") { + navigate(redirectUrl); + } else if (billingDate) { + if ( + new Date(billingDate) > new Date() + ) { + localStorage.removeItem( + "userDetails" + ); + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); + } else { + navigate(`/subscription`, { + replace: true + }); + } + } else { + navigate(`/subscription`, { + replace: true + }); + } + } else { + // Redirect to the appropriate URL after successful login + navigate(redirectUrl); } - }); - if (tenentInfo.length) { - dispatch( - showTenant( - tenentInfo[0].tenentName || "" - ) - ); - localStorage.setItem( - "TenantName", - tenentInfo[0].tenentName || "" - ); } + } else { localStorage.setItem( "PageLanding", element.pageId @@ -234,35 +293,17 @@ function Login() { setState({ ...state, loading: false }); if (isEnableSubscription) { const LocalUserDetails = { - name: results[0].get("Name"), - email: results[0].get("Email"), - phone: results[0]?.get("Phone") || "", - company: results[0].get("Company") + name: _user.name, + email: email, + phone: _user?.phone || "" + // company: results.get("Company"), }; localStorage.setItem( "userDetails", JSON.stringify(LocalUserDetails) ); - const res = await fetchSubscription(); - const freeplan = res.plan; - const billingDate = res.billingDate; - if (freeplan === "freeplan") { - navigate(redirectUrl); - } else if (billingDate) { - if ( - new Date(billingDate) > new Date() - ) { - localStorage.removeItem( - "userDetails" - ); - // Redirect to the appropriate URL after successful login - navigate(redirectUrl); - } else { - navigate(`/subscription`, { - replace: true - }); - } - } else { + const billingDate = ""; + if (billingDate) { navigate(`/subscription`, { replace: true }); @@ -272,44 +313,9 @@ function Login() { navigate(redirectUrl); } } - } else { - localStorage.setItem( - "PageLanding", - element.pageId - ); - localStorage.setItem( - "defaultmenuid", - element.menuId - ); - localStorage.setItem( - "pageType", - element.pageType - ); - setState({ ...state, loading: false }); - if (isEnableSubscription) { - const LocalUserDetails = { - name: _user.name, - email: email, - phone: _user?.phone || "" - // company: results.get("Company"), - }; - localStorage.setItem( - "userDetails", - JSON.stringify(LocalUserDetails) - ); - const billingDate = ""; - if (billingDate) { - navigate(`/subscription`, { - replace: true - }); - } - } else { - // Redirect to the appropriate URL after successful login - navigate(redirectUrl); - } } - } - }); + }); + } } else { setState({ ...state, loading: false }); setIsModal(true); @@ -719,7 +725,6 @@ function Login() { .post(url, JSON.stringify(body), { headers: headers1 }) .then((axiosres) => { const roles = axiosres.data.result; - console.log("roles ", roles); if (roles && roles.length > 0) { userRoles = roles; let _currentRole = ""; @@ -729,7 +734,6 @@ function Login() { ); if (filterRoles.length > 0) { _currentRole = filterRoles?.[0]; - console.log("_currentRole ", _currentRole); let SettingsUser = userSettings; SettingsUser.forEach(async (item) => { if (item.role === _currentRole) { @@ -753,6 +757,10 @@ function Login() { const results = [result]; if (results) { let extendedInfo_stringify = JSON.stringify(results); + localStorage.setItem( + "Extand_Class", + extendedInfo_stringify + ); let extendedInfo = JSON.parse(extendedInfo_stringify); if (extendedInfo.length > 1) { extendedInfo.forEach((x) => { diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 5f337c918..1f0d5df68 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -7,6 +7,9 @@ import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; import Tooltip from "../primitives/Tooltip"; import AddUser from "../components/AddUser"; +import SubscribeCard from "../primitives/SubscribeCard"; +import { isEnableSubscription } from "../constant/const"; +import { checkIsSubscribedTeam } from "../constant/Utils"; const heading = [ "Sr.No", "Name", @@ -28,6 +31,7 @@ const UserList = () => { const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const [isActiveModal, setIsActiveModal] = useState({}); const [isActLoader, setIsActLoader] = useState({}); + const [isSubscribe, setIsSubscribe] = useState(false); const recordperPage = 10; const startIndex = (currentPage - 1) * recordperPage; // user per page @@ -89,6 +93,10 @@ const UserList = () => { async function fetchUserList() { try { setIsLoader(true); + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribedTeam(); + setIsSubscribe(getIsSubscribe); + } const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; const res = await Parse.Cloud.run("getuserlistbyorg", { organizationId: extUser.OrganizationId.objectId @@ -170,7 +178,7 @@ const UserList = () => { return ( <div className="relative"> {isLoader && ( - <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box"> + <div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box"> <Loader /> </div> )} @@ -179,90 +187,95 @@ const UserList = () => { <Loader /> </div> )} - <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> - {isAlert.msg && ( - <Alert type={isAlert.type}> - <div className="ml-3">{isAlert.msg}</div> - </Alert> - )} - <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> - <div className="font-light"> - User list{" "} - <span className="text-xs md:text-[13px] font-normal"> - <Tooltip message={"user list from departments"} /> - </span> + {isSubscribe && isEnableSubscription && !isLoader && ( + <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> + {isAlert.msg && ( + <Alert type={isAlert.type}> + <div className="ml-3">{isAlert.msg}</div> + </Alert> + )} + <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> + <div className="font-light"> + User list{" "} + <span className="text-xs md:text-[13px] font-normal"> + <Tooltip message={"user list from departments"} /> + </span> + </div> + <div className="cursor-pointer" onClick={() => handleFormModal()}> + <i className="fa-light fa-square-plus text-accent text-[40px]"></i> + </div> </div> - <div className="cursor-pointer" onClick={() => handleFormModal()}> - <i className="fa-light fa-square-plus text-accent text-[40px]"></i> - </div> - </div> - <div className={` overflow-x-auto w-full`}> - <table className="op-table border-collapse w-full"> - <thead className="text-[14px]"> - <tr className="border-y-[1px]"> - {heading?.map((item, index) => ( - <React.Fragment key={index}> - <th className="px-4 py-2">{item}</th> - </React.Fragment> - ))} - </tr> - </thead> - <tbody className="text-[12px]"> - {userList?.length > 0 && ( - <> - {userList.map((item, index) => ( - <tr className="border-y-[1px]" key={index}> - {heading.includes("Sr.No") && ( - <th className="px-4 py-2">{startIndex + index + 1}</th> - )} - <td className="px-4 py-2 font-semibold">{item?.Name} </td> - <td className="px-4 py-2 ">{item?.Email || "-"}</td> - <td className="px-4 py-2">{item?.Phone || "-"}</td> - <td className="px-4 py-2"> - {item?.UserRole?.split("_").pop() || "-"} - </td> - <td className="px-4 py-2"> - {formatRow(item.DepartmentIds)} - </td> - <td className="px-4 py-2 font-semibold"> - <label className="cursor-pointer relative block items-center mb-0"> - <input - type="checkbox" - className="op-toggle transition-all op-toggle-secondary" - checked={item?.IsDisabled !== true} - onChange={() => handleToggleBtn(item)} - /> - </label> - {isActiveModal[item.objectId] && ( - <ModalUi - isOpen - title={"User status"} - handleClose={handleClose} - > - <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> - Are you sure you want to deactivate this user? - </div> - <hr className="bg-[#ccc] mt-4 " /> - <div className="flex items-center mt-3 gap-2 text-white"> - <button - onClick={() => handleToggleSubmit(item)} - className="op-btn op-btn-primary" - > - Yes - </button> - <button - onClick={handleClose} - className="op-btn op-btn-secondary" - > - No - </button> - </div> - </div> - </ModalUi> + <div className={` overflow-x-auto w-full`}> + <table className="op-table border-collapse w-full"> + <thead className="text-[14px]"> + <tr className="border-y-[1px]"> + {heading?.map((item, index) => ( + <React.Fragment key={index}> + <th className="px-4 py-2">{item}</th> + </React.Fragment> + ))} + </tr> + </thead> + <tbody className="text-[12px]"> + {userList?.length > 0 && ( + <> + {userList.map((item, index) => ( + <tr className="border-y-[1px]" key={index}> + {heading.includes("Sr.No") && ( + <th className="px-4 py-2"> + {startIndex + index + 1} + </th> )} - </td> - {/* <td className="px-3 py-2 text-white grid grid-cols-2"> + <td className="px-4 py-2 font-semibold"> + {item?.Name}{" "} + </td> + <td className="px-4 py-2 ">{item?.Email || "-"}</td> + <td className="px-4 py-2">{item?.Phone || "-"}</td> + <td className="px-4 py-2"> + {item?.UserRole?.split("_").pop() || "-"} + </td> + <td className="px-4 py-2"> + {formatRow(item.DepartmentIds)} + </td> + <td className="px-4 py-2 font-semibold"> + <label className="cursor-pointer relative block items-center mb-0"> + <input + type="checkbox" + className="op-toggle transition-all op-toggle-secondary" + checked={item?.IsDisabled !== true} + onChange={() => handleToggleBtn(item)} + /> + </label> + {isActiveModal[item.objectId] && ( + <ModalUi + isOpen + title={"User status"} + handleClose={handleClose} + > + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + Are you sure you want to deactivate this user? + </div> + <hr className="bg-[#ccc] mt-4 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleToggleSubmit(item)} + className="op-btn op-btn-primary" + > + Yes + </button> + <button + onClick={handleClose} + className="op-btn op-btn-secondary" + > + No + </button> + </div> + </div> + </ModalUi> + )} + </td> + {/* <td className="px-3 py-2 text-white grid grid-cols-2"> {actions?.length > 0 && actions.map((act, index) => ( <button @@ -305,70 +318,76 @@ const UserList = () => { </ModalUi> )} </td> */} - </tr> - ))} - </> - )} - </tbody> - </table> - </div> - <div className="op-join flex flex-wrap items-center p-2"> - {userList.length > recordperPage && ( - <button - onClick={() => paginateBack()} - className="op-join-item op-btn op-btn-sm" - > - Prev - </button> - )} - {pageNumbers.map((x, i) => ( - <button - key={i} - onClick={() => setCurrentPage(x)} - disabled={x === "..."} - className={`${ - x === currentPage ? "op-btn-active" : "" - } op-join-item op-btn op-btn-sm`} - > - {x} - </button> - ))} - {userList.length > recordperPage && ( - <button - onClick={() => paginateFront()} - className="op-join-item op-btn op-btn-sm" - > - Next - </button> - )} - </div> - {userList?.length <= 0 && ( - <div - className={`${ - isDashboard ? "h-[317px]" : "" - } flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`} - > - <div className="w-[60px] h-[60px] overflow-hidden"> - <img - className="w-full h-full object-contain" - src={pad} - alt="img" - /> - </div> - <div className="text-sm font-semibold">No Data Available</div> + </tr> + ))} + </> + )} + </tbody> + </table> </div> - )} - <ModalUi - title={"Add User"} - isOpen={isModal} - handleClose={handleFormModal} - > - <AddUser - handleUserData={handleUserData} - closePopup={handleFormModal} - /> - </ModalUi> - </div> + <div className="op-join flex flex-wrap items-center p-2"> + {userList.length > recordperPage && ( + <button + onClick={() => paginateBack()} + className="op-join-item op-btn op-btn-sm" + > + Prev + </button> + )} + {pageNumbers.map((x, i) => ( + <button + key={i} + onClick={() => setCurrentPage(x)} + disabled={x === "..."} + className={`${ + x === currentPage ? "op-btn-active" : "" + } op-join-item op-btn op-btn-sm`} + > + {x} + </button> + ))} + {userList.length > recordperPage && ( + <button + onClick={() => paginateFront()} + className="op-join-item op-btn op-btn-sm" + > + Next + </button> + )} + </div> + {userList?.length <= 0 && ( + <div + className={`${ + isDashboard ? "h-[317px]" : "" + } flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`} + > + <div className="w-[60px] h-[60px] overflow-hidden"> + <img + className="w-full h-full object-contain" + src={pad} + alt="img" + /> + </div> + <div className="text-sm font-semibold">No Data Available</div> + </div> + )} + <ModalUi + title={"Add User"} + isOpen={isModal} + handleClose={handleFormModal} + > + <AddUser + handleUserData={handleUserData} + closePopup={handleFormModal} + /> + </ModalUi> + </div> + )} + {!isSubscribe && isEnableSubscription && !isLoader && ( + <div data-tut="apisubscribe"> + <SubscribeCard plan={"TEAM"} /> + </div> + )} </div> ); }; diff --git a/apps/OpenSign/src/primitives/DotLottieReact.js b/apps/OpenSign/src/primitives/DotLottieReact.js index 66ca3b8b3..79811cff9 100644 --- a/apps/OpenSign/src/primitives/DotLottieReact.js +++ b/apps/OpenSign/src/primitives/DotLottieReact.js @@ -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> diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index f8e40ac22..847bed726 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -12,6 +12,7 @@ import Tour from "reactour"; import Parse from "parse"; import { saveAs } from "file-saver"; import { + checkIsSubscribedTeam, // copytoData, replaceMailVaribles } from "../constant/Utils"; @@ -25,6 +26,8 @@ import "react-quill/dist/quill.snow.css"; import BulkSendUi from "../components/BulkSendUi"; import Loader from "./Loader"; import Select from "react-select"; +import { isEnableSubscription } from "../constant/const"; +import SubscribeCard from "./SubscribeCard"; const ReportTable = (props) => { const navigate = useNavigate(); @@ -64,6 +67,9 @@ const ReportTable = (props) => { // const [isPublicProfile, setIsPublicProfile] = useState({}); // const [publicUserName, setIsPublicUserName] = useState(""); const [isViewShare, setIsViewShare] = useState({}); + const [isSubscribe, setIsSubscribe] = useState(true); + const Extand_Class = localStorage.getItem("Extand_Class"); + const extClass = Extand_Class && JSON.parse(Extand_Class); const startIndex = (currentPage - 1) * props.docPerPage; const { isMoreDocs, setIsNextRecord } = props; // For loop is used to calculate page numbers visible below table @@ -191,9 +197,7 @@ const ReportTable = (props) => { } else { setActLoader({ [`${item.objectId}_${act.btnId}`]: true }); try { - const params = { - templateId: item.objectId - }; + const params = { templateId: item.objectId }; const templateDeatils = await axios.post( `${localStorage.getItem("baseUrl")}functions/getTemplate`, params, @@ -224,6 +228,19 @@ const ReportTable = (props) => { }); } + // console.log("extClass ", extClass); + let extUserId = Doc.ExtUserPtr.objectId; + let creatorId = Doc.CreatedBy.objectId; + if (extClass && extClass.length > 0) { + if (Doc.ExtUserPtr?.objectId !== extClass[0].objectId) { + const Extand_Class = localStorage.getItem("Extand_Class"); + const extClass = Extand_Class && JSON.parse(Extand_Class); + if (extClass && extClass.length > 0) { + extUserId = extClass[0].objectId; + creatorId = extClass[0]?.UserId.objectId; + } + } + } let placeholdersArr = []; if (Doc.Placeholders?.length > 0) { placeholdersArr = Doc.Placeholders; @@ -238,12 +255,12 @@ const ReportTable = (props) => { ExtUserPtr: { __type: "Pointer", className: "contracts_Users", - objectId: Doc.ExtUserPtr.objectId + objectId: extUserId }, CreatedBy: { __type: "Pointer", className: "_User", - objectId: Doc.CreatedBy.objectId + objectId: creatorId }, Signers: signers, SendinOrder: Doc?.SendinOrder || false, @@ -254,7 +271,7 @@ const ReportTable = (props) => { const res = await axios.post( `${localStorage.getItem( "baseUrl" - )}classes/${localStorage.getItem("_appName")}_Document`, + )}classes/contracts_Document`, data, { headers: { @@ -315,7 +332,7 @@ const ReportTable = (props) => { } }; - const handleActionBtn = (act, item) => { + const handleActionBtn = async (act, item) => { if (act.action === "redirect") { handleURL(item, act); } else if (act.action === "delete") { @@ -331,6 +348,10 @@ const ReportTable = (props) => { } else if (act.action === "bulksend") { handleBulkSend(item); } else if (act.action === "sharewith") { + if (isEnableSubscription) { + const getIsSubscribe = await checkIsSubscribedTeam(); + setIsSubscribe(getIsSubscribe); + } if (item?.SharedWith && item?.SharedWith.length > 0) { // below code is used to get existing sharewith departments and formated them as per react-select const formatedList = item?.SharedWith.map((x) => ({ @@ -1331,106 +1352,126 @@ const ReportTable = (props) => { <td className="px-2 py-2"> <div className="text-base-content flex flex-row gap-x-2 gap-y-1 justify-start items-center"> {props.actions?.length > 0 && - props.actions.map((act, index) => ( - <div - role="button" - data-tut={act?.selector} - key={index} - onClick={() => handleActionBtn(act, item)} - title={act.hoverLabel} - className={ - act.action !== "option" - ? `${ - act?.btnColor ? act.btnColor : "" - } op-btn op-btn-sm mr-1` - : "text-base-content focus:outline-none text-lg mr-2 relative" - } - > - <i className={act.btnIcon}></i> - {act.btnLabel && ( - <span className="uppercase font-medium"> - {act.btnLabel} - </span> - )} - {isOption[item.objectId] && - act.action === "option" && ( - <ul className="absolute -right-2 top-6 z-[20] op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box"> - {act.subaction?.map((subact) => ( - <li - key={subact.btnId} - onClick={() => - handleActionBtn(subact, item) - } - title={subact.hoverLabel} - > - <span> - <i - className={`${subact.btnIcon} mr-1.5`} - ></i> - {subact.btnLabel && ( - <span className="text-[13px] capitalize font-medium"> - {subact.btnLabel} + props.actions.map( + (act, index) => + (item.ExtUserPtr?.objectId === + extClass?.[0]?.objectId || + act.btnLabel === "Use") && ( + <div + role="button" + data-tut={act?.selector} + key={index} + onClick={() => handleActionBtn(act, item)} + title={act.hoverLabel} + className={ + act.action !== "option" + ? `${ + act?.btnColor ? act.btnColor : "" + } op-btn op-btn-sm mr-1` + : "text-base-content focus:outline-none text-lg mr-2 relative" + } + > + <i className={act.btnIcon}></i> + {act.btnLabel && ( + <span className="uppercase font-medium"> + {act.btnLabel} + </span> + )} + {isOption[item.objectId] && + act.action === "option" && ( + <ul className="absolute -right-2 top-6 z-[20] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box"> + {act.subaction?.map((subact) => ( + <li + key={subact.btnId} + onClick={() => + handleActionBtn(subact, item) + } + title={subact.hoverLabel} + > + <span> + <i + className={`${subact.btnIcon} mr-1.5`} + ></i> + {subact.btnLabel && ( + <span className="text-[13px] capitalize font-medium"> + {subact.btnLabel} + </span> + )} </span> - )} - </span> - </li> - ))} - </ul> - )} - </div> - ))} + </li> + ))} + </ul> + )} + </div> + ) + )} </div> {isShareWith[item.objectId] && ( <div className="op-modal op-modal-open"> <div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative"> - <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> - Share with - </h3> - <div - className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" - onClick={() => setIsShareWith({})} - > - ✕ - </div> - <form - className="h-full w-full z-[1300] px-2 mt-3" - onSubmit={(e) => handleShareWith(e, item)} - > - <Select - // onSortEnd={onSortEnd} - distance={4} - isMulti - options={departmentList} - value={selectedDepartments} - onChange={onChange} - closeMenuOnSelect={false} - required={true} - noOptionsMessage={() => - "Departments not found" - } - unstyled - classNames={{ - control: () => - "op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]", - valueContainer: () => - "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", - multiValue: () => - "op-badge op-badge-primary h-full text-[11px]", - multiValueLabel: () => "mb-[2px]", - menu: () => - "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", - menuList: () => - "shadow-md rounded-lg overflow-hidden", - option: () => - "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", - noOptionsMessage: () => - "p-2 bg-base-200 rounded-lg m-1 p-2" - }} - /> - <button className="op-btn op-btn-primary my-3"> - Submit - </button> - </form> + {isSubscribe && isEnableSubscription && ( + <> + <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> + Share with + </h3> + <div + className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" + onClick={() => setIsShareWith({})} + > + ✕ + </div> + <form + className="h-full w-full z-[1300] px-2 mt-3" + onSubmit={(e) => handleShareWith(e, item)} + > + <Select + // onSortEnd={onSortEnd} + distance={4} + isMulti + options={departmentList} + value={selectedDepartments} + onChange={onChange} + closeMenuOnSelect={false} + required={true} + noOptionsMessage={() => + "Departments not found" + } + unstyled + classNames={{ + control: () => + "op-input op-input-bordered op-input-sm border-gray-400 focus:outline-none hover:border-base-content w-full h-full text-[11px]", + valueContainer: () => + "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", + multiValue: () => + "op-badge op-badge-primary h-full text-[11px]", + multiValueLabel: () => "mb-[2px]", + menu: () => + "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", + menuList: () => + "shadow-md rounded-lg overflow-hidden", + option: () => + "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", + noOptionsMessage: () => + "p-2 bg-base-200 rounded-lg m-1 p-2" + }} + /> + <button className="op-btn op-btn-primary ml-[10px] my-3"> + Submit + </button> + </form> + </> + )} + {!isSubscribe && isEnableSubscription && ( + <> + <div + className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40" + onClick={() => setIsShareWith({})} + > + ✕ + </div> + <SubscribeCard plan={"TEAM"} /> + </> + )} </div> </div> )} diff --git a/apps/OpenSign/src/primitives/SubscribeCard.js b/apps/OpenSign/src/primitives/SubscribeCard.js index 84fdcc163..78f21001c 100644 --- a/apps/OpenSign/src/primitives/SubscribeCard.js +++ b/apps/OpenSign/src/primitives/SubscribeCard.js @@ -1,12 +1,12 @@ import React from "react"; import { useNavigate } from "react-router-dom"; -const SubscribeCard = () => { +const SubscribeCard = ({ plan }) => { const navigate = useNavigate(); return ( <div className="op-card op-bg-primary text-primary-content w-full shadow-lg"> <div className="op-card-body"> - <h2 className="op-card-title">Upgrade to PRO Plan</h2> + <h2 className="op-card-title">Upgrade to {plan ? plan : "PRO"} Plan</h2> <p>$29.99/month: Sign 100 docs using API. Only $0.15/doc after that.</p> <div className="op-card-actions justify-end"> <button diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index a92824195..af4165b12 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -47,6 +47,7 @@ import isextenduser from './parsefunction/isextenduser.js'; import getUserByOrg from './parsefunction/getUserByOrg.js'; import getUserListByOrg from './parsefunction/getUserListByOrg.js'; import DepartmentsAftersave from './parsefunction/DepartmentsAftersave.js'; +import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.js'; // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); @@ -54,7 +55,7 @@ Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave); Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave); Parse.Cloud.afterSave('contracts_Departments', DepartmentsAftersave); - +Parse.Cloud.afterSave('contracts_Subscriptions', SubscriptionAftersave); // This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification. Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave); Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave); diff --git a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js index 9d7998697..c3ff9e910 100644 --- a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js +++ b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js @@ -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!" }; } diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js new file mode 100644 index 000000000..75849cfb6 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js @@ -0,0 +1,82 @@ +async function addDepartmentAndOrg(extUser) { + try { + const orgCls = new Parse.Object('contracts_Organizations'); + orgCls.set('Name', extUser.Company); + orgCls.set('IsActive', true); + const orgRes = await orgCls.save(null, { useMasterKey: true }); + const departmentCls = new Parse.Object('contracts_Departments'); + departmentCls.set('Name', 'All User'); + departmentCls.set('OrganizationId', { + __type: 'Pointer', + className: 'contracts_Organizations', + objectId: orgRes.id, + }); + departmentCls.set('IsActive', true); + const departmentRes = await departmentCls.save(null, { useMasterKey: true }); + const updateUser = new Parse.Object('contracts_Users'); + updateUser.id = extUser.objectId; + updateUser.set('UserRole', 'contracts_Admin'); + updateUser.set('OrganizationId', { + __type: 'Pointer', + className: 'contracts_Organizations', + objectId: orgRes.id, + }); + updateUser.set('DepartmentIds', [ + { + __type: 'Pointer', + className: 'contracts_Departments', + objectId: departmentRes.id, + }, + ]); + const extUserRes = await updateUser.save(null, { useMasterKey: true }); + } catch (err) { + console.log('err in add department, role, org', err); + } +} + +export default async function SubscriptionAftersave(request) { + const oldObj = request.original; + if (!oldObj) { + try { + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.include('CreatedBy'); + const res = await subscription.get(request.object.id, { useMasterKey: true }); + const _res = JSON.parse(JSON.stringify(res)); + const user = _res.CreatedBy?.email; + if (user) { + const extUserQuery = new Parse.Query('contracts_Users'); + extUserQuery.equalTo('Email', user); + const extUserRes = await extUserQuery.first({ useMasterKey: true }); + if (extUserRes) { + const extUser = JSON.parse(JSON.stringify(extUserRes)); + if (extUser?.UserRole !== 'contracts_Admin') { + await addDepartmentAndOrg(extUser); + } + } + } + } catch (err) { + console.log('Err in subscriptionaftersave', err); + } + } else { + try { + const subscription = new Parse.Query('contracts_Subscriptions'); + subscription.include('CreatedBy'); + const res = await subscription.get(request.object.id, { useMasterKey: true }); + const _res = JSON.parse(JSON.stringify(res)); + const user = _res.CreatedBy?.email; + if (user) { + const extUserQuery = new Parse.Query('contracts_Users'); + extUserQuery.equalTo('Email', user); + const extUserRes = await extUserQuery.first({ useMasterKey: true }); + if (extUserRes) { + const extUser = JSON.parse(JSON.stringify(extUserRes)); + if (extUser?.UserRole !== 'contracts_Admin') { + await addDepartmentAndOrg(extUser); + } + } + } + } catch (err) { + console.log('Err in subscriptionaftersave', err); + } + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/getReport.js b/apps/OpenSignServer/cloud/parsefunction/getReport.js index c1b5b1b8b..2d8fe2aa9 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getReport.js +++ b/apps/OpenSignServer/cloud/parsefunction/getReport.js @@ -42,10 +42,10 @@ export default async function getReport(request) { $or: [ { SharedWith: { $in: departmentArr } }, { - CreatedBy: { + ExtUserPtr: { __type: 'Pointer', - className: '_User', - objectId: userId, + className: 'contracts_Users', + objectId: extUser.id, }, }, ], diff --git a/apps/OpenSignServer/cloud/parsefunction/usersignup.js b/apps/OpenSignServer/cloud/parsefunction/usersignup.js index 2d4338a5f..81f0e8cc6 100644 --- a/apps/OpenSignServer/cloud/parsefunction/usersignup.js +++ b/apps/OpenSignServer/cloud/parsefunction/usersignup.js @@ -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', { From 450de195a76969cdcc4d03c090c745288ef5598b Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Wed, 3 Jul 2024 20:47:19 +0530 Subject: [PATCH 10/10] refactor: change name from department to team --- apps/OpenSign/src/App.js | 8 +- apps/OpenSign/src/components/AddDepartment.js | 8 +- apps/OpenSign/src/components/AddUser.js | 7 +- apps/OpenSign/src/components/pdf/PdfHeader.js | 24 +++-- .../src/components/sidebar/Sidebar.js | 4 +- apps/OpenSign/src/json/plansArr.js | 8 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 26 +++--- apps/OpenSign/src/pages/PlanSubscriptions.js | 21 ++++- .../pages/{DepartmentList.js => TeamList.js} | 89 +++++++++---------- apps/OpenSign/src/pages/UserList.js | 18 ++-- .../src/primitives/GetReportDisplay.js | 9 +- apps/OpenSign/src/primitives/SubscribeCard.js | 7 +- .../parsefunction/SubscriptionAftersave.js | 2 +- 13 files changed, 117 insertions(+), 114 deletions(-) rename apps/OpenSign/src/pages/{DepartmentList.js => TeamList.js} (84%) diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 5154e248b..40b6801b6 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -18,9 +18,8 @@ import LazyPage from "./primitives/LazyPage"; import { isEnableSubscription } from "./constant/const"; import SSOVerify from "./pages/SSOVerify"; import Loader from "./primitives/Loader"; -import DepartmentList from "./pages/DepartmentList"; +import TeamList from "./pages/TeamList"; import UserList from "./pages/UserList"; -import AddDepartment from "./components/AddDepartment"; const DebugPdf = lazy(() => import("./pages/DebugPdf")); const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const GuestLogin = lazy(() => import("./pages/GuestLogin")); @@ -123,11 +122,8 @@ function App() { </> )} <Route element={<HomeLayout />}> - <Route path="/departments" element={<DepartmentList />} /> - <Route path="/departmentform" element={<AddDepartment />} /> - + <Route path="/teams" element={<TeamList />} /> <Route path="/users" element={<UserList />} /> - <Route path="/changepassword" element={<LazyPage Page={ChangePassword} />} diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddDepartment.js index b9de1d4b3..6af89e6f0 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddDepartment.js @@ -56,7 +56,7 @@ const AddDepartment = (props) => { objectId: x.objectId })); } else { - const AllUser = departmentList.find((x) => x.objectId === "All User"); + const AllUser = departmentList.find((x) => x.objectId === "All Users"); updatedAncestors = [ AllUser, { @@ -107,7 +107,7 @@ const AddDepartment = (props) => { const ParentId = departmentList.find( (x) => x.objectId === formdata.department ); - props.handleDepartmentInfo({ + props.handleTeamInfo({ objectId: newdepartmentRes.id, Name: formdata.name, ParentId: ParentId, @@ -115,7 +115,7 @@ const AddDepartment = (props) => { IsActive: true }); } else { - props.handleDepartmentInfo({ + props.handleTeamInfo({ objectId: newdepartmentRes.id, Name: formdata.name, ParentId: "", @@ -197,7 +197,7 @@ const AddDepartment = (props) => { htmlFor="phone" className="block text-xs text-gray-700 font-semibold" > - Department + Parent Team </label> <select value={formdata.department} diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 983d7cd8a..38e3de2ea 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -336,7 +336,7 @@ const AddUser = (props) => { > Password </label> - <div className="flex justify-between items-center op-input op-input-bordered op-input-sm bg-base-200 text-base-content w-full h-full text-[13px]"> + <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)} @@ -344,7 +344,7 @@ const AddUser = (props) => { ></i> </div> <div className="text-[12px] ml-2 mb-0 text-[red]"> - Password will be generated one time; make sure to copy it. + Password will only be generated once; make sure to copy it. </div> </div> <div className="mb-3"> @@ -358,6 +358,7 @@ const AddUser = (props) => { <input type="text" name="phone" + placeholder="optional" value={formdata.phone} onChange={(e) => handleChange(e)} // required @@ -369,7 +370,7 @@ const AddUser = (props) => { htmlFor="phone" className="block text-xs text-gray-700 font-semibold" > - Department + Team </label> <select value={formdata.department} diff --git a/apps/OpenSign/src/components/pdf/PdfHeader.js b/apps/OpenSign/src/components/pdf/PdfHeader.js index c90521eb2..4223194b9 100644 --- a/apps/OpenSign/src/components/pdf/PdfHeader.js +++ b/apps/OpenSign/src/components/pdf/PdfHeader.js @@ -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" diff --git a/apps/OpenSign/src/components/sidebar/Sidebar.js b/apps/OpenSign/src/components/sidebar/Sidebar.js index e8b587b3f..23fb34fb6 100644 --- a/apps/OpenSign/src/components/sidebar/Sidebar.js +++ b/apps/OpenSign/src/components/sidebar/Sidebar.js @@ -50,11 +50,11 @@ const Sidebar = ({ isOpen, closeSidebar }) => { ...newItem.children, { icon: "fa-light fa-building-memo", - title: "Departments", + title: "Teams", target: "_self", pageType: "", description: "", - objectId: "departments" + objectId: "teams" }, { icon: "fa-light fa-users fa-fw", diff --git a/apps/OpenSign/src/json/plansArr.js b/apps/OpenSign/src/json/plansArr.js index 7a347caa0..5178f2d27 100644 --- a/apps/OpenSign/src/json/plansArr.js +++ b/apps/OpenSign/src/json/plansArr.js @@ -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: @@ -57,10 +57,10 @@ const plans = [ ] }, { - planName: "OPENSIGN™ TEAM", + planName: "OPENSIGN™ TEAMS", currency: "$", - monthlyPrice: "100.99", - yearlyPrice: "329.99", + monthlyPrice: "40", + yearlyPrice: "20 <sup>/month/user</sup>", subtitle: "Exclusive Access to advanced features.", btnText: "Subscribe", url: diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 6fd6db379..0c9b5b812 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -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" diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 70e5e138a..a45f525e1 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -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"} diff --git a/apps/OpenSign/src/pages/DepartmentList.js b/apps/OpenSign/src/pages/TeamList.js similarity index 84% rename from apps/OpenSign/src/pages/DepartmentList.js rename to apps/OpenSign/src/pages/TeamList.js index 7b9fe3d9a..72269d8eb 100644 --- a/apps/OpenSign/src/pages/DepartmentList.js +++ b/apps/OpenSign/src/pages/TeamList.js @@ -11,7 +11,7 @@ import { isEnableSubscription } from "../constant/const"; import { checkIsSubscribedTeam } from "../constant/Utils"; import SubscribeCard from "../primitives/SubscribeCard"; -const heading = ["Sr.No", "Name", "Parent Department", "Is-Active"]; +const heading = ["Sr.No", "Name", "Parent Team", "Active"]; // const actions = [ // { // btnId: "1231", @@ -23,9 +23,9 @@ const heading = ["Sr.No", "Name", "Parent Department", "Is-Active"]; // } // ]; -const DepartmentList = () => { +const TeamList = () => { const recordperPage = 10; - const [departmentList, setDepartmentList] = useState([]); + const [teamList, setTeamList] = useState([]); const [isLoader, setIsLoader] = useState(false); const [isModal, setIsModal] = useState(false); const location = useLocation(); @@ -41,7 +41,7 @@ const DepartmentList = () => { const getPaginationRange = () => { const totalPageNumbers = 7; // Adjust this value to show more/less page numbers const pages = []; - const totalPages = Math.ceil(departmentList.length / recordperPage); + const totalPages = Math.ceil(teamList.length / recordperPage); if (totalPages <= totalPageNumbers) { for (let i = 1; i <= totalPages; i++) { pages.push(i); @@ -91,9 +91,9 @@ const DepartmentList = () => { }; const pageNumbers = getPaginationRange(); useEffect(() => { - fetchDepartmentList(); + fetchTeamList(); }, []); - async function fetchDepartmentList() { + async function fetchTeamList() { try { setIsLoader(true); if (isEnableSubscription) { @@ -101,20 +101,20 @@ const DepartmentList = () => { setIsSubscribe(getIsSubscribe); } const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - const department = new Parse.Query("contracts_Departments"); - department.equalTo("OrganizationId", { + const teamCls = new Parse.Query("contracts_Departments"); + teamCls.equalTo("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", objectId: extUser.OrganizationId.objectId }); - department.descending("createdAt"); - const departmentRes = await department.find(); - if (departmentRes.length > 0) { - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - setDepartmentList(_departmentRes); + 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 departmentlist", err); + console.log("Err in fetch teamlist", err); setIsAlert({ type: "danger", msg: "Something went wrong." }); } finally { setTimeout(() => { @@ -129,7 +129,7 @@ const DepartmentList = () => { // Get current list const indexOfLastDoc = currentPage * recordperPage; const indexOfFirstDoc = indexOfLastDoc - recordperPage; - const currentList = departmentList?.slice(indexOfFirstDoc, indexOfLastDoc); + const currentList = teamList?.slice(indexOfFirstDoc, indexOfLastDoc); const handleClose = () => { setIsActiveModal({}); @@ -143,40 +143,38 @@ const DepartmentList = () => { // // setIsDeleteModal({ [item.objectId]: true }); // // } // }; - const handleToggleBtn = (department) => { - setIsActiveModal({ [department.objectId]: true }); + const handleToggleBtn = (team) => { + setIsActiveModal({ [team.objectId]: true }); }; - const handleToggleSubmit = async (department) => { - const index = departmentList.findIndex( - (obj) => obj.objectId === department.objectId - ); + const handleToggleSubmit = async (team) => { + const index = teamList.findIndex((obj) => obj.objectId === team.objectId); if (index !== -1) { setIsActiveModal({}); - setIsActLoader({ [department.objectId]: true }); - const newArray = [...departmentList]; + setIsActLoader({ [team.objectId]: true }); + const newArray = [...teamList]; const IsActive = newArray[index].IsActive; newArray[index] = { ...newArray[index], IsActive: !IsActive }; - setDepartmentList(newArray); + setTeamList(newArray); try { - const departmentCls = new Parse.Object("contracts_Departments"); - departmentCls.id = department.objectId; - departmentCls.set("IsActive", !IsActive); - await departmentCls.save(); + const teamCls = new Parse.Object("contracts_Departments"); + teamCls.id = team.objectId; + teamCls.set("IsActive", !IsActive); + await teamCls.save(); setIsAlert({ type: "success", - msg: "Department disabled successfully." + msg: "Team disabled successfully." }); } catch (err) { setIsAlert({ type: "danger", msg: "something went wrong." }); - console.log("err in disable department", err); + console.log("err in disable team", err); } finally { setIsActLoader({}); setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); } } }; - const handleDepartmentInfo = (department) => { - setDepartmentList((prev) => [department, ...prev]); + const handleTeamInfo = (team) => { + setTeamList((prev) => [team, ...prev]); }; return ( <div className="relative"> @@ -199,9 +197,9 @@ const DepartmentList = () => { )} <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <div className="font-light"> - department list{" "} + Teams{" "} <span className="text-xs md:text-[13px] font-normal"> - <Tooltip message={"department list"} /> + <Tooltip message={"Teams"} /> </span> </div> <div className="cursor-pointer" onClick={() => handleFormModal()}> @@ -220,7 +218,7 @@ const DepartmentList = () => { </tr> </thead> <tbody className="text-[12px]"> - {departmentList?.length > 0 && ( + {teamList?.length > 0 && ( <> {currentList.map((item, index) => ( <tr className="border-y-[1px]" key={index}> @@ -235,7 +233,7 @@ const DepartmentList = () => { <td className="px-4 py-2 font-semibold"> {item?.ParentId?.Name || "-"} </td> - {item?.Name !== "All User" && ( + {item?.Name !== "All Users" && ( <td className="px-4 py-2 font-semibold"> <label className="cursor-pointer relative block items-center mb-0"> <input @@ -248,13 +246,12 @@ const DepartmentList = () => { {isActiveModal[item.objectId] && ( <ModalUi isOpen - title={"Department status"} + 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 - department? + 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"> @@ -299,7 +296,7 @@ const DepartmentList = () => { </table> </div> <div className="op-join flex flex-wrap items-center p-2"> - {departmentList.length > recordperPage && ( + {teamList.length > recordperPage && ( <button onClick={() => paginateBack()} className="op-join-item op-btn op-btn-sm" @@ -319,7 +316,7 @@ const DepartmentList = () => { {x} </button> ))} - {departmentList.length > recordperPage && ( + {teamList.length > recordperPage && ( <button onClick={() => paginateFront()} className="op-join-item op-btn op-btn-sm" @@ -328,7 +325,7 @@ const DepartmentList = () => { </button> )} </div> - {departmentList?.length <= 0 && ( + {teamList?.length <= 0 && ( <div className={`${ isDashboard ? "h-[317px]" : "" @@ -345,12 +342,12 @@ const DepartmentList = () => { </div> )} <ModalUi - title={"Add Department"} + title={"Add Team"} isOpen={isModal} handleClose={handleFormModal} > <AddDepartment - handleDepartmentInfo={handleDepartmentInfo} + handleTeamInfo={handleTeamInfo} closePopup={handleFormModal} /> </ModalUi> @@ -358,11 +355,11 @@ const DepartmentList = () => { )} {!isSubscribe && isEnableSubscription && !isLoader && ( <div data-tut="apisubscribe"> - <SubscribeCard plan={"TEAM"} /> + <SubscribeCard plan={"TEAMS"} price={"20"} /> </div> )} </div> ); }; -export default DepartmentList; +export default TeamList; diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 1f0d5df68..7c38bec57 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -10,15 +10,7 @@ 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", - "Department", - "Is-Active" -]; +const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"]; // const actions = []; const UserList = () => { const [userList, setUserList] = useState([]); @@ -165,7 +157,7 @@ const UserList = () => { }); } catch (err) { setIsAlert({ type: "danger", msg: "something went wrong." }); - console.log("err in disable department", err); + console.log("err in disable team", err); } finally { setIsActLoader({}); setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); @@ -196,9 +188,9 @@ const UserList = () => { )} <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <div className="font-light"> - User list{" "} + Users{" "} <span className="text-xs md:text-[13px] font-normal"> - <Tooltip message={"user list from departments"} /> + <Tooltip message={"users from Teams"} /> </span> </div> <div className="cursor-pointer" onClick={() => handleFormModal()}> @@ -385,7 +377,7 @@ const UserList = () => { )} {!isSubscribe && isEnableSubscription && !isLoader && ( <div data-tut="apisubscribe"> - <SubscribeCard plan={"TEAM"} /> + <SubscribeCard plan={"TEAMS"} price={"20"} /> </div> )} </div> diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 847bed726..46f832210 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -1379,7 +1379,7 @@ const ReportTable = (props) => { )} {isOption[item.objectId] && act.action === "option" && ( - <ul className="absolute -right-2 top-6 z-[20] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box"> + <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} @@ -1431,7 +1431,7 @@ const ReportTable = (props) => { options={departmentList} value={selectedDepartments} onChange={onChange} - closeMenuOnSelect={false} + closeMenuOnSelect required={true} noOptionsMessage={() => "Departments not found" @@ -1469,7 +1469,10 @@ const ReportTable = (props) => { > ✕ </div> - <SubscribeCard plan={"TEAM"} /> + <SubscribeCard + plan={"TEAMS"} + price={"20"} + /> </> )} </div> diff --git a/apps/OpenSign/src/primitives/SubscribeCard.js b/apps/OpenSign/src/primitives/SubscribeCard.js index 78f21001c..b0d3537f4 100644 --- a/apps/OpenSign/src/primitives/SubscribeCard.js +++ b/apps/OpenSign/src/primitives/SubscribeCard.js @@ -1,13 +1,16 @@ import React from "react"; import { useNavigate } from "react-router-dom"; -const SubscribeCard = ({ plan }) => { +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 {plan ? plan : "PRO"} Plan</h2> - <p>$29.99/month: Sign 100 docs using API. Only $0.15/doc after that.</p> + <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")} diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js index 75849cfb6..ff9420dc7 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js @@ -5,7 +5,7 @@ async function addDepartmentAndOrg(extUser) { orgCls.set('IsActive', true); const orgRes = await orgCls.save(null, { useMasterKey: true }); const departmentCls = new Parse.Object('contracts_Departments'); - departmentCls.set('Name', 'All User'); + departmentCls.set('Name', 'All Users'); departmentCls.set('OrganizationId', { __type: 'Pointer', className: 'contracts_Organizations',