diff --git a/apps/OpenSign/src/components/AddDepartment.js b/apps/OpenSign/src/components/AddTeam.js similarity index 64% rename from apps/OpenSign/src/components/AddDepartment.js rename to apps/OpenSign/src/components/AddTeam.js index 6af89e6f0..3c784738c 100644 --- a/apps/OpenSign/src/components/AddDepartment.js +++ b/apps/OpenSign/src/components/AddTeam.js @@ -1,38 +1,43 @@ import React, { useEffect, useState } from "react"; import Parse from "parse"; import Title from "./Title"; -import Alert from "../primitives/Alert"; import Loader from "../primitives/Loader"; -const AddDepartment = (props) => { +const AddTeam = (props) => { const [formdata, setFormdata] = useState({ name: "", - department: "" + team: "" }); const [isLoader, setIsLoader] = useState(false); - const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); - const [departmentList, setDepartmentList] = useState([]); + const [teamList, setTeamList] = useState([]); useEffect(() => { - getDepartmentList(); + getTeamList(); }, []); - const getDepartmentList = async () => { + const getTeamList = async () => { + setIsLoader(true); try { 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_Teams"); + teamCls.equalTo("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", objectId: extUser.OrganizationId.objectId }); - department.equalTo("IsActive", true); - const departmentRes = await department.find(); - if (departmentRes.length > 0) { - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - setDepartmentList(_departmentRes); + teamCls.equalTo("IsActive", true); + const teamRes = await teamCls.find(); + if (teamRes.length > 0) { + const _teamRes = JSON.parse(JSON.stringify(teamRes)); + const allUsersteam = _teamRes.find((x) => x.Name === "All Users"); + if (allUsersteam) { + setFormdata({ team: allUsersteam.objectId }); + } + setTeamList(_teamRes); } } catch (err) { - console.log("Err in fetch top level departmentlist", err); + console.log("Err in fetch top level teamList", err); + } finally { + setIsLoader(false); } }; @@ -45,24 +50,24 @@ const AddDepartment = (props) => { e.stopPropagation(); // Extracting values except for the 'name' key let updatedAncestors = []; - if (formdata.department) { - const Ancestors = departmentList.find( - (x) => x.objectId === formdata.department + if (formdata.team) { + const Ancestors = teamList.find( + (x) => x.objectId === formdata.team )?.Ancestors; if (Ancestors && Ancestors.length > 0) { updatedAncestors = Ancestors.map((x) => ({ __type: "Pointer", - className: "contracts_Departments", + className: "contracts_Teams", objectId: x.objectId })); } else { - const AllUser = departmentList.find((x) => x.objectId === "All Users"); + const AllUser = teamList.find((x) => x.objectId === "All Users"); updatedAncestors = [ AllUser, { __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department + className: "contracts_Teams", + objectId: formdata.team } ]; } @@ -70,45 +75,43 @@ const AddDepartment = (props) => { try { const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; setIsLoader(true); - const department = new Parse.Query("contracts_Departments"); - department.equalTo("Name", formdata.name); + const team = new Parse.Query("contracts_Teams"); + team.equalTo("Name", formdata.name); if (updatedAncestors.length > 0) { const ParentId = updatedAncestors[updatedAncestors.length - 1]; - department.equalTo("ParentId", ParentId); + team.equalTo("ParentId", ParentId); } if (localUser && localUser.OrganizationId) { - department.equalTo("OrganizationId", { + team.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." }); + const isTeam = await team.first(); + if (isTeam) { + props.setIsAlert({ type: "info", msg: "Teams already exists." }); setIsLoader(false); } else { - const newDepartment = new Parse.Object("contracts_Departments"); - newDepartment.set("Name", formdata.name); + const newTeam = new Parse.Object("contracts_Teams"); + newTeam.set("Name", formdata.name); if (updatedAncestors.length > 0) { const ParentId = updatedAncestors[updatedAncestors.length - 1]; - newDepartment.set("ParentId", ParentId); - newDepartment.set("Ancestors", updatedAncestors); + newTeam.set("ParentId", ParentId); + newTeam.set("Ancestors", updatedAncestors); } if (localUser && localUser.OrganizationId) { - newDepartment.set("OrganizationId", { + newTeam.set("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", objectId: localUser.OrganizationId.objectId }); } - const newdepartmentRes = await newDepartment.save(); + const newTeamRes = await newTeam.save(); if (updatedAncestors.length > 0) { - const ParentId = departmentList.find( - (x) => x.objectId === formdata.department - ); + const ParentId = teamList.find((x) => x.objectId === formdata.team); props.handleTeamInfo({ - objectId: newdepartmentRes.id, + objectId: newTeamRes.id, Name: formdata.name, ParentId: ParentId, Ancestors: updatedAncestors, @@ -116,7 +119,7 @@ const AddDepartment = (props) => { }); } else { props.handleTeamInfo({ - objectId: newdepartmentRes.id, + objectId: newTeamRes.id, Name: formdata.name, ParentId: "", Ancestors: "", @@ -129,20 +132,18 @@ const AddDepartment = (props) => { } setFormdata({ name: "", - department: { name: "", objectId: "" } + team: { name: "", objectId: "" } }); - setIsAlert({ + props.setIsAlert({ type: "success", - msg: "Department created successfully." + msg: "Team created successfully." }); } } catch (err) { - console.log("err in save department", err); - setIsAlert({ type: "danger", msg: "Something went wrong." }); + console.log("err in save team", err); + props.setIsAlert({ type: "danger", msg: "Something went wrong." }); } finally { - setTimeout(() => { - setIsAlert({ type: "success", msg: "" }); - }, 1500); + setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); setIsLoader(false); } }; @@ -151,7 +152,7 @@ const AddDepartment = (props) => { const handleReset = () => { setFormdata({ name: "", - department: { name: "", objectId: "" } + team: { name: "", objectId: "" } }); }; const handleChange = (e) => { @@ -160,12 +161,7 @@ const AddDepartment = (props) => { return (
- - {isAlert.msg && ( - <Alert type={isAlert.type}> - <div className="ml-3">{isAlert.msg}</div> - </Alert> - )} + <Title title="Add Team" /> {isLoader && ( <div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box"> <Loader /> @@ -191,7 +187,6 @@ 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="phone" @@ -200,16 +195,13 @@ const AddDepartment = (props) => { Parent Team </label> <select - value={formdata.department} + value={formdata.team} onChange={(e) => handleDropdown(e)} - name="department" + name="team" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" > - <option defaultValue={""} value={""}> - select - </option> - {departmentList.length > 0 && - departmentList.map((x) => ( + {teamList.length > 0 && + teamList.map((x) => ( <option key={x.objectId} value={x.objectId}> {x.Name} </option> @@ -234,4 +226,4 @@ const AddDepartment = (props) => { ); }; -export default AddDepartment; +export default AddTeam; diff --git a/apps/OpenSign/src/components/AddUser.js b/apps/OpenSign/src/components/AddUser.js index 38e3de2ea..82bac3ced 100644 --- a/apps/OpenSign/src/components/AddUser.js +++ b/apps/OpenSign/src/components/AddUser.js @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import Parse from "parse"; import axios from "axios"; import Title from "./Title"; -import Alert from "../primitives/Alert"; import Loader from "../primitives/Loader"; import { copytoData } from "../constant/Utils"; function generatePassword(length) { @@ -22,35 +21,34 @@ const AddUser = (props) => { name: "", phone: "", email: "", - department: "", + team: "", password: "", role: "" }); const [isLoader, setIsLoader] = useState(false); - const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); - const [departmentList, setDepartmentList] = useState([]); + const [teamList, setTeamList] = useState([]); const role = ["OrgAdmin", "Manager", "User"]; const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); useEffect(() => { - getDepartmentList(); + getTeamList(); }, []); - const getDepartmentList = async () => { + const getTeamList = 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", { + const team = new Parse.Query("contracts_Teams"); + team.equalTo("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", objectId: extUser.OrganizationId.objectId }); - department.equalTo("IsActive", true); - const departmentRes = await department.find(); - if (departmentRes.length > 0) { - const _departmentRes = JSON.parse(JSON.stringify(departmentRes)); - setDepartmentList(_departmentRes); + team.equalTo("IsActive", true); + const teamRes = await team.find(); + if (teamRes.length > 0) { + const _teamRes = JSON.parse(JSON.stringify(teamRes)); + setTeamList(_teamRes); } }; const checkUserExist = async () => { @@ -75,10 +73,10 @@ const AddUser = (props) => { setIsLoader(true); const res = await checkUserExist(); if (res) { - setIsAlert({ type: "danger", msg: "User already exist." }); + props.setIsAlert({ type: "danger", msg: "User already exist." }); setIsLoader(false); setTimeout(() => { - setIsAlert({ type: "success", msg: "" }); + props.setIsAlert({ type: "success", msg: "" }); }, 1000); } else { try { @@ -89,12 +87,12 @@ const AddUser = (props) => { } extUser.set("Email", formdata.email); extUser.set("UserRole", `contracts_${formdata.role}`); - if (formdata?.department) { - extUser.set("DepartmentIds", [ + if (formdata?.team) { + extUser.set("TeamIds", [ { __type: "Pointer", - className: "contracts_Departments", - objectId: formdata.department + className: "contracts_Teams", + objectId: formdata.team } ]); } @@ -162,12 +160,10 @@ 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 + if (formdata?.team) { + const team = teamList.find((x) => x.objectId === formdata.team); + parseData.TeamIds = parseData.TeamIds.map((y) => + y.objectId === team.objectId ? team : y ); } props.handleUserData(parseData); @@ -178,7 +174,7 @@ const AddUser = (props) => { name: "", email: "", phone: "", - department: "", + team: "", role: "" }); } @@ -224,12 +220,10 @@ 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 + if (formdata?.team) { + const team = teamList.find((x) => x.objectId === formdata.team); + parseData.TeamIds = parseData.TeamIds.map((y) => + y.objectId === team.objectId ? team : y ); } @@ -240,7 +234,7 @@ const AddUser = (props) => { name: "", email: "", phone: "", - department: "", + team: "", role: "" }); } @@ -248,9 +242,9 @@ const AddUser = (props) => { } catch (err) { console.log("err", err); setIsLoader(false); - setIsAlert({ type: "danger", msg: "something went wrong." }); + props.setIsAlert({ type: "danger", msg: "something went wrong." }); } finally { - setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); } } }; @@ -261,7 +255,7 @@ const AddUser = (props) => { name: "", email: "", phone: "", - department: "", + team: "", role: "" }); if (props.closePopup) { @@ -274,19 +268,12 @@ const AddUser = (props) => { const copytoclipboard = (text) => { copytoData(text); - setIsAlert({ type: "success", msg: "Copied" }); - setTimeout(() => { - setIsAlert({ type: "success", msg: "" }); - }, 1500); // Reset copied state after 1.5 seconds + props.setIsAlert({ type: "success", msg: "Copied" }); + setTimeout(() => props.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"} /> - {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 /> @@ -340,10 +327,10 @@ const AddUser = (props) => { <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]" + className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer " ></i> </div> - <div className="text-[12px] ml-2 mb-0 text-[red]"> + <div className="text-[12px] ml-2 mb-0 text-[red] select-none"> Password will only be generated once; make sure to copy it. </div> </div> @@ -370,20 +357,20 @@ const AddUser = (props) => { htmlFor="phone" className="block text-xs text-gray-700 font-semibold" > - Team + Team<span className="text-[red] text-[13px]"> *</span> </label> <select - value={formdata.department} + value={formdata.team} onChange={(e) => handleChange(e)} - name="department" + name="team" className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" required > <option defaultValue={""} value={""}> select </option> - {departmentList.length > 0 && - departmentList.map((x) => ( + {teamList.length > 0 && + teamList.map((x) => ( <option key={x.objectId} value={x.objectId}> {x.Name} </option> @@ -395,13 +382,14 @@ const AddUser = (props) => { htmlFor="phone" className="block text-xs text-gray-700 font-semibold" > - Role + 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 defaultValue={""} value={""}> select diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 8a41d5c86..809dc0a43 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -120,7 +120,7 @@ function Login() { email: currentUser.get("email") }) .then((extUser) => { - if (roles) { + if (extUser && roles) { userRoles = roles; let _currentRole = ""; const valuesToExclude = [ @@ -149,7 +149,7 @@ function Login() { ); let tenentInfo = []; const results = [extUser]; - if (results) { + if (extUser) { let extendedInfo_stringify = JSON.stringify(results); @@ -160,45 +160,7 @@ function Login() { 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 { + if (extendedInfo.length > 0) { extendedInfo.forEach((x) => { if (x.TenantId) { let obj = { @@ -488,7 +450,7 @@ function Login() { async (result) => { let tenentInfo = []; const results = [result]; - if (results) { + if (results && results.length > 0) { let extendedInfo_stringify = JSON.stringify(results); localStorage.setItem( diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 6b467f358..2998c2ba9 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -1950,29 +1950,37 @@ function PlaceHolderSign() { {!mailStatus && ( <div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div> )} - {isCurrUser && ( + <div + className={ + mailStatus === "success" + ? "flex justify-center mt-1" + : "" + } + > + {isCurrUser && ( + <button + onClick={() => { + handleRecipientSign(); + }} + type="button" + className="op-btn op-btn-primary mr-1" + > + Yes + </button> + )} + <button onClick={() => { - handleRecipientSign(); + setIsSend(false); + setSignerPos([]); + navigate("/report/1MwEuxLEkF"); }} type="button" - className="op-btn op-btn-primary mr-1" + className="op-btn op-btn-ghost" > - Yes + {isCurrUser ? "No" : "Close"} </button> - )} - - <button - onClick={() => { - setIsSend(false); - setSignerPos([]); - navigate("/report/1MwEuxLEkF"); - }} - type="button" - className="op-btn op-btn-ghost" - > - {isCurrUser ? "No" : "Close"} - </button> + </div> </div> </ModalUi> <ModalUi diff --git a/apps/OpenSign/src/pages/TeamList.js b/apps/OpenSign/src/pages/TeamList.js index 72269d8eb..5820e6729 100644 --- a/apps/OpenSign/src/pages/TeamList.js +++ b/apps/OpenSign/src/pages/TeamList.js @@ -6,22 +6,22 @@ import { useLocation } from "react-router-dom"; import Tooltip from "../primitives/Tooltip"; import ModalUi from "../primitives/ModalUi"; import pad from "../assets/images/pad.svg"; -import AddDepartment from "../components/AddDepartment"; +import AddTeam from "../components/AddTeam"; import { isEnableSubscription } from "../constant/const"; import { checkIsSubscribedTeam } from "../constant/Utils"; import SubscribeCard from "../primitives/SubscribeCard"; const heading = ["Sr.No", "Name", "Parent Team", "Active"]; -// const actions = [ -// { -// btnId: "1231", -// hoverLabel: "Edit", -// btnColor: "op-btn-primary", -// btnIcon: "fa-light fa-pen", -// redirectUrl: "draftDocument", -// action: "redirect" -// } -// ]; +const actions = [ + { + btnId: "1231", + hoverLabel: "Edit", + btnColor: "op-btn-primary", + btnIcon: "fa-light fa-pen", + redirectUrl: "", + action: "edit" + } +]; const TeamList = () => { const recordperPage = 10; @@ -32,10 +32,11 @@ const TeamList = () => { const isDashboard = location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; const [currentPage, setCurrentPage] = useState(1); - const [isActiveModal, setIsActiveModal] = useState(false); + const [isActiveModal, setIsActiveModal] = useState({}); const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const [isActLoader, setIsActLoader] = useState({}); const [isSubscribe, setIsSubscribe] = useState(false); + const [isEditModal, setIsEditModal] = useState({}); const startIndex = (currentPage - 1) * recordperPage; // user per page const getPaginationRange = () => { @@ -101,7 +102,7 @@ const TeamList = () => { setIsSubscribe(getIsSubscribe); } const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; - const teamCls = new Parse.Query("contracts_Departments"); + const teamCls = new Parse.Query("contracts_Teams"); teamCls.equalTo("OrganizationId", { __type: "Pointer", className: "contracts_Organizations", @@ -109,7 +110,7 @@ const TeamList = () => { }); teamCls.descending("createdAt"); const teamRes = await teamCls.find(); - if (teamCls.length > 0) { + if (teamRes.length > 0) { const _teamRes = JSON.parse(JSON.stringify(teamRes)); setTeamList(_teamRes); } @@ -133,16 +134,17 @@ const TeamList = () => { const handleClose = () => { setIsActiveModal({}); + setIsEditModal({}); }; // 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 === "edit") { + setIsEditModal({ [item.objectId]: true }); + } + }; const handleToggleBtn = (team) => { setIsActiveModal({ [team.objectId]: true }); }; @@ -156,7 +158,7 @@ const TeamList = () => { newArray[index] = { ...newArray[index], IsActive: !IsActive }; setTeamList(newArray); try { - const teamCls = new Parse.Object("contracts_Departments"); + const teamCls = new Parse.Object("contracts_Teams"); teamCls.id = team.objectId; teamCls.set("IsActive", !IsActive); await teamCls.save(); @@ -176,6 +178,33 @@ const TeamList = () => { const handleTeamInfo = (team) => { setTeamList((prev) => [team, ...prev]); }; + const handleEditChange = (e, team) => { + const index = teamList.findIndex((obj) => obj.objectId === team.objectId); + if (index !== -1) { + const newArray = [...teamList]; + newArray[index] = { ...newArray[index], Name: e.target.value }; + setTeamList(newArray); + } + }; + const updateTeamName = async (e, team) => { + e.preventDefault(); + e.stopPropagation(); + setIsActLoader({ [team.objectId]: true }); + setIsEditModal({}); + try { + const teamCls = new Parse.Object("contracts_Teams"); + teamCls.id = team?.objectId; + teamCls.set("Name", team.Name); + await teamCls.save(); + setIsAlert({ type: "success", msg: "Team Update successfully." }); + } catch (Err) { + console.log("Err in update team name"), Err; + setIsAlert({ type: "danger", msg: "Something went wrong." }); + } finally { + setIsActLoader({}); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); + } + }; return ( <div className="relative"> {isLoader && ( @@ -233,61 +262,101 @@ const TeamList = () => { <td className="px-4 py-2 font-semibold"> {item?.ParentId?.Name || "-"} </td> - {item?.Name !== "All Users" && ( - <td className="px-4 py-2 font-semibold"> - <label className="cursor-pointer relative block items-center mb-0"> - <input - type="checkbox" - className="op-toggle transition-all op-toggle-secondary" - checked={item?.IsActive} - onChange={() => handleToggleBtn(item)} - /> - </label> - {isActiveModal[item.objectId] && ( - <ModalUi - isOpen - title={"Team status"} - handleClose={handleClose} - > - <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> - Are you sure you want to disable this team? + <td className="px-4 py-2 font-semibold"> + {item?.Name !== "All Users" && ( + <> + <label className="cursor-pointer relative block items-center mb-0"> + <input + type="checkbox" + className="op-toggle transition-all op-toggle-secondary" + checked={item?.IsActive} + onChange={() => handleToggleBtn(item)} + /> + </label> + {isActiveModal[item.objectId] && ( + <ModalUi + isOpen + title={"Team status"} + handleClose={handleClose} + > + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + Are you sure you want to disable this + team? + </div> + <hr className="bg-[#ccc] mt-4 " /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleToggleSubmit(item)} + className="op-btn op-btn-primary" + > + Yes + </button> + <button + onClick={handleClose} + className="op-btn op-btn-secondary" + > + No + </button> + </div> </div> - <hr className="bg-[#ccc] mt-4 " /> - <div className="flex items-center mt-3 gap-2 text-white"> + </ModalUi> + )} + </> + )} + </td> + <td className="px-3 py-2 text-white flex flex-wrap gap-1"> + {item?.Name !== "All Users" && ( + <> + {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> + ))} + {isEditModal[item.objectId] && ( + <ModalUi + isOpen + title={"Edit Team"} + handleClose={handleClose} + > + <form + className="m-[20px]" + onSubmit={(e) => updateTeamName(e, item)} + > + <label className="text-xs font-semibold text-base-content ml-1"> + Name of Team{" "} + <span className="text-[red] text-[13px]"> + * + </span> + </label> + <input + className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-base-content w-full text-xs" + value={item.Name} + onChange={(e) => + handleEditChange(e, item) + } + required + /> <button - onClick={() => handleToggleSubmit(item)} - className="op-btn op-btn-primary" + type="submit" + className="op-btn op-btn-primary mt-3" > - Yes + Save </button> - <button - onClick={handleClose} - className="op-btn op-btn-secondary" - > - No - </button> - </div> - </div> - </ModalUi> - )} - </td> - )} - {/* <td className="px-3 py-2 text-white flex flex-wrap gap-1"> - {actions?.length > 0 && - actions.map((act, index) => ( - <button - key={index} - onClick={() => handleActionBtn(act, item)} - title={act.hoverLabel} - className={`${ - act?.btnColor ? act.btnColor : "" - } op-btn op-btn-sm w-[50px]`} - > - <i className={act.btnIcon}></i> - </button> - ))} - </td> */} + </form> + </ModalUi> + )} + </> + )} + </td> </tr> ))} </> @@ -346,7 +415,8 @@ const TeamList = () => { isOpen={isModal} handleClose={handleFormModal} > - <AddDepartment + <AddTeam + setIsAlert={setIsAlert} handleTeamInfo={handleTeamInfo} closePopup={handleFormModal} /> diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index 55ad4f8df..bf96da429 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -1400,6 +1400,7 @@ const TemplatePlaceholder = () => { <button onClick={() => { setIsCreateDocModal(false); + navigate("/report/6TeaPr321t"); }} type="button" className="op-btn op-btn-secondary ml-2" diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 7c38bec57..aad0af541 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -226,47 +226,48 @@ const UserList = () => { <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? + <td className="px-4 py-2">{formatRow(item.TeamIds)}</td> + {item.UserRole !== "contracts_Admin" && ( + <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> - <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> + </ModalUi> + )} + </td> + )} {/* <td className="px-3 py-2 text-white grid grid-cols-2"> {actions?.length > 0 && actions.map((act, index) => ( @@ -369,6 +370,7 @@ const UserList = () => { handleClose={handleFormModal} > <AddUser + setIsAlert={setIsAlert} handleUserData={handleUserData} closePopup={handleFormModal} /> diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 46f832210..99a75330e 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -57,9 +57,9 @@ const ReportTable = (props) => { 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 [teamList, setTeamList] = useState([]); + const [selectedTeam, setSelectedTeam] = useState([]); + const onChange = (selectedOptions) => setSelectedTeam(selectedOptions); // const [selectedPublicRole, setSelectedPublicRole] = useState(""); // const [isCelebration, setIsCelebration] = useState(false); // const [currentLists, setCurrentLists] = useState([]); @@ -136,34 +136,34 @@ const ReportTable = (props) => { // below useEffect reset currenpage to 1 if user change route useEffect(() => { checkTourStatus(); - fetchDepartmentList(); + fetchTeamList(); return () => setCurrentPage(1); // eslint-disable-next-line }, []); - // `fetchDepartmentList` is used to fetch department list for share with functionality - const fetchDepartmentList = async () => { + // `fetchTeamList` is used to fetch team list for share with functionality + const fetchTeamList = 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", { + const team = new Parse.Query("contracts_Teams"); + team.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) => ({ + team.equalTo("IsActive", true); + const teamtRes = await team.find(); + if (teamtRes.length > 0) { + const _teamRes = JSON.parse(JSON.stringify(teamtRes)); + const formatedList = _teamRes.map((x) => ({ label: x.Name, value: x.objectId })); - setDepartmentList(formatedList); + setTeamList(formatedList); } } catch (err) { - console.log("Err in fetch top level departmentlist", err); + console.log("Err in fetch top level teamlist", err); } } }; @@ -353,12 +353,12 @@ const ReportTable = (props) => { 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 + // below code is used to get existing sharewith teams and formated them as per react-select const formatedList = item?.SharedWith.map((x) => ({ label: x.Name, value: x.objectId })); - setSelectedDepartments(formatedList); + setSelectedTeam(formatedList); } setIsShareWith({ [item.objectId]: true }); } @@ -979,7 +979,7 @@ const ReportTable = (props) => { // setTimeout(() => setIsAlert(false), 1500); // }; - // `handleShareWith` is used to save departments in sharedWith field + // `handleShareWith` is used to save teams in sharedWith field const handleShareWith = async (e, template) => { e.preventDefault(); e.stopPropagation(); @@ -988,12 +988,12 @@ const ReportTable = (props) => { try { const templateCls = new Parse.Object("contracts_Template"); templateCls.id = template.objectId; - const departmentArr = selectedDepartments.map((x) => ({ + const teamArr = selectedTeam.map((x) => ({ __type: "Pointer", - className: "contracts_Departments", + className: "contracts_Teams", objectId: x.value })); - templateCls.set("SharedWith", departmentArr); + templateCls.set("SharedWith", teamArr); const res = await templateCls.save(); if (res) { setIsAlert(true); @@ -1411,54 +1411,76 @@ const ReportTable = (props) => { <div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative"> {isSubscribe && isEnableSubscription && ( <> - <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> - Share with - </h3> - <div - className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" - onClick={() => setIsShareWith({})} - > - ✕ - </div> - <form - className="h-full w-full z-[1300] px-2 mt-3" - onSubmit={(e) => handleShareWith(e, item)} - > - <Select - // onSortEnd={onSortEnd} - distance={4} - isMulti - options={departmentList} - value={selectedDepartments} - onChange={onChange} - closeMenuOnSelect - required={true} - noOptionsMessage={() => - "Departments not found" - } - unstyled - classNames={{ - control: () => - "op-input op-input-bordered op-input-sm border-gray-400 focus:outline-none hover:border-base-content w-full h-full text-[11px]", - valueContainer: () => - "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", - multiValue: () => - "op-badge op-badge-primary h-full text-[11px]", - multiValueLabel: () => "mb-[2px]", - menu: () => - "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", - menuList: () => - "shadow-md rounded-lg overflow-hidden", - option: () => - "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", - noOptionsMessage: () => - "p-2 bg-base-200 rounded-lg m-1 p-2" - }} - /> - <button className="op-btn op-btn-primary ml-[10px] my-3"> - Submit - </button> - </form> + {item?.Signers?.length > 0 ? ( + <div className="h-[150px] flex justify-center items-center mx-2"> + <div + className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" + onClick={() => setIsShareWith({})} + > + ✕ + </div> + <div className="text-base-content text-base text-center"> + You cannot share a template if any + roles already have contacts assigned. + Please remove all contact assignments + from the roles before sharing the + template. + </div> + </div> + ) : ( + <> + <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={teamList} + value={selectedTeam} + onChange={onChange} + closeMenuOnSelect + required={true} + noOptionsMessage={() => + "Team not found" + } + unstyled + classNames={{ + control: () => + "op-input op-input-bordered op-input-sm border-gray-400 focus:outline-none hover:border-base-content w-full h-full text-[11px]", + valueContainer: () => + "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", + multiValue: () => + "op-badge op-badge-primary h-full text-[11px]", + multiValueLabel: () => "mb-[2px]", + menu: () => + "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", + menuList: () => + "shadow-md rounded-lg overflow-hidden", + option: () => + "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", + noOptionsMessage: () => + "p-2 bg-base-200 rounded-lg m-1 p-2" + }} + /> + <button className="op-btn op-btn-primary ml-[10px] my-3"> + Submit + </button> + </form> + </> + )} </> )} {!isSubscribe && isEnableSubscription && ( diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index af4165b12..b4f01c77b 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -46,7 +46,7 @@ import ssoSignin from './parsefunction/ssoSignin.js'; import isextenduser from './parsefunction/isextenduser.js'; import getUserByOrg from './parsefunction/getUserByOrg.js'; import getUserListByOrg from './parsefunction/getUserListByOrg.js'; -import DepartmentsAftersave from './parsefunction/DepartmentsAftersave.js'; +import TeamsAftersave from './parsefunction/TeamsAftersave.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. @@ -54,7 +54,7 @@ Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave); Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave); -Parse.Cloud.afterSave('contracts_Departments', DepartmentsAftersave); +Parse.Cloud.afterSave('contracts_Teams', TeamsAftersave); 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); diff --git a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js index c3ff9e910..35e65dbc1 100644 --- a/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js +++ b/apps/OpenSignServer/cloud/parsefunction/GetTemplate.js @@ -24,18 +24,16 @@ export default async function GetTemplate(request) { const extUserQuery = new Parse.Query('contracts_Users'); extUserQuery.equalTo('Email', userRes.data.email); - extUserQuery.include('DepartmentIds'); + extUserQuery.include('TeamIds'); 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]) - ); + if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) { + let teamsArr = []; + _extUser?.TeamIds?.forEach(x => (teamsArr = [...teamsArr, ...x.Ancestors])); // Create the first query const sharedWithQuery = new Parse.Query('contracts_Template'); - sharedWithQuery.containedIn('SharedWith', departmentArr); + sharedWithQuery.containedIn('SharedWith', teamsArr); // Create the second query const createdByQuery = new Parse.Query('contracts_Template'); diff --git a/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js index ff9420dc7..445452101 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js +++ b/apps/OpenSignServer/cloud/parsefunction/SubscriptionAftersave.js @@ -1,18 +1,34 @@ -async function addDepartmentAndOrg(extUser) { +async function addTeamAndOrg(extUser) { try { const orgCls = new Parse.Object('contracts_Organizations'); orgCls.set('Name', extUser.Company); orgCls.set('IsActive', true); + orgCls.set('ExtUserId', { + __type: 'Pointer', + className: 'contracts_Users', + objectId: extUser?.objectId, + }); + orgCls.set('CreatedBy', { + __type: 'Pointer', + className: '_User', + objectId: extUser?.UserId?.objectId, + }); + orgCls.set('TenantId', { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: extUser?.TenantId?.objectId, + }); + const orgRes = await orgCls.save(null, { useMasterKey: true }); - const departmentCls = new Parse.Object('contracts_Departments'); - departmentCls.set('Name', 'All Users'); - departmentCls.set('OrganizationId', { + const teamCls = new Parse.Object('contracts_Teams'); + teamCls.set('Name', 'All Users'); + teamCls.set('OrganizationId', { __type: 'Pointer', className: 'contracts_Organizations', objectId: orgRes.id, }); - departmentCls.set('IsActive', true); - const departmentRes = await departmentCls.save(null, { useMasterKey: true }); + teamCls.set('IsActive', true); + const teamRes = await teamCls.save(null, { useMasterKey: true }); const updateUser = new Parse.Object('contracts_Users'); updateUser.id = extUser.objectId; updateUser.set('UserRole', 'contracts_Admin'); @@ -21,16 +37,16 @@ async function addDepartmentAndOrg(extUser) { className: 'contracts_Organizations', objectId: orgRes.id, }); - updateUser.set('DepartmentIds', [ + updateUser.set('TeamIds', [ { __type: 'Pointer', - className: 'contracts_Departments', - objectId: departmentRes.id, + className: 'contracts_Teams', + objectId: teamRes.id, }, ]); const extUserRes = await updateUser.save(null, { useMasterKey: true }); } catch (err) { - console.log('err in add department, role, org', err); + console.log('err in add team, role, org', err); } } @@ -50,7 +66,7 @@ export default async function SubscriptionAftersave(request) { if (extUserRes) { const extUser = JSON.parse(JSON.stringify(extUserRes)); if (extUser?.UserRole !== 'contracts_Admin') { - await addDepartmentAndOrg(extUser); + await addTeamAndOrg(extUser); } } } @@ -71,7 +87,7 @@ export default async function SubscriptionAftersave(request) { if (extUserRes) { const extUser = JSON.parse(JSON.stringify(extUserRes)); if (extUser?.UserRole !== 'contracts_Admin') { - await addDepartmentAndOrg(extUser); + await addTeamAndOrg(extUser); } } } diff --git a/apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js b/apps/OpenSignServer/cloud/parsefunction/TeamsAftersave.js similarity index 74% rename from apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js rename to apps/OpenSignServer/cloud/parsefunction/TeamsAftersave.js index 3feb37466..36566933b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/DepartmentsAftersave.js +++ b/apps/OpenSignServer/cloud/parsefunction/TeamsAftersave.js @@ -1,4 +1,4 @@ -export default async function DepartmentsAftersave(req) { +export default async function TeamsAftersave(req) { if (!req.original) { try { const Ancestors = req.object.get('Ancestors'); @@ -8,7 +8,7 @@ export default async function DepartmentsAftersave(req) { ...Ancestors, { __type: 'Pointer', - className: 'contracts_Departments', + className: 'contracts_Teams', objectId: req.object.id, }, ]; @@ -16,7 +16,7 @@ export default async function DepartmentsAftersave(req) { updatedAncestors = [ { __type: 'Pointer', - className: 'contracts_Departments', + className: 'contracts_Teams', objectId: req.object.id, }, ]; @@ -24,7 +24,7 @@ export default async function DepartmentsAftersave(req) { req.object.set('Ancestors', updatedAncestors); await req.object.save(null, { useMasterKey: true }); } catch (err) { - console.log('Err in department aftersave', err); + console.log('Err in team aftersave', err); } } } diff --git a/apps/OpenSignServer/cloud/parsefunction/getReport.js b/apps/OpenSignServer/cloud/parsefunction/getReport.js index 2d8fe2aa9..8f7aec1e4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getReport.js +++ b/apps/OpenSignServer/cloud/parsefunction/getReport.js @@ -28,19 +28,17 @@ export default async function getReport(request) { if (reportId == '6TeaPr321t') { const extUserQuery = new Parse.Query('contracts_Users'); extUserQuery.equalTo('Email', userRes.data.email); - extUserQuery.include('DepartmentIds'); + extUserQuery.include('TeamIds'); 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]) - ); + if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) { + let teamArr = []; + _extUser?.TeamIds?.forEach(x => (teamArr = [...teamArr, ...x.Ancestors])); strParams = JSON.stringify({ ...params, $or: [ - { SharedWith: { $in: departmentArr } }, + { SharedWith: { $in: teamArr } }, { ExtUserPtr: { __type: 'Pointer', diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js b/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js index 060c68134..86815ac39 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserByOrg.js @@ -10,7 +10,7 @@ export default async function getUserByOrg(req) { } else { try { const extUser = new Parse.Query('contracts_Users'); - extUser.include('DepartmentIds'); + extUser.include('TeamIds'); extUser.equalTo('OrganizationId', orgPtr); const userRes = await extUser.first({ useMasterKey: true }); if (userRes.length > 0) { diff --git a/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js index b5fa1a6ab..ffb173a6c 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js +++ b/apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js @@ -11,7 +11,7 @@ export default async function getUserListByOrg(req) { try { const extUser = new Parse.Query('contracts_Users'); extUser.equalTo('OrganizationId', orgPtr); - extUser.include('DepartmentIds'); + extUser.include('TeamIds'); extUser.descending('createdAt'); const userRes = await extUser.find({ useMasterKey: true }); if (userRes.length > 0) {