feat: change manager role with editot, add admin panel for self-hosting

This commit is contained in:
prafull-opensignlabs
2024-07-12 19:07:27 +05:30
parent 1e04bb4dbe
commit 2604156c7f
30 changed files with 1404 additions and 204 deletions
File diff suppressed because one or more lines are too long
+17 -1
View File
@@ -32,6 +32,8 @@ const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
const ManageSign = lazy(() => import("./pages/Managesign"));
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook"));
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => {
@@ -72,7 +74,21 @@ function App() {
<Routes>
<Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} />
<Route path="/signup" element={<LazyPage Page={Signup} />} />
{isEnableSubscription && (
<Route path="/signup" element={<LazyPage Page={Signup} />} />
)}
{!isEnableSubscription && (
<>
<Route
path="/addadmin"
element={<LazyPage Page={AddAdmin} />}
/>
<Route
path="/addexistadmin"
element={<LazyPage Page={UpdateExistUserAdmin} />}
/>
</>
)}
</Route>
<Route element={<Validate />}>
<Route
+35 -27
View File
@@ -4,6 +4,7 @@ import axios from "axios";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { copytoData } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
function generatePassword(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@@ -27,7 +28,7 @@ const AddUser = (props) => {
});
const [isLoader, setIsLoader] = useState(false);
const [teamList, setTeamList] = useState([]);
const role = ["OrgAdmin", "Manager", "User"];
const role = ["OrgAdmin", "Editor", "User"];
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
@@ -48,7 +49,13 @@ const AddUser = (props) => {
const teamRes = await team.find();
if (teamRes.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamRes));
// if (isEnableSubscription) {
setTeamList(_teamRes);
if (!isEnableSubscription) {
const allUserId =
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
setFormdata((prev) => ({ ...prev, team: allUserId }));
}
}
};
const checkUserExist = async () => {
@@ -226,7 +233,6 @@ const AddUser = (props) => {
y.objectId === team.objectId ? team : y
);
}
props.handleUserData(parseData);
}
setIsLoader(false);
@@ -352,31 +358,33 @@ const AddUser = (props) => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Team<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.team}
onChange={(e) => handleChange(e)}
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>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
{isEnableSubscription && (
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Team<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.team}
onChange={(e) => handleChange(e)}
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>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
)}
<div className="mb-3">
<label
htmlFor="phone"
+2 -2
View File
@@ -31,8 +31,8 @@ const Header = ({ showSidebar }) => {
const getIsSubscribe = await checkIsSubscribed();
if (getIsSubscribe) {
const applogo = await getAppLogo();
if (applogo) {
setAppLogo(applogo);
if (applogo?.logo) {
setAppLogo(applogo?.logo);
} else {
setAppLogo(localStorage.getItem("appLogo") || "");
}
+36 -22
View File
@@ -5,7 +5,7 @@ import SocialMedia from "./SocialMedia";
import dp from "../../assets/images/dp.png";
import sidebarList from "../../json/menuJson";
import { useNavigate } from "react-router-dom";
import { isStaging } from "../../constant/const";
import { isEnableSubscription } from "../../constant/const";
const Sidebar = ({ isOpen, closeSidebar }) => {
const navigate = useNavigate();
@@ -29,7 +29,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
let userRole = "contracts_Users";
let userRole = "contracts_User";
if (extClass && extClass.length > 0) {
userRole = extClass[0].UserRole;
}
@@ -38,28 +38,42 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
userRole === "contracts_OrgAdmin"
) {
const newSidebarList = sidebarList.map((item) => {
if (item.title === "Settings" && isStaging) {
if (item.title === "Settings") {
// Make a shallow copy of the item
const newItem = { ...item };
newItem.children = [
...newItem.children,
{
icon: "fa-light fa-building-memo",
title: "Teams",
target: "_self",
pageType: "",
description: "",
objectId: "teams"
},
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
];
if (isEnableSubscription) {
newItem.children = [
...newItem.children,
{
icon: "fa-light fa-building-memo",
title: "Teams",
target: "_self",
pageType: "",
description: "",
objectId: "teams"
},
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
];
} else {
newItem.children = [
...newItem.children,
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
];
}
return newItem;
}
return item;
+14 -11
View File
@@ -1856,18 +1856,21 @@ export const getFileName = (fileUrl) => {
//fetch tenant app logo from `partners_Tenant` class by domain name
export const getAppLogo = async () => {
const domainName = window.location.host;
try {
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
tenantCreditsQuery.equalTo("Domain", domainName);
const res = await tenantCreditsQuery.first();
if (res) {
const updateRes = JSON.parse(JSON.stringify(res));
return updateRes?.Logo;
if (window.location.host === "app.opensignlabs.com") {
return { logo: appInfo.applogo, user: "exist" };
} else {
const domain = window.location.host;
try {
const tenant = await Parse.Cloud.run("getlogobydomain", {
domain: domain
});
if (tenant) {
return { logo: tenant?.logo, user: tenant?.user };
}
} catch (err) {
console.log("err in getlogo ", err);
return { logo: appInfo.applogo, user: "exist" };
}
} catch (e) {
return null;
}
};
+1 -1
View File
@@ -32,7 +32,7 @@ export const appInfo = {
extended_class: "contracts_Users"
},
{
role: "contracts_Manager",
role: "contracts_Editor",
menuId: "H9vRfEYKhT",
pageType: "dashboard",
pageId: "35KBoSgoAK",
+5 -1
View File
@@ -3,6 +3,10 @@ export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
export const themeColor = "#47a3ad";
export const iconColor = "#686968";
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
export const isEnableSubscription =
process.env.REACT_APP_ENABLE_SUBSCRIPTION &&
process.env.REACT_APP_ENABLE_SUBSCRIPTION?.toLowerCase() === "true"
? true
: false;
export const isStaging =
window.location.origin === "https://staging-app.opensignlabs.com";
+97 -65
View File
@@ -1,6 +1,15 @@
export default function reportJson(id) {
// console.log("json ", json);
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const declineHead = [
"Sr.No",
"Title",
"Reason",
"Folder",
"File",
"Owner",
"Signers"
];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
@@ -140,7 +149,7 @@ export default function reportJson(id) {
case "UPr2Fm5WY3":
return {
reportName: "Declined Documents",
heading: head,
heading: declineHead,
actions: [
{
btnId: "1458",
@@ -306,76 +315,99 @@ export default function reportJson(id) {
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
};
// template report
case "6TeaPr321t":
case "6TeaPr321t": {
const templateAct = [
{
btnId: "2234",
btnLabel: "Use",
hoverLabel: "Use",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-plus",
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
},
{
btnId: "1631",
btnLabel: "Quick send",
hoverLabel: "Quick send",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "2434",
btnLabel: "Edit",
hoverLabel: "Edit",
btnIcon: "fa-light fa-pen",
redirectUrl: "template",
action: "redirect"
},
{
btnId: "1834",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa-light fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
];
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
let templateActions = templateAct;
if (extClass && extClass.length > 0) {
if (extClass?.[0]?.UserRole !== "contracts_User") {
templateActions = templateAct.map((item) => {
if (item.action === "option") {
// Make a shallow copy of the item
const newItem = { ...item };
newItem.subaction = [
{
btnId: "1873",
btnLabel: "Share with team",
hoverLabel: "Share with team",
btnIcon: "fa-light fa-share-nodes",
redirectUrl: "",
action: "sharewith"
},
...newItem.subaction
];
return newItem;
}
return item;
});
}
}
return {
reportName: "Templates",
heading: templateReport,
actions: [
{
btnId: "2234",
btnLabel: "Use",
hoverLabel: "Use",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-plus",
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
},
{
btnId: "1631",
btnLabel: "Quick send",
hoverLabel: "Quick send",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "1873",
btnLabel: "Share-with",
hoverLabel: "Share-with",
btnIcon: "fa-light fa-share-nodes",
redirectUrl: "",
action: "sharewith"
},
{
btnId: "2434",
btnLabel: "Edit",
hoverLabel: "Edit",
btnIcon: "fa-light fa-pen",
redirectUrl: "template",
action: "redirect"
},
{
btnId: "1834",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa-light fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
],
actions: templateActions,
helpMsg:
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
};
}
default:
return null;
}
+460
View File
@@ -0,0 +1,460 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { NavLink, useNavigate } from "react-router-dom";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import Loader from "../primitives/Loader";
import axios from "axios";
const AddAdmin = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [showPassword, setShowPassword] = useState(false);
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [password, setPassword] = useState("");
const [email, setEmail] = useState("");
const [company, setCompany] = useState("");
const [jobTitle, setJobTitle] = useState("");
const [lengthValid, setLengthValid] = useState(false);
const [caseDigitValid, setCaseDigitValid] = useState(false);
const [specialCharValid, setSpecialCharValid] = useState(false);
const [isAuthorize, setIsAuthorize] = useState(false);
const [isSubscribeNews, setIsSubscribeNews] = useState(false);
const [errMsg, setErrMsg] = useState("");
const [state, setState] = useState({
loading: false,
alertType: "success",
alertMsg: ""
});
const togglePasswordVisibility = () => setShowPassword(!showPassword);
useEffect(() => {
checkUserExist();
// eslint-disable-next-line
}, []);
const checkUserExist = async () => {
setState((prev) => ({ ...prev, loading: true }));
try {
const app = await getAppLogo();
if (app?.user === "exist") {
setErrMsg("Admin already exist.");
}
} catch (err) {
setErrMsg("Something went wrong.");
console.log("err in check user exist", err);
} finally {
setState((prev) => ({ ...prev, loading: false }));
}
};
const clearStorage = async () => {
if (Parse.User.current()) {
await Parse.User.logOut();
}
const baseUrl = localStorage.getItem("baseUrl");
const appid = localStorage.getItem("parseAppId");
const applogo = localStorage.getItem("appLogo");
const defaultmenuid = localStorage.getItem("defaultmenuid");
const PageLanding = localStorage.getItem("PageLanding");
const userSettings = localStorage.getItem("userSettings");
localStorage.clear();
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("userSettings", userSettings);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
};
const handleSubmit = async (event) => {
event.preventDefault();
if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage();
setState({ loading: true });
const userDetails = {
name: name,
email: email,
phone: phone,
company: company,
jobTitle: jobTitle
};
localStorage.setItem("userDetails", JSON.stringify(userDetails));
try {
event.preventDefault();
const user = new Parse.User();
user.set("name", name);
user.set("email", email);
user.set("password", password);
user.set("phone", phone);
user.set("username", email);
const userRes = await user.save();
if (userRes) {
const params = {
userDetails: {
jobTitle: jobTitle,
company: company,
name: name,
email: email,
phone: phone,
role: "contracts_Admin"
}
};
try {
const usersignup = await Parse.Cloud.run("addadmin", params);
if (usersignup) {
if (isSubscribeNews) {
subscribeNewsletter();
}
handleNavigation(userRes.getSessionToken());
}
} catch (err) {
alert(err.message);
setState({ loading: false });
}
}
} catch (error) {
console.log("err ", error);
if (error.code === 202) {
const params = { email: email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert("User already exists with this username!");
setState({ loading: false });
} else {
// console.log("state.email ", email);
try {
await Parse.User.requestPasswordReset(email).then(
async function (res) {
if (res.data === undefined) {
alert("Verification mail has been sent to your E-mail!");
}
}
);
} catch (err) {
console.log(err);
}
setState({ loading: false });
}
} else {
alert(error.message);
setState({ loading: false });
}
}
}
};
const handleNavigation = async (sessionToken) => {
const res = await Parse.User.become(sessionToken);
if (res) {
const _user = JSON.parse(JSON.stringify(res));
console.log("_user ", _user);
localStorage.setItem("accesstoken", sessionToken);
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
const userSettings = appInfo.settings;
const currentUser = Parse.User.current();
const extUser = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (extUser) {
const IsDisabled = extUser?.get("IsDisabled") || false;
if (!IsDisabled) {
const userRole = extUser?.get("UserRole");
const menu =
userRole && userSettings.find((menu) => menu.role === userRole);
if (menu) {
const _currentRole = userRole;
const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
const extInfo_stringify = JSON.stringify([extUser]);
localStorage.setItem("Extand_Class", extInfo_stringify);
const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", extInfo?.Email);
localStorage.setItem("username", extInfo?.Name);
if (extInfo?.TenantId) {
const tenant = {
Id: extInfo?.TenantId?.objectId || "",
Name: extInfo?.TenantId?.TenantName || ""
};
localStorage.setItem("TenantId", tenant?.Id);
dispatch(showTenant(tenant?.Name));
localStorage.setItem("TenantName", tenant?.Name);
}
localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType);
setState({
loading: false,
alertType: "success",
alertMsg: "Registered user successfully."
});
navigate(`/${menu.pageType}/${menu.pageId}`);
} else {
setState({
loading: false,
alertType: "danger",
alertMsg: "Role not found."
});
}
} else {
setState({
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
});
}
}
} catch (error) {
console.log("error in fetch extuser", error);
const msg = error.message || "Something went wrong.";
setState({ loading: false, alertType: "danger", alertMsg: msg });
} finally {
setTimeout(() => setState({ loading: false, alertMsg: "" }), 2000);
}
}
};
const handlePasswordChange = (e) => {
const newPassword = e.target.value;
setPassword(newPassword);
// Check conditions separately
setLengthValid(newPassword.length >= 8);
setCaseDigitValid(
/[a-z]/.test(newPassword) &&
/[A-Z]/.test(newPassword) &&
/\d/.test(newPassword)
);
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
};
const subscribeNewsletter = async () => {
try {
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": "legadranaxn"
};
const newsletter = await axios.post(
"https://app.opensignlabs.com",
{ Name: name, Email: email, Domain: window.location.host },
{ header: headers }
);
console.log("newsletter ", newsletter);
} catch (err) {
console.log("err in subscribeNewsletter", err);
}
};
return (
<div className="h-screen flex justify-center">
{state.loading ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
</div>
) : (
<>
{errMsg ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
{errMsg}
</div>
) : (
<div className="w-[95%] md:w-[500px]">
<form onSubmit={handleSubmit}>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<h2 className="text-[30px] text-center mt-3 font-medium">
Opensign Setup
</h2>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
target="_blank"
rel="noopener noreferrer"
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
>
Join our discord server
<i
aria-hidden="true"
className="fa-brands fa-discord ml-1"
></i>
{/* <span className="fa-sr-only">OpenSign&apos;s Discord</span> */}
</NavLink>
<div className="px-6 py-3 text-xs">
<label className="block ">
Name <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Email <span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
type="email"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
required
/>
<hr className="my-2 border-none" />
<label>
Phone <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Company <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={company}
onChange={(e) => setCompany(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Job Title{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={jobTitle}
onChange={(e) => setJobTitle(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Password <span className="text-[red] text-[13px]">*</span>
</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="password"
value={password}
onChange={(e) => handlePasswordChange(e)}
required
/>
<span
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
onClick={togglePasswordVisibility}
>
{showPassword ? (
<i className="fa fa-eye-slash" /> // Close eye icon
) : (
<i className="fa fa-eye" /> // Open eye icon
)}
</span>
</div>
{password.length > 0 && (
<div className="mt-1 text-[11px]">
<p
className={`${
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8
characters long
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain
uppercase letter, lowercase letter, digit
</p>
<p
className={`${
specialCharValid ? "text-green-600" : "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should contain
special character
</p>
</div>
)}
<div className="mt-2.5 ml-1 flex flex-row items-center">
<input
type="checkbox"
className="op-checkbox op-checkbox-sm"
id="termsandcondition"
checked={isAuthorize}
onChange={(e) => setIsAuthorize(e.target.checked)}
required
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
I agree to the
</label>
<span
className="underline cursor-pointer ml-1"
onClick={() =>
openInNewTab(
"https://www.opensignlabs.com/terms-and-conditions"
)
}
>
Terms of Service
</span>
<span>.</span>
</div>
<div className="mt-2.5 ml-1 flex flex-row items-center">
<input
type="checkbox"
className="op-checkbox op-checkbox-sm"
id="termsandcondition"
checked={isSubscribeNews}
onChange={(e) => setIsSubscribeNews(e.target.checked)}
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
Subscribe to OpenSign newsletter
</label>
</div>
</div>
<div className="mx-4 text-center text-xs font-bold mb-3">
<button
type="submit"
className="op-btn op-btn-primary w-full"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Next"}
</button>
</div>
</div>
</form>
</div>
)}
</>
)}
</div>
);
};
export default AddAdmin;
+3 -3
View File
@@ -61,9 +61,9 @@ function ForgotPassword() {
}, []);
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
const app = await getAppLogo();
if (app?.logo) {
setImage(app?.logo);
} else {
setImage(appInfo?.applogo || undefined);
}
+3 -3
View File
@@ -29,9 +29,9 @@ function GuestLogin() {
//function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = async () => {
if (isEnableSubscription) {
const applogo = await getAppLogo();
if (applogo) {
setAppLogo(applogo);
const app = await getAppLogo();
if (app?.logo) {
setAppLogo(app?.logo);
} else {
setAppLogo(logo);
}
+22 -22
View File
@@ -50,18 +50,16 @@ function Login() {
}
dispatch(fetchAppInfo());
saveLogo();
// eslint-disable-next-line
}, []);
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
} else {
setImage(appInfo?.applogo || undefined);
}
const app = await getAppLogo();
if (!isEnableSubscription && app?.user === "not_exist") {
navigate("/addadmin");
}
if (app?.logo) {
setImage(app?.logo);
} else {
setImage(appInfo?.applogo || undefined);
}
@@ -686,20 +684,22 @@ function Login() {
>
{state.loading ? "Loading..." : "Login"}
</button>
<button
type="button"
className="op-btn op-btn-accent"
disabled={state.loading}
onClick={() =>
navigate(
location.search
? "/signup" + location.search
: "/signup"
)
}
>
Create Account
</button>
{isEnableSubscription && (
<button
type="button"
className="op-btn op-btn-accent"
disabled={state.loading}
onClick={() =>
navigate(
location.search
? "/signup" + location.search
: "/signup"
)
}
>
Create Account
</button>
)}
</div>
</form>
{(appInfo.googleClietId || isEnableSubscription) && (
+2 -2
View File
@@ -1676,7 +1676,7 @@ function PdfRequestFiles(props) {
{/* this modal is used to show decline alert */}
<PdfDeclineModal
show={isDecline.isDeclined}
headMsg="Document Declined"
headMsg="Document declined"
bodyMssg={
isDecline.currnt === "Sure"
? "Are you sure want to decline this document ?"
@@ -1692,7 +1692,7 @@ function PdfRequestFiles(props) {
{/* this modal is used for show expired alert */}
<PdfDeclineModal
show={isExpired}
headMsg="Document Expired!"
headMsg="Document expired"
bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`}
/>
{!isEmailVerified && (
+3 -3
View File
@@ -291,9 +291,9 @@ const Signup = () => {
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
const app = await getAppLogo();
if (app?.logo) {
setImage(app?.logo);
} else {
setImage(appInfo?.applogo || undefined);
}
@@ -0,0 +1,177 @@
import React, { useEffect, useState } from "react";
import Loader from "../primitives/Loader";
import Parse from "parse";
import { NavLink, useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
const UpdateExistUserAdmin = () => {
const navigate = useNavigate();
const [formdata, setFormdata] = useState({ email: "", masterkey: "" });
const [loader, setLoader] = useState(true);
const [errMsg, setErrMsg] = useState("");
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "danger", msg: "" });
useEffect(() => {
checkIsAdminExist();
}, []);
const checkIsAdminExist = async () => {
try {
const isAdminExist = await Parse.Cloud.run("checkadminexist");
if (isAdminExist !== "not_exist") {
console.log("isAdminExist ", isAdminExist);
setErrMsg("Admin already exists.");
}
} catch (err) {
console.log("Err in checkadminexist", err);
setErrMsg("Something went wrong.");
} finally {
setLoader(false);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmitLoading(true);
try {
const updateUserAsAdmin = await Parse.Cloud.run(
"updateuserasadmin",
formdata
);
console.log("updateUserAsAdmin ", updateUserAsAdmin);
if (updateUserAsAdmin === "admin_created") {
setIsAlert({ type: "success", msg: "Admin created" });
navigate("/");
}
} catch (err) {
console.log("err in updateuserasadmin", err.code);
if (err.code === 404) {
setIsAlert((prev) => ({ ...prev, msg: "Invalid masterkey" }));
} else if (err.code === 101) {
setIsAlert((prev) => ({ ...prev, msg: "User not found" }));
} else if (err.code === 137) {
setIsAlert((prev) => ({ ...prev, msg: "Admin already exist" }));
} else {
setErrMsg("Something went wrong.");
}
} finally {
setIsSubmitLoading(false);
setTimeout(() => {
setIsAlert((prev) => ({ type: "danger", msg: "" }));
}, 2000);
}
};
return (
<div className="h-screen flex justify-center">
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{loader ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
</div>
) : (
<>
{errMsg ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
{errMsg}
</div>
) : (
<div className="w-[95%] md:w-[500px]">
<form onSubmit={handleSubmit}>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50 overflow-hidden">
{isSubmitLoading && (
<div className="absolute z-40 w-full h-full flex justify-center bg-black/30">
<Loader />
</div>
)}
<h2 className="text-[30px] text-center mt-3 font-medium">
Opensign Setup
</h2>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
target="_blank"
rel="noopener noreferrer"
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
>
Join our discord server
<i
aria-hidden="true"
className="fa-brands fa-discord ml-1"
></i>
{/* <span className="fa-sr-only">OpenSign&apos;s Discord</span> */}
</NavLink>
<div className="px-6 py-3 text-xs">
<label>
Email <span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
type="email"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formdata.email}
onChange={(e) =>
setFormdata((prev) => ({
...prev,
email: e.target.value?.toLowerCase()
}))
}
required
/>
<hr className="my-2 border-none" />
<label>
Master key{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formdata.masterkey}
onChange={(e) =>
setFormdata((prev) => ({
...prev,
masterkey: e.target.value
}))
}
required
/>
{/*
<div className="mt-2.5 ml-1 flex flex-row items-center">
<input
type="checkbox"
className="op-checkbox op-checkbox-sm"
id="termsandcondition"
checked={isSubscribeNews}
onChange={(e) => setIsSubscribeNews(e.target.checked)}
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
Subscribe to OpenSign newsletter
</label>
</div> */}
</div>
<div className="mx-4 text-center text-xs font-bold mb-3">
<button
type="submit"
className="op-btn op-btn-primary w-full"
disabled={loader}
>
{loader ? "Loading..." : "Next"}
</button>
</div>
</div>
</form>
</div>
)}
</>
)}
</div>
);
};
export default UpdateExistUserAdmin;
+3 -1
View File
@@ -88,6 +88,8 @@ const UserList = () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
} else {
setIsSubscribe(true);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const res = await Parse.Cloud.run("getuserlistbyorg", {
@@ -179,7 +181,7 @@ const UserList = () => {
<Loader />
</div>
)}
{isSubscribe && isEnableSubscription && !isLoader && (
{isSubscribe && !isLoader && (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isAlert.msg && (
<Alert type={isAlert.type}>
@@ -160,6 +160,16 @@ const ReportTable = (props) => {
value: x.objectId
}));
setTeamList(formatedList);
if (!isEnableSubscription) {
const selected = _teamRes.map(
(x) =>
x.Name === "All Users" && {
label: x.Name,
value: x.objectId
}
);
setSelectedTeam(selected);
}
}
}
} catch (err) {
@@ -351,6 +361,8 @@ const ReportTable = (props) => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
} else {
setIsSubscribe(true);
}
if (item?.SharedWith && item?.SharedWith.length > 0) {
// below code is used to get existing sharewith teams and formated them as per react-select
@@ -1158,6 +1170,13 @@ const ReportTable = (props) => {
<td className="px-4 py-2 font-semibold min-w-56 max-w-56">
{item?.Name}{" "}
</td>
{props?.heading?.includes("Reason") && (
<td className="px-4 py-2">
{item?.DeclineReason?.length > 25
? item?.DeclineReason?.slice(0, 25) + "..."
: item?.DeclineReason || "-"}
</td>
)}
{props.heading.includes("Note") && (
<td className="px-4 py-2">
{item?.Note?.length > 25
@@ -1527,6 +1546,30 @@ const ReportTable = (props) => {
/>
</>
)}
{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>
<div className="px-2 mt-3 w-full h-full">
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
{selectedTeam?.[0]?.label}
</div>
</div>
<button
onClick={(e) => handleShareWith(e, item)}
className="op-btn op-btn-primary ml-[10px] my-3"
>
Submit
</button>
</>
)}
</div>
</div>
)}
@@ -14,7 +14,7 @@ function CustomModal({
show && (
<dialog className="op-modal op-modal-open absolute z-[448]">
<div className="w-[95%] md:w-[60%] lg:w-[40%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="text-[#de4337] font-bold text-lg pt-[15px] px-[20px]">
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{headMsg && headMsg}
</h3>
<div className="p-[10px] px-[20px]">
+10 -2
View File
@@ -8,7 +8,7 @@ import { addUserToGroups } from './parsefunction/AddUserToRole.js';
import { getUserGroups } from './parsefunction/UserGroups.js';
import DocumentAftersave from './parsefunction/DocumentAftersave.js';
import ContactbookAftersave from './parsefunction/ContactBookAftersave.js';
import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js';
// import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js';
import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js';
import AuthLoginAsMail from './parsefunction/AuthLoginAsMail.js';
import getUserId from './parsefunction/getUserId.js';
@@ -49,11 +49,15 @@ import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import TeamsAftersave from './parsefunction/TeamsAftersave.js';
import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.js';
import PublicUserLinkContactToDoc from './parsefunction/PublicUserLinkContactToDoc.js';
import GetLogoByDomain from './parsefunction/GetLogobyDomain.js';
import AddAdmin from './parsefunction/AddAdmin.js';
import CheckAdminExist from './parsefunction/CheckAdminExist.js';
import UpdateExistUserAsAdmin from './parsefunction/UpdateExistUserAsAdmin.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
// Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
Parse.Cloud.afterSave('contracts_Teams', TeamsAftersave);
Parse.Cloud.afterSave('contracts_Subscriptions', SubscriptionAftersave);
@@ -107,3 +111,7 @@ Parse.Cloud.define('isextenduser', isextenduser);
Parse.Cloud.define('getuserbyorg', getUserByOrg);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('publicuserlinkcontacttodoc', PublicUserLinkContactToDoc);
Parse.Cloud.define('getlogobydomain', GetLogoByDomain);
Parse.Cloud.define('addadmin', AddAdmin);
Parse.Cloud.define('checkadminexist', CheckAdminExist);
Parse.Cloud.define('updateuserasadmin', UpdateExistUserAsAdmin);
@@ -0,0 +1,221 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
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 teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
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');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
} catch (err) {
console.log('err in add team, role, org', err);
}
}
async function saveUser(userDetails) {
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', userDetails.email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const url = `${serverUrl}/loginAs`;
const axiosRes = await axios({
method: 'POST',
url: url,
headers: {
'Content-Type': 'application/json;charset=utf-8',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
params: {
userId: userRes.id,
},
});
const login = await axiosRes.data;
// console.log("login ", login);
return { id: login.objectId, sessionToken: login.sessionToken };
} else {
const user = new Parse.User();
user.set('username', userDetails.email);
user.set('password', userDetails.password);
user.set('email', userDetails.email);
if (userDetails?.phone) {
user.set('phone', userDetails.phone);
}
user.set('name', userDetails.name);
const res = await user.signUp();
// console.log("res ", res);
return { id: res.id, sessionToken: res.getSessionToken() };
}
}
export default async function AddAdmin(request) {
const userDetails = request.params.userDetails;
// const subscription = request.params.subscription;
const user = await saveUser(userDetails);
try {
const extQuery = new Parse.Query('contracts_Users');
extQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
const extUser = await extQuery.first({ useMasterKey: true });
if (extUser) {
return { message: 'User already exist' };
} else {
// console.log("role ", role);
const partnerQuery = new Parse.Object('partners_Tenant');
partnerQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
if (userDetails?.phone) {
partnerQuery.set('ContactNumber', userDetails.phone);
}
partnerQuery.set('TenantName', userDetails.company);
partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true);
partnerQuery.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
if (userDetails && userDetails.pincode) {
partnerQuery.set('PinCode', userDetails.pincode);
}
if (userDetails && userDetails.country) {
partnerQuery.set('Country', userDetails.country);
}
if (userDetails && userDetails.state) {
partnerQuery.set('State', userDetails.state);
}
if (userDetails && userDetails.city) {
partnerQuery.set('City', userDetails.city);
}
if (userDetails && userDetails.address) {
partnerQuery.set('Address', userDetails.address);
}
const tenantRes = await partnerQuery.save(null, { useMasterKey: true });
// console.log("tenantRes ", tenantRes);
const newObj = new Parse.Object('contracts_Users');
newObj.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
newObj.set('UserRole', userDetails.role);
newObj.set('Email', userDetails.email);
newObj.set('Name', userDetails.name);
if (userDetails?.phone) {
newObj.set('Phone', userDetails?.phone);
}
newObj.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantRes.id,
});
if (userDetails && userDetails.company) {
newObj.set('Company', userDetails.company);
}
if (userDetails && userDetails.jobTitle) {
newObj.set('JobTitle', userDetails.jobTitle);
}
const extRes = await newObj.save(null, { useMasterKey: true });
// if (subscription) {
const extUser = {
objectId: extRes.id,
Name: userDetails.name,
Email: userDetails.email,
Phone: userDetails?.phone ? userDetails.phone : '',
TenantId: { objectId: tenantRes.id },
UserId: { objectId: user.id },
UserRole: userDetails.role,
Company: userDetails.company,
JobTitle: userDetails.jobTitle,
};
await addTeamAndOrg(extUser);
// await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
// }
return { message: 'User sign up', sessionToken: user.sessionToken };
}
} catch (err) {
console.log('Err ', err);
}
}
// async function saveSubscription(extUserId, UserId, tenantId, subscription) {
// const SubscriptionId = subscription?.data?.subscription?.subscription_id || '';
// const Next_billing_date = subscription?.data?.subscription?.next_billing_at || '';
// const planCode = subscription?.data?.subscription?.plan?.plan_code || '';
// try {
// const createSubscription = new Parse.Object('contracts_Subscriptions');
// createSubscription.set('SubscriptionId', SubscriptionId);
// createSubscription.set('SubscriptionDetails', subscription);
// createSubscription.set('ExtUserPtr', {
// __type: 'Pointer',
// className: 'contracts_Users',
// objectId: extUserId,
// });
// createSubscription.set('CreatedBy', {
// __type: 'Pointer',
// className: '_User',
// objectId: UserId,
// });
// createSubscription.set('TenantId', {
// __type: 'Pointer',
// className: 'partners_Tenant',
// objectId: tenantId,
// });
// createSubscription.set('Next_billing_date', new Date(Next_billing_date));
// createSubscription.set('PlanCode', planCode);
// await createSubscription.save(null, { useMasterKey: true });
// } catch (err) {
// console.log('err in save subscription pgsignup', err);
// }
// }
@@ -0,0 +1,20 @@
// `CheckAdminExist` is used to check is admin with org exist or not in db
export default async function CheckAdminExist() {
try {
const extClsQuery = new Parse.Query('contracts_Users');
extClsQuery.equalTo('UserRole', 'contracts_Admin');
extClsQuery.exists('OrganizationId');
extClsQuery.notEqualTo('IsDisabled', true);
const extAdminRes = await extClsQuery.first({ useMasterKey: true });
if (extAdminRes) {
return 'exist';
} else {
return 'not_exist';
}
} catch (err) {
console.log('err in isAdminExist', err);
const code = err?.code || 400;
const msg = err?.message || 'something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -0,0 +1,25 @@
// `GetLogoByDomain` is used to get logo by domain as well as check any tenant exist or not in db
export default async function GetLogoByDomain(request) {
const domain = request.params.domain;
try {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('Domain', domain);
const res = await tenantCreditsQuery.first();
if (res) {
const updateRes = JSON.parse(JSON.stringify(res));
return { logo: updateRes?.Logo, user: 'exist' };
} else {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
const tenantRes = await tenantCreditsQuery.first();
if (tenantRes) {
return { logo: '', user: 'exist' };
} else {
return { logo: '', user: 'not_exist' };
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -0,0 +1,124 @@
async function updateUserExceptAdmin(data) {
const Contracts = Parse.Object.extend('contracts_Users');
let skip = 0;
const limit = 100;
let hasMore = true;
while (hasMore) {
const query = new Parse.Query(Contracts);
query.notEqualTo('UserRole', 'contracts_Admin');
query.limit(limit);
query.skip(skip);
try {
// Fetch a batch of entries
const results = await query.find({ useMasterKey: true });
// Check if there are more entries to process
hasMore = results.length === limit;
skip += results.length;
// Iterate through each entry and update the userRole field
for (let i = 0; i < results.length; i++) {
const contract = results[i];
contract.set('UserRole', 'contracts_User');
contract.set('OrganizationId', data.orgPtr);
contract.set('TeamIds', data.teamIds);
}
// Save all the updated entries
await Parse.Object.saveAll(results, { useMasterKey: true });
console.log(`${results.length} user roles updated successfully`);
} catch (error) {
console.error('Error while updating user roles:', error);
hasMore = false; // Stop if there's an error
}
}
}
// `UpdateExistUserAsAdmin` is used to create admin from exist user records and transfer to org of admin
export default async function UpdateExistUserAsAdmin(request) {
const email = request.params.email;
const masterkey = request.params.masterkey;
try {
if (masterkey !== process.env.MASTER_KEY) {
throw new Parse.Error(404, 'Invalid master key.');
}
const extClsQuery = new Parse.Query('contracts_Users');
extClsQuery.equalTo('UserRole', 'contracts_Admin');
extClsQuery.exists('OrganizationId');
extClsQuery.exists('TeamIds');
extClsQuery.notEqualTo('IsDisabled', true);
const extAdminRes = await extClsQuery.first({ useMasterKey: true });
if (extAdminRes) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Admin already exist.');
} else {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('Email', email);
extCls.notEqualTo('IsDisabled', true);
const extRes = await extCls.first({ useMasterKey: true });
console.log('extRes ', extRes);
if (extRes) {
const _extRes = JSON.parse(JSON.stringify(extRes));
const org = new Parse.Object('contracts_Organizations');
org.set('Name', _extRes.Company);
org.set('IsActive', true);
org.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _extRes.TenantId.objectId,
});
org.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extRes.id,
});
org.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: _extRes.UserId.objectId,
});
const orgRes = await org.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
teamCls.set('IsActive', true);
const teamRes = await teamCls.save(null, { useMasterKey: true });
const orgPtr = {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
};
const teamIds = [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
];
const extUser = new Parse.Object('contracts_Users');
extUser.id = extRes.id;
extUser.set('UserRole', 'contracts_Admin');
extUser.set('OrganizationId', orgPtr);
extUser.set('TeamIds', teamIds);
const extUserRes = await extUser.save(null, { useMasterKey: true });
if (extUserRes) {
const data = { orgPtr: orgPtr, teamIds: teamIds };
updateUserExceptAdmin(data);
return 'admin_created';
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
} catch (err) {
console.log('err in updateExistUserAsAdmin', err);
const code = err.code || 400;
const msg = err.message || 'something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,8 +1,13 @@
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
import fs from 'node:fs';
import fontkit from '@pdf-lib/fontkit';
export default async function GenerateCertificate(docDetails) {
const pdfDoc = await PDFDocument.create();
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
// `fontBytes` is used to embed custom font in pdf
const fontBytes = fs.readFileSync('./font/times.ttf'); //
pdfDoc.registerFontkit(fontkit);
const timesRomanFont = await pdfDoc.embedFont(fontBytes, { subset: true });
const pngUrl = fs.readFileSync('./logo.png').buffer;
const pngImage = await pdfDoc.embedPng(pngUrl);
const page = pdfDoc.addPage();
@@ -162,6 +162,7 @@ export default function reportJson(id, userId) {
'Signers.Email',
'Signers.Phone',
'Placeholders',
'DeclineReason',
],
};
// Expired Documents report
@@ -58,18 +58,6 @@ export default async function usersignup(request) {
if (extUser) {
return { message: 'User already exist' };
} else {
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
sessionToken: user.sessionToken, //localStorage.getItem("accesstoken"),
};
let body = {
appName: extClass,
roleName: userDetails.role,
userId: user.id,
};
let role = await axios.post(roleurl, body, { headers: headers });
// console.log("role ", role);
const partnerCls = Parse.Object.extend('partners_Tenant');
const partnerQuery = new partnerCls();
Binary file not shown.
+9
View File
@@ -11,6 +11,7 @@
"dependencies": {
"@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^2.2.0",
"@pdf-lib/fontkit": "^1.1.1",
"@signpdf/placeholder-pdf-lib": "^3.2.4",
"@signpdf/signer-p12": "^3.2.4",
"@signpdf/signpdf": "^3.2.4",
@@ -3006,6 +3007,14 @@
"node": ">=4.0.0"
}
},
"node_modules/@pdf-lib/fontkit": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz",
"integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
+1
View File
@@ -20,6 +20,7 @@
"dependencies": {
"@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^2.2.0",
"@pdf-lib/fontkit": "^1.1.1",
"@signpdf/placeholder-pdf-lib": "^3.2.4",
"@signpdf/signer-p12": "^3.2.4",
"@signpdf/signpdf": "^3.2.4",