diff --git a/apps/OpenSign/public/locales/en/translation.json b/apps/OpenSign/public/locales/en/translation.json index fdc74a522..72ff69023 100644 --- a/apps/OpenSign/public/locales/en/translation.json +++ b/apps/OpenSign/public/locales/en/translation.json @@ -636,6 +636,8 @@ "quota-mail-tip":"Tip: You can still sign <1>unlimited documents by manually sharing the signing request links.", "quota-mail-head":"Quota Reached", "unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.", - "sent-this-month":"Sent this month" + "sent-this-month":"Sent this month", + "available-seats":"Available seats", + "buy-users":"Buy more users" } diff --git a/apps/OpenSign/public/locales/fr/translation.json b/apps/OpenSign/public/locales/fr/translation.json index c4eaf9200..4373ff1c2 100644 --- a/apps/OpenSign/public/locales/fr/translation.json +++ b/apps/OpenSign/public/locales/fr/translation.json @@ -635,6 +635,7 @@ "quota-mail-tip-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents en partageant manuellement les liens de demande de signature.", "quota-mail-head":"Quota atteint", "unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.", - "sent-this-month":"envoyé ce mois-ci" - + "sent-this-month":"envoyé ce mois-ci", + "available-seats":"Disponible sièges", + "buy-users":"Acheter plus d'utilisateurs" } diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index ee0187aef..085784e4e 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -252,9 +252,9 @@ const HomeLayout = () => { } }; - const handleLoginBtn = () => { + const handleLoginBtn = async () => { try { - Parse?.User?.logOut(); + await Parse?.User?.logOut(); } catch (err) { console.log("err ", err); } finally { diff --git a/apps/OpenSign/src/pages/PlanSubscriptions.js b/apps/OpenSign/src/pages/PlanSubscriptions.js index 8428487ef..96d283ccb 100644 --- a/apps/OpenSign/src/pages/PlanSubscriptions.js +++ b/apps/OpenSign/src/pages/PlanSubscriptions.js @@ -121,9 +121,9 @@ const PlanSubscriptions = () => { }; const handleLogout = async () => { try { - Parse?.User?.logOut(); + await Parse?.User?.logOut(); } catch (err) { - console.log("Err", err); + console.log("Err while logging out", err); } let appdata = localStorage.getItem("userSettings"); let applogo = localStorage.getItem("appLogo"); diff --git a/apps/OpenSign/src/pages/UserList.js b/apps/OpenSign/src/pages/UserList.js index 1359e0261..2bb42c555 100644 --- a/apps/OpenSign/src/pages/UserList.js +++ b/apps/OpenSign/src/pages/UserList.js @@ -9,7 +9,7 @@ import Tooltip from "../primitives/Tooltip"; import AddUser from "../components/AddUser"; import SubscribeCard from "../primitives/SubscribeCard"; import { isEnableSubscription } from "../constant/const"; -import { checkIsSubscribed } from "../constant/Utils"; +import { fetchSubscriptionInfo } from "../constant/Utils"; import Title from "../components/Title"; import { validplan } from "../json/plansArr"; import { useTranslation } from "react-i18next"; @@ -19,7 +19,11 @@ const UserList = () => { const { t } = useTranslation(); const [userList, setUserList] = useState([]); const [isLoader, setIsLoader] = useState(false); - const [isModal, setIsModal] = useState(false); + const [isModal, setIsModal] = useState({ + form: false, + addseats: false, + options: false + }); const location = useLocation(); const isDashboard = location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; @@ -27,9 +31,16 @@ const UserList = () => { const [isAlert, setIsAlert] = useState({ type: "success", msg: "" }); const [isActiveModal, setIsActiveModal] = useState({}); const [isActLoader, setIsActLoader] = useState({}); - const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false }); + const [isSubscribe, setIsSubscribe] = useState({ + plan: "", + isValid: false, + priceperUser: 0 + }); const [isAdmin, setIsAdmin] = useState(false); const [formHeader, setFormHeader] = useState(t("add-user")); + const [usersCount, setUserCounts] = useState({ allowed: 0, totalAllowed: 0 }); + const [amount, setAmount] = useState({ quantity: 1, price: 0 }); + const [isBuyLoader, setIsBuyLoader] = useState(false); const recordperPage = 10; const startIndex = (currentPage - 1) * recordperPage; // user per page @@ -81,7 +92,6 @@ const UserList = () => { pages.push(lastPageIndex); } } - return pages; }; const pageNumbers = getPaginationRange(); @@ -96,13 +106,44 @@ const UserList = () => { async function fetchUserList() { try { setIsLoader(true); + const extUser = + localStorage.getItem("Extand_Class") && + JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; if (isEnableSubscription) { - const subscribe = await checkIsSubscribed(); - setIsSubscribe(subscribe); + const subscribe = await fetchSubscriptionInfo(); + if (subscribe?.plan_code?.includes("team")) { + const isSupAdmin = + subscribe?.adminId && extUser?.objectId === subscribe?.adminId; + setIsSubscribe({ + plan: subscribe.plan_code, + isValid: true, + adminId: subscribe.adminId, + priceperUser: subscribe.price, + isSuperAdmin: isSupAdmin + }); + setAmount((prev) => ({ ...prev, price: subscribe.price })); + try { + const res = await Parse.Cloud.run("allowedusers"); + console.log("res ", res); + setUserCounts((obj) => ({ + ...obj, + allowed: res, + totalAllowed: subscribe?.totalAllowedUser || 0 + })); + } catch (err) { + console.log("err while get users", err); + } + } else { + setIsSubscribe({ + plan: subscribe.plan_code, + isValid: false, + adminId: subscribe.adminId + }); + } } else { setIsSubscribe({ plan: "teams-yearly", isValid: true }); } - const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; + if (extUser) { const admin = extUser?.UserRole && @@ -121,14 +162,12 @@ const UserList = () => { console.log("Err in fetch userlist", err); setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") }); } finally { - setTimeout(() => { - setIsAlert({ type: "success", msg: "" }); - }, 1500); + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); setIsLoader(false); } } - const handleFormModal = () => { - setIsModal(!isModal); + const handleModal = (modalName) => { + setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] })); }; // Change page @@ -196,6 +235,39 @@ const UserList = () => { const handleToggleBtn = (user) => { setIsActiveModal({ [user.objectId]: true }); }; + + const handleAddOnSubmit = async (e) => { + e.preventDefault(); + e.stopPropagation(); + setIsBuyLoader(true); + try { + const resAddon = await Parse.Cloud.run("buyaddonusers", { + users: amount.quantity + }); + if (resAddon) { + const _resAddon = JSON.parse(JSON.stringify(resAddon)); + if (_resAddon.status === "success") { + setUserCounts((obj) => ({ + ...obj, + allowed: obj.allowed + amount.quantity, + totalAllowed: _resAddon.addon + })); + } + } + } catch (err) { + console.log("Err in buy addon", err); + setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") }); + } finally { + setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000); + setIsBuyLoader(false); + handleModal("addseats"); + } + }; + const handlePricePerUser = (e) => { + const quantity = e.target.value; + const price = e.target?.value > 0 ? isSubscribe.priceperUser * quantity : 0; + setAmount((prev) => ({ ...prev, quantity: quantity, price: price })); + }; return (
@@ -205,7 +277,7 @@ const UserList = () => { </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"> + <div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box"> <Loader /> </div> )} @@ -216,19 +288,46 @@ const UserList = () => { {isAlert.msg && <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"> - {t("report-name.Users")} + {t("report-name.Users")}{" "} <span className="text-xs md:text-[13px] font-normal"> <Tooltip message={t("users-from-teams")} /> </span> </div> - <div - className="cursor-pointer" - onClick={() => handleFormModal()} - > - <i className="fa-light fa-square-plus text-accent text-[40px]"></i> + <div className="flex flex-row gap-2 items-center"> + {isEnableSubscription && isSubscribe?.isSuperAdmin && ( + <div + className="hidden md:flex op-btn op-btn-sm h-[35px] op-btn-primary op-btn-outline text-xs mb-0.5" + onClick={() => handleModal("addseats")} + > + {t("buy-users")} + </div> + )} + <div + className="cursor-pointer" + onClick={() => handleModal("form")} + > + <i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i> + </div> + {isEnableSubscription && isSubscribe?.isSuperAdmin && ( + <div + className="cursor-pointer relative md:hidden mb-0.5" + onClick={() => handleModal("options")} + > + <i className="fa-light fa-ellipsis-vertical fa-xl"></i> + {isModal?.options && ( + <ul className="absolute -right-3 top-auto z-[70] w-max op-menu op-menu-xs shadow bg-base-100 text-base-content rounded-box border"> + <li onClick={() => handleModal("addseats")}> + <span className="text-[13px] capitalize font-medium"> + {t("buy-users")} + </span> + </li> + </ul> + )} + </div> + )} </div> </div> - <div className={` overflow-x-auto w-full`}> + <div className="overflow-x-auto w-full"> <table className="op-table border-collapse w-full"> <thead className="text-[14px]"> <tr className="border-y-[1px]"> @@ -313,34 +412,42 @@ const UserList = () => { </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" - > - {t("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" - > - {t("next")} - </button> + <div className="flex flex-row justify-between items-center text-xs font-medium"> + <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" + > + {t("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" + > + {t("next")} + </button> + )} + </div> + {isEnableSubscription && isSubscribe?.isSuperAdmin && ( + <div> + {t("available-seats")}: {usersCount.allowed}/ + {usersCount.totalAllowed} + </div> )} </div> {userList?.length <= 0 && ( @@ -362,17 +469,59 @@ const UserList = () => { </div> )} <ModalUi - isOpen={isModal} + isOpen={isModal.form} title={formHeader} - handleClose={handleFormModal} + handleClose={() => handleModal("form")} > <AddUser setIsAlert={setIsAlert} handleUserData={handleUserData} - closePopup={handleFormModal} + closePopup={() => handleModal("form")} setFormHeader={setFormHeader} /> </ModalUi> + <ModalUi + isOpen={isModal.addseats} + title="Add Seats" + handleClose={() => handleModal("addseats")} + > + {isBuyLoader && ( + <div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50"> + <Loader /> + </div> + )} + <form onSubmit={handleAddOnSubmit} className=" px-3 pb-6 pt-6"> + <div className="mb-3 flex justify-between"> + <label + htmlFor="quantity" + className="block text-xs text-gray-700 font-semibold" + > + {t("Quantity-of-users")} + <span className="text-[red] text-[13px]"> *</span> + </label> + <input + type="number" + name="quantity" + value={amount.quantity} + onChange={(e) => handlePricePerUser(e)} + className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" + required + /> + </div> + <div className="mb-3 flex justify-between"> + <label className="block text-xs text-gray-700 font-semibold"> + {t("Price")} (1 * {isSubscribe.priceperUser}) + </label> + <div className="w-1/4 flex justify-center items-center text-sm"> + USD {amount.price} + </div> + </div> + <hr className="text-base-content mb-3" /> + <button className="op-btn op-btn-primary w-full"> + {t("Proceed")} + </button> + </form> + </ModalUi> </div> ) : ( <div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box"> diff --git a/apps/OpenSign/src/script/locales/en/translation.json b/apps/OpenSign/src/script/locales/en/translation.json index 0ec4689d2..c328f92da 100644 --- a/apps/OpenSign/src/script/locales/en/translation.json +++ b/apps/OpenSign/src/script/locales/en/translation.json @@ -633,6 +633,8 @@ "quota-mail-tip":"Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.", "quota-mail-head":"Quota Reached", "unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.", - "sent-this-month":"Sent this month" + "sent-this-month":"Sent this month", + "available-seats":"Available seats", + "buy-users":"Buy more users" } diff --git a/apps/OpenSign/src/script/locales/fr/translation.json b/apps/OpenSign/src/script/locales/fr/translation.json index cdb61fce5..5bca0a5c5 100644 --- a/apps/OpenSign/src/script/locales/fr/translation.json +++ b/apps/OpenSign/src/script/locales/fr/translation.json @@ -632,7 +632,8 @@ "quota-mail-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.", "quota-mail-head":"Quota atteint", "unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.", - "sent-this-month":"envoyé ce mois-ci" - + "sent-this-month":"envoyé ce mois-ci", + "available-seats":"Disponible sièges", + "buy-users":"Acheter plus d'utilisateurs" }