From 2c715a77a2d0a2a8ddd03379ec9723cf926c5cc0 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Thu, 27 Jun 2024 19:00:55 +0530 Subject: [PATCH] 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 {