Compare commits

...
Author SHA1 Message Date
prafull-opensignlabsandGitHub 72401291f8 Delete apps/OpenSign/vite.public-template.config.js 2025-05-14 20:24:54 +05:30
prafull-opensignlabsandGitHub 7b34314b9e Merge pull request #1733 from OpenSignLabs/updates-15023728155
fix: correct build commands for selfhosting
2025-05-14 20:13:14 +05:30
prafull-opensignlabs b6f1cceeca Merge pull request #612 from OpenSignLabs/staging
fix: correct build commands for selfhosting
2025-05-14 14:42:30 +00:00
prafull-opensignlabsandGitHub c422cdac06 Merge pull request #1732 from OpenSignLabs/updates-15023371589
fix: edit template option not visible in mobile view
2025-05-14 20:00:02 +05:30
prafull-opensignlabsandGitHub ab08d2615d Update package.json 2025-05-14 19:59:01 +05:30
prafull-opensignlabs 2b0c7488d9 Merge pull request #610 from OpenSignLabs/staging
fix: edit template option not visible in mobile view
2025-05-14 14:27:57 +00:00
prafull-opensignlabsandGitHub 6927540741 Merge pull request #1731 from OpenSignLabs/updates-15022725718
v2.21.0
2025-05-14 19:33:19 +05:30
prafull-opensignlabs 1ceeee36ac v2.21.0
Merge pull request #601 from OpenSignLabs/staging
2025-05-14 14:00:50 +00:00
116 changed files with 5897 additions and 2834 deletions
@@ -3,11 +3,11 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" /> <meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> --> <!-- <link rel="apple-touch-icon" href="/logo192.png" /> -->
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
@@ -17,5 +17,6 @@
</head> </head>
<body> <body>
<div id="root" style="touch-action:pan-x pan-y;"></div> <div id="root" style="touch-action:pan-x pan-y;"></div>
<script type="module" src="/src/index.jsx"></script>
</body> </body>
</html> </html>
+3382 -638
View File
File diff suppressed because it is too large Load Diff
+27 -14
View File
@@ -4,7 +4,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.2", "@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.13.4", "@lottiefiles/dotlottie-react": "^0.13.5",
"@pdf-lib/fontkit": "^1.1.1", "@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.6", "@radix-ui/themes": "^3.1.6",
"@reduxjs/toolkit": "^2.7.0", "@reduxjs/toolkit": "^2.7.0",
@@ -13,7 +13,7 @@
"date-fns-tz": "^3.2.0", "date-fns-tz": "^3.2.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^23.16.8", "i18next": "^23.16.8",
"i18next-browser-languagedetector": "^8.0.5", "i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2", "i18next-http-backend": "^3.0.2",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
@@ -41,7 +41,7 @@
"react-quill-new": "^3.4.6", "react-quill-new": "^3.4.6",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-rnd": "^10.5.2", "react-rnd": "^10.5.2",
"react-router": "^7.5.2", "react-router": "^7.5.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-scrollbars-custom": "^4.1.1", "react-scrollbars-custom": "^4.1.1",
"react-select": "^5.10.1", "react-select": "^5.10.1",
@@ -57,18 +57,20 @@
"serve": "^14.2.4", "serve": "^14.2.4",
"styled-components": "^5.3.0", "styled-components": "^5.3.0",
"web-vitals": "^4.2.4", "web-vitals": "^4.2.4",
"ws": "^8.18.1", "ws": "^8.18.2",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
}, },
"scripts": { "scripts": {
"build": "npm run version && react-scripts build", "build": "npm run version && NODE_OPTIONS=\"--max-old-space-size=8192\" vite build",
"start-dev": "react-scripts start", "start-dev": "vite",
"dev": "vite",
"preview": "vite preview",
"start": "serve -s build", "start": "serve -s build",
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt", "version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"", "version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
"build-win": "npm run version-win && react-scripts build", "build-win": "npm run version-win && vite build",
"test": "react-scripts test", "test": "vitest run",
"eject": "react-scripts eject", "test:watch": "vitest",
"release": "standard-version", "release": "standard-version",
"commit": "cz" "commit": "cz"
}, },
@@ -96,10 +98,16 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.26.10", "@babel/core": "^7.27.1",
"@babel/preset-env": "^7.26.9", "@babel/preset-env": "^7.27.1",
"@babel/preset-react": "^7.26.3", "@babel/preset-react": "^7.27.1",
"@babel/runtime-corejs2": "^7.27.0", "@babel/runtime-corejs2": "^7.27.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^18.3.21",
"@vitejs/plugin-react": "^4.4.1",
"@vitejs/plugin-react-swc": "^3.9.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"babel-loader": "^10.0.0", "babel-loader": "^10.0.0",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
@@ -109,15 +117,20 @@
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"dotenv-webpack": "^8.1.0", "dotenv-webpack": "^8.1.0",
"eslint": "^8.57.1", "eslint": "^8.57.1",
"eslint-plugin-prettier": "^5.2.6", "eslint-plugin-prettier": "^5.4.0",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"lint-staged": "^15.5.1", "lint-staged": "^15.5.1",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"pretty-quick": "^4.1.1", "pretty-quick": "^4.1.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"terser-webpack-plugin": "^5.3.14", "terser-webpack-plugin": "^5.3.14",
"vite": "^6.3.5",
"vite-plugin-svgr": "^4.3.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.1.3",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
}, },
"overrides": { "overrides": {
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
import "@testing-library/jest-dom";
@@ -1,7 +1,6 @@
import React, { useState, useEffect, lazy } from "react"; import React, { useState, useEffect, lazy } from "react";
import { Routes, Route, BrowserRouter } from "react-router"; import { Routes, Route, BrowserRouter } from "react-router";
import { pdfjs } from "react-pdf"; import { pdfjs } from "react-pdf";
import Login from "./pages/Login";
import Form from "./pages/Form"; import Form from "./pages/Form";
import Report from "./pages/Report"; import Report from "./pages/Report";
import Dashboard from "./pages/Dashboard"; import Dashboard from "./pages/Dashboard";
@@ -30,6 +29,7 @@ const ManageSign = lazy(() => import("./pages/Managesign"));
const AddAdmin = lazy(() => import("./pages/AddAdmin")); const AddAdmin = lazy(() => import("./pages/AddAdmin"));
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin")); const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
const Preferences = lazy(() => import("./pages/Preferences")); const Preferences = lazy(() => import("./pages/Preferences"));
const Login = lazy(() => import("./pages/Login"));
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => { const AppLoader = () => {
@@ -67,7 +67,7 @@ function App() {
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
<Route element={<ValidateRoute />}> <Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} /> <Route exact path="/" element={<LazyPage Page={Login} />} />
<Route <Route
path="/addadmin" path="/addadmin"
element={<LazyPage Page={AddAdmin} />} element={<LazyPage Page={AddAdmin} />}
-275
View File
@@ -1,275 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { copytoData, usertimezone } from "../constant/Utils";
import { emailRegex } from "../constant/const";
import { useTranslation } from "react-i18next";
function generatePassword(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const AddUser = (props) => {
const { t } = useTranslation();
const [formdata, setFormdata] = useState({
name: "",
phone: "",
email: "",
team: "",
password: "",
role: ""
});
const [isFormLoader, setIsFormLoader] = useState(false);
const [teamList, setTeamList] = useState([]);
const role = ["OrgAdmin", "Editor", "User"];
useEffect(() => {
getTeamList();
// eslint-disable-next-line
}, []);
const getTeamList = async () => {
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const teamRes = await Parse.Cloud.run("getteams", { active: true });
if (teamRes.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamRes));
setTeamList(_teamRes);
const allUserId =
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
setFormdata((prev) => ({ ...prev, team: allUserId }));
}
};
const checkUserExist = async () => {
try {
const res = await Parse.Cloud.run("getUserDetails", {
email: formdata.email
});
if (res) {
return true;
} else {
return false;
}
} catch (err) {
console.log("err", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
if (!emailRegex.test(formdata.email)) {
alert("Please enter a valid email address.");
} else {
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.showAlert("danger", t("user-already-exist"));
setIsFormLoader(false);
} else {
if (localStorage.getItem("TenantId")) {
const timezone = usertimezone;
try {
const params = {
name: formdata.name,
email: formdata.email,
phone: formdata.phone,
password: formdata.password,
role: formdata.role,
team: formdata.team,
timezone: timezone,
tenantId: localStorage.getItem("TenantId"),
organization: {
objectId: localUser?.OrganizationId?.objectId,
company: localUser?.Company
}
};
const res = await Parse.Cloud.run("adduser", params);
const parseData = JSON.parse(JSON.stringify(res));
console.log("parseData ", parseData);
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
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);
}
setIsFormLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
props.showAlert("success", t("user-created-successfully"));
} catch (err) {
console.log("err", err);
setIsFormLoader(false);
props.showAlert("danger", t("something-went-wrong-mssg"));
}
} else {
props.showAlert("danger", t("something-went-wrong-mssg"));
}
}
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
if (props.closePopup) {
props.closePopup();
}
};
const handleChange = (event) => {
let { name, value } = event.target;
if (name === "email") {
value = value?.toLowerCase()?.replace(/\s/g, "");
}
setFormdata((prev) => ({ ...prev, [name]: value }));
};
const copytoclipboard = (text) => {
copytoData(text);
props.showAlert("success", t("copied"));
};
return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
<Title title={t("add-user")} />
{isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
required
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
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 className="block text-xs text-gray-700 font-semibold">
{t("password")}
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
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] select-none">
{t("password-generateed")}
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder={t("phone-optional")}
value={formdata.phone}
onChange={(e) => handleChange(e)}
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"
>
{t("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"
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
{t("submit")}
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
{t("cancel")}
</div>
</div>
</form>
</div>
</div>
);
};
export default AddUser;
+288
View File
@@ -0,0 +1,288 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import {
copytoData,
usertimezone
} from "../constant/Utils";
import {
emailRegex,
} from "../constant/const";
import {
useTranslation
} from "react-i18next";
function generatePassword(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const AddUser = (props) => {
const { t } = useTranslation();
const [formdata, setFormdata] = useState({
name: "",
phone: "",
email: "",
team: "",
password: "",
role: ""
});
const [isFormLoader, setIsFormLoader] = useState(false);
const [teamList, setTeamList] = useState([]);
const role = ["OrgAdmin", "Editor", "User"];
useEffect(() => {
getTeamList();
// eslint-disable-next-line
}, []);
const getTeamList = async () => {
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const teamRes = await Parse.Cloud.run("getteams", { active: true });
if (teamRes.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamRes));
setTeamList(_teamRes);
const allUserId =
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
setFormdata((prev) => ({ ...prev, team: allUserId }));
}
};
const checkUserExist = async () => {
try {
const res = await Parse.Cloud.run("getUserDetails", {
email: formdata.email
});
if (res) {
return true;
} else {
return false;
}
} catch (err) {
console.log("err", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
if (!emailRegex.test(formdata.email)) {
alert("Please enter a valid email address.");
} else {
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.showAlert("danger", t("user-already-exist"));
setIsFormLoader(false);
} else {
if (localStorage.getItem("TenantId")) {
const timezone = usertimezone;
try {
const params = {
name: formdata.name,
email: formdata.email,
phone: formdata.phone,
password: formdata.password,
role: formdata.role,
team: formdata.team,
timezone: timezone,
tenantId: localStorage.getItem("TenantId"),
organization: {
objectId: localUser?.OrganizationId?.objectId,
company: localUser?.Company
},
};
const res = await Parse.Cloud.run("adduser", params);
const parseData = JSON.parse(JSON.stringify(res));
console.log("parseData ", parseData);
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
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);
}
setIsFormLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
props.showAlert("success", t("user-created-successfully"));
} catch (err) {
console.log("err", err);
setIsFormLoader(false);
props.showAlert("danger", t("something-went-wrong-mssg"));
}
} else {
props.showAlert("danger", t("something-went-wrong-mssg"));
}
}
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
if (props.closePopup) {
props.closePopup();
}
};
const handleChange = (event) => {
let { name, value } = event.target;
if (name === "email") {
value = value?.toLowerCase()?.replace(/\s/g, "");
}
setFormdata((prev) => ({ ...prev, [name]: value }));
};
const copytoclipboard = (text) => {
copytoData(text);
props.showAlert("success", t("copied"));
};
return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
<Title title={t("add-user")} />
{isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
required
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
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 className="block text-xs text-gray-700 font-semibold">
{t("password")}
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
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] select-none">
{t("password-generateed")}
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder={t("phone-optional")}
value={formdata.phone}
onChange={(e) => handleChange(e)}
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"
>
{t("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"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
{t("submit")}
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
{t("cancel")}
</div>
</div>
</form>
</div>
</div>
);
};
export default AddUser;
@@ -3,7 +3,9 @@ import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput"; import SuggestionInput from "./shared/fields/SuggestionInput";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
const BulkSendUi = (props) => { const BulkSendUi = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [forms, setForms] = useState([]); const [forms, setForms] = useState([]);
@@ -72,6 +74,7 @@ const BulkSendUi = (props) => {
setForms(newForms); setForms(newForms);
}; };
const handleRemoveForm = (index) => { const handleRemoveForm = (index) => {
const updatedForms = forms.filter((_, i) => i !== index); const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms); setForms(updatedForms);
@@ -159,16 +162,15 @@ const BulkSendUi = (props) => {
}; };
const batchQuery = async (Documents) => { const batchQuery = async (Documents) => {
const token = { const token =
"X-Parse-Session-Token": localStorage.getItem("accesstoken") { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
};
const functionsUrl = `${localStorage.getItem( const functionsUrl = `${localStorage.getItem(
"baseUrl" "baseUrl"
)}functions/batchdocuments`; )}functions/batchdocuments`;
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token ...token,
}; };
const params = { Documents: JSON.stringify(Documents) }; const params = { Documents: JSON.stringify(Documents) };
try { try {
@@ -275,7 +277,8 @@ const BulkSendUi = (props) => {
)} )}
</> </>
) : ( ) : (
<></> <>
</>
)} )}
</> </>
)} )}
@@ -32,6 +32,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
async function initializeHead() { async function initializeHead() {
const applogo = await getAppLogo(); const applogo = await getAppLogo();
if (applogo?.logo) { if (applogo?.logo) {
@@ -1,4 +1,7 @@
import React, { useState, useRef } from "react"; import React, {
useState,
useRef,
} from "react";
import { import {
base64ToArrayBuffer, base64ToArrayBuffer,
convertBase64ToFile, convertBase64ToFile,
@@ -24,9 +27,10 @@ const EditTemplate = ({
template, template,
onSuccess, onSuccess,
setPdfArrayBuffer, setPdfArrayBuffer,
setPdfBase64Url setPdfBase64Url,
}) => { }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const inputFileRef = useRef(null); const inputFileRef = useRef(null);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
@@ -165,7 +169,10 @@ const EditTemplate = ({
} }
let pdfUrl; let pdfUrl;
if (uploadPdf?.base64) { if (uploadPdf?.base64) {
pdfUrl = await convertBase64ToFile(uploadPdf.name, uploadPdf.base64); pdfUrl = await convertBase64ToFile(
uploadPdf.name,
uploadPdf.base64,
);
setUploadPdf((prev) => ({ ...prev, url: pdfUrl })); setUploadPdf((prev) => ({ ...prev, url: pdfUrl }));
const pdfBuffer = base64ToArrayBuffer(uploadPdf.base64); const pdfBuffer = base64ToArrayBuffer(uploadPdf.base64);
setPdfArrayBuffer && setPdfArrayBuffer(pdfBuffer); setPdfArrayBuffer && setPdfArrayBuffer(pdfBuffer);
@@ -447,7 +454,11 @@ const EditTemplate = ({
</Tooltip> </Tooltip>
</label> </label>
<div className="flex flex-col md:flex-row md:gap-4"> <div className="flex flex-col md:flex-row md:gap-4">
<div className={`flex items-center gap-2 ml-2 mb-1`}> <div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -456,7 +467,11 @@ const EditTemplate = ({
/> />
<div className="text-center">{t("yes")}</div> <div className="text-center">{t("yes")}</div>
</div> </div>
<div className={`flex items-center gap-2 ml-2 mb-1`}> <div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -1,6 +1,10 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { handleToPrint } from "../../constant/Utils"; import {
import { emailRegex } from "../../constant/const"; handleToPrint,
} from "../../constant/Utils";
import {
emailRegex,
} from "../../constant/const";
import Loader from "../../primitives/Loader"; import Loader from "../../primitives/Loader";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -37,7 +41,8 @@ function EmailComponent({
setEmailList([]); setEmailList([]);
}, 1500); }, 1500);
setIsLoading(false); setIsLoading(false);
} else { }
else {
setIsLoading(false); setIsLoading(false);
setIsEmail(false); setIsEmail(false);
setIsAlert({ setIsAlert({
@@ -276,6 +276,20 @@ function Header(props) {
className="bg-white shadow-md rounded-md px-3 py-2" className="bg-white shadow-md rounded-md px-3 py-2"
sideOffset={5} sideOffset={5}
> >
{props?.setIsEditTemplate && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => props?.setIsEditTemplate(true)}
>
<div className="flex flex-row">
<i
className="fa-light fa-gear mr-[3px]"
aria-hidden="true"
></i>
<span className="font-[500]">{t("Edit")}</span>
</div>
</DropdownMenu.Item>
)}
<DropdownMenu.Item <DropdownMenu.Item
className="DropdownMenuItem" className="DropdownMenuItem"
onClick={() => onClick={() =>
@@ -7,7 +7,8 @@ import {
radioButtonWidget, radioButtonWidget,
range, range,
textInputWidget, textInputWidget,
textWidget textWidget,
widgetDataValue
} from "../../constant/Utils"; } from "../../constant/Utils";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
@@ -24,6 +25,8 @@ const widgetCls =
function PlaceholderType(props) { function PlaceholderType(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const type = props?.pos?.type; const type = props?.pos?.type;
const widgetData =
props.pos?.options?.defaultValue || props.pos?.options?.response;
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`); const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
const [selectOption, setSelectOption] = useState(""); const [selectOption, setSelectOption] = useState("");
const [validatePlaceholder, setValidatePlaceholder] = useState(""); const [validatePlaceholder, setValidatePlaceholder] = useState("");
@@ -141,29 +144,6 @@ function PlaceholderType(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => {
if (
["name", "email", "job title", "company"].includes(props.pos?.type) &&
props.isNeedSign &&
props.data?.signerObjId === props?.signerObjId
) {
const defaultData = props.pos?.options?.defaultValue;
if (defaultData) {
setTextValue(defaultData);
}
if (props.pos?.options?.hint) {
setHint(props.pos?.options.hint);
} else {
setHint(props.pos?.type);
}
} else if ([textInputWidget].includes(props.pos?.type)) {
const defaultData = props.pos?.options?.defaultValue;
if (defaultData) {
setTextValue(defaultData);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos?.options?.defaultValue]);
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => ( const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div <div
style={{ style={{
@@ -180,28 +160,26 @@ function PlaceholderType(props) {
</div> </div>
)); ));
ExampleCustomInput.displayName = "ExampleCustomInput"; ExampleCustomInput.displayName = "ExampleCustomInput";
useEffect(() => { useEffect(() => {
if ( if (
["name", "email", "job title", "company"].includes(type) && ["name", "email", "job title", "company", textInputWidget].includes(
type
) &&
props.isNeedSign && props.isNeedSign &&
props.data?.signerObjId === props.signerObjId props.data?.signerObjId === props.signerObjId
) { ) {
const isDefault = true; if (widgetData) {
const senderUser = localStorage.getItem("Extand_Class"); setTextValue(widgetData);
const jsonSender = JSON.parse(senderUser); }
onChangeInput( }
jsonSender && jsonSender[0],
null, if (props.pos?.options?.hint) {
props.xyPosition, setHint(props.pos?.options.hint);
null, } else {
props.setXyPosition, setHint(props.pos?.type);
props.data.Id,
isDefault
);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [type]); }, [type, widgetData]);
//function for show checked checkbox //function for show checked checkbox
const selectCheckbox = (ind) => { const selectCheckbox = (ind) => {
const res = props.pos.options?.response; const res = props.pos.options?.response;
@@ -462,10 +440,14 @@ function PlaceholderType(props) {
props.isNeedSign && props.isNeedSign &&
(props.pos.options?.isReadOnly || (props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId) props.data?.signerObjId !== props.signerObjId)
? " bg-black/68 select-none " ? "select-none"
: "" + textWidgetCls : "" + textWidgetCls
}`} }`}
style={{ fontSize: fontSize, color: fontColor }} style={{
fontSize: fontSize,
color: fontColor,
background: props.data?.blockColor
}}
disabled={ disabled={
props.isNeedSign && props.isNeedSign &&
(props.pos.options?.isReadOnly || (props.pos.options?.isReadOnly ||
@@ -460,15 +460,7 @@ function SignPad(props) {
props?.convertToImg(fontSelect, typedSignature, data); props?.convertToImg(fontSelect, typedSignature, data);
setPenColor(allColor[key]); setPenColor(allColor[key]);
}} }}
className={`border-b-[2px] ${ className={`border-b-[2px] ${key === 0 && penColor === "blue" ? "border-blue-600" : key === 1 && penColor === "red" ? "border-red-500" : key === 2 && penColor === "black" ? "border-black" : "border-white"} text-[${data}] text-[16px] fa-light fa-pen-nib`}
key === 0 && penColor === "blue"
? "border-blue-600"
: key === 1 && penColor === "red"
? "border-red-500"
: key === 2 && penColor === "black"
? "border-black"
: "border-white"
} text-[${data}] text-[16px] fa-light fa-pen-nib`}
></i> ></i>
); );
})} })}
@@ -651,11 +643,7 @@ function SignPad(props) {
<> <>
<div className="flex justify-center"> <div className="flex justify-center">
<div <div
className={`${ className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
> >
<img <img
alt="stamp img" alt="stamp img"
@@ -682,11 +670,7 @@ function SignPad(props) {
<> <>
<div className="flex justify-center"> <div className="flex justify-center">
<div <div
className={`${ className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
> >
<img <img
alt="stamp img" alt="stamp img"
@@ -711,11 +695,7 @@ function SignPad(props) {
!props?.image ? ( !props?.image ? (
<div className="flex justify-center"> <div className="flex justify-center">
<div <div
className={`${ className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
onClick={() => props?.imageRef.current.click()} onClick={() => props?.imageRef.current.click()}
> >
<input <input
@@ -734,11 +714,7 @@ function SignPad(props) {
<> <>
<div className="flex justify-center"> <div className="flex justify-center">
<div <div
className={`${ className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] mb-[6px] overflow-hidden`}
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
} bg-white border-[1.3px] border-[#007bff] mb-[6px] overflow-hidden`}
> >
<img <img
alt="print img" alt="print img"
@@ -826,11 +802,7 @@ function SignPad(props) {
ref={canvasRef} ref={canvasRef}
penColor={penColor} penColor={penColor}
canvasProps={{ canvasProps={{
className: `${ className: `${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-[#007bff]`
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
} border-[1.3px] border-[#007bff]`
}} }}
onEnd={() => onEnd={() =>
handleSignatureChange( handleSignatureChange(
@@ -117,6 +117,7 @@ const WidgetNameModal = (props) => {
} }
} }
const handleCheckboxChange = (index) => { const handleCheckboxChange = (index) => {
// Update the state with the modified array // Update the state with the modified array
setSignatureType((prev) => setSignatureType((prev) =>
@@ -6,7 +6,8 @@ import { useTranslation } from "react-i18next";
const FolderModal = (props) => { const FolderModal = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const [clickFolder, setClickFolder] = useState(""); const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]); const [folderList, setFolderList] = useState([]);
@@ -6,7 +6,8 @@ import Tooltip from "../../../primitives/Tooltip";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => { const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const [isOpen, SetIsOpen] = useState(false); const [isOpen, SetIsOpen] = useState(false);
@@ -117,7 +117,9 @@ const SignersInput = (props) => {
}; };
const loadOptions = async (inputValue) => { const loadOptions = async (inputValue) => {
try { try {
const contactRes = await findContact(inputValue); const contactRes = await findContact(
inputValue,
);
if (contactRes) { if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes)); const res = JSON.parse(JSON.stringify(contactRes));
//compareArrays is a function where compare between two array (total signersList and dcument signers list) //compareArrays is a function where compare between two array (total signersList and dcument signers list)
@@ -152,9 +154,7 @@ const SignersInput = (props) => {
{props.label ? props.label : t("signers")} {props.label ? props.label : t("signers")}
{props.required && <span className="text-red-500 text-[13px]">*</span>} {props.required && <span className="text-red-500 text-[13px]">*</span>}
<span <span
className={`z-[${ className={`z-[${props?.helptextZindex ? props.helptextZindex : 30}] absolute ml-1 text-xs`}
props?.helptextZindex ? props.helptextZindex : 30
}] absolute ml-1 text-xs`}
> >
<Tooltip <Tooltip
id={`${props.label ? props.label : "signers"}-tooltip`} id={`${props.label ? props.label : "signers"}-tooltip`}
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
import { NavLink } from "react-router"; import { NavLink } from "react-router";
const Menu = ({ item, isOpen, closeSidebar }) => { const Menu = ({ item, isOpen, closeSidebar }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
import { NavLink } from "react-router"; import { NavLink } from "react-router";
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => { const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const { title, icon, children } = item; const { title, icon, children } = item;
@@ -7,7 +7,9 @@ import { appInfo } from "./appinfo";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
import printModule from "print-js"; import printModule from "print-js";
import fontkit from "@pdf-lib/fontkit"; import fontkit from "@pdf-lib/fontkit";
import { themeColor } from "./const"; import {
themeColor
} from "./const";
import { format, toZonedTime } from "date-fns-tz"; import { format, toZonedTime } from "date-fns-tz";
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]; export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
@@ -135,12 +137,15 @@ export const pdfNewWidthFun = (divRef) => {
}; };
//`contractUsers` function is used to get contract_User details //`contractUsers` function is used to get contract_User details
export const contractUsers = async () => { export const contractUsers = async (
) => {
try { try {
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`; const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken"); const accesstoken =
const token = { "X-Parse-Session-Token": accesstoken }; localStorage.getItem("accesstoken");
const token =
{ "X-Parse-Session-Token": accesstoken };
const headers = { const headers = {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -298,7 +303,7 @@ export const changeDateToMomentFormat = (format) => {
return "L"; return "L";
} }
}; };
export const addWidgetOptions = (type, signer) => { export const addWidgetOptions = (type, signer, widgetValue) => {
const defaultOpt = { name: type, status: "required" }; const defaultOpt = { name: type, status: "required" };
switch (type) { switch (type) {
case "signature": case "signature":
@@ -315,11 +320,11 @@ export const addWidgetOptions = (type, signer) => {
case "initials": case "initials":
return defaultOpt; return defaultOpt;
case "name": case "name":
return { ...defaultOpt }; return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
case "company": case "company":
return { ...defaultOpt }; return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
case "job title": case "job title":
return { ...defaultOpt }; return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
case "date": { case "date": {
const dateFormat = signer?.DateFormat const dateFormat = signer?.DateFormat
? selectFormat(signer?.DateFormat) ? selectFormat(signer?.DateFormat)
@@ -333,7 +338,11 @@ export const addWidgetOptions = (type, signer) => {
case "image": case "image":
return defaultOpt; return defaultOpt;
case "email": case "email":
return { ...defaultOpt, validation: { type: "email", pattern: "" } }; return {
...defaultOpt,
validation: { type: "email", pattern: "" },
defaultValue: widgetValue ? widgetValue : ""
};
case "dropdown": case "dropdown":
return defaultOpt; return defaultOpt;
case radioButtonWidget: case radioButtonWidget:
@@ -556,7 +565,7 @@ export const signPdfFun = async (
documentId, documentId,
signerObjectId, signerObjectId,
objectId, objectId,
widgets widgets,
) => { ) => {
let isCustomCompletionMail = false; let isCustomCompletionMail = false;
try { try {
@@ -565,7 +574,10 @@ export const signPdfFun = async (
if (tenantDetails && tenantDetails === "user does not exist!") { if (tenantDetails && tenantDetails === "user does not exist!") {
return { status: "error", message: "User does not exist." }; return { status: "error", message: "User does not exist." };
} else { } else {
if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) { if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject
) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
} }
@@ -855,7 +867,8 @@ export const onChangeInput = (
...position, ...position,
options: { options: {
...position.options, ...position.options,
response: value response: value,
defaultValue:""
} }
}; };
} }
@@ -1009,8 +1022,7 @@ export const calculateInitialWidthHeight = (widgetData) => {
document.body.removeChild(span); document.body.removeChild(span);
return { getWidth: width, getHeight: height }; return { getWidth: width, getHeight: height };
}; };
export const addInitialData = (signerPos, setXyPosition, value, userId) => { export const widgetDataValue = (type, value) => {
function widgetDataValue(type) {
switch (type) { switch (type) {
case "name": case "name":
return value?.Name; return value?.Name;
@@ -1023,7 +1035,8 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
default: default:
return ""; return "";
} }
} };
export const addInitialData = (signerPos, setXyPosition, value, userId) => {
return signerPos.map((item) => { return signerPos.map((item) => {
if (item.placeHolder && item.placeHolder.length > 0) { if (item.placeHolder && item.placeHolder.length > 0) {
// If there is a nested array, recursively add the field to the last object // If there is a nested array, recursively add the field to the last object
@@ -1048,7 +1061,7 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
// Adjust this line to add the desired field // Adjust this line to add the desired field
}; };
} else { } else {
const widgetData = widgetDataValue(item.type); const widgetData = widgetDataValue(item.type, value);
if (["name", "company", "job title", "email"].includes(item.type)) { if (["name", "company", "job title", "email"].includes(item.type)) {
return { return {
...item, ...item,
@@ -1066,7 +1079,8 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
//function for embed document id //function for embed document id
export const embedDocId = async (pdfDoc, documentId, allPages) => { export const embedDocId = async (pdfDoc, documentId, allPages) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
// `fontBytes` is used to embed custom font in pdf // `fontBytes` is used to embed custom font in pdf
const fontBytes = await fileasbytes( const fontBytes = await fileasbytes(
"https://cdn.opensignlabs.com/webfonts/times.ttf" "https://cdn.opensignlabs.com/webfonts/times.ttf"
@@ -1075,7 +1089,8 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
const font = await pdfDoc.embedFont(fontBytes, { subset: true }); const font = await pdfDoc.embedFont(fontBytes, { subset: true });
for (let i = 0; i < allPages; i++) { for (let i = 0; i < allPages; i++) {
const fontSize = 10; const fontSize = 10;
const textContent = documentId && `${appName} DocumentId: ${documentId} `; const textContent =
documentId && `${appName} DocumentId: ${documentId} `;
const pages = pdfDoc.getPages(); const pages = pdfDoc.getPages();
const page = pages[i]; const page = pages[i];
try { try {
@@ -1900,7 +1915,9 @@ export const placeholderHeight = (pos) => {
}; };
//function for getting contracts_contactbook details //function for getting contracts_contactbook details
export const contactBook = async (objectId) => { export const contactBook = async (
objectId,
) => {
const result = await axios const result = await axios
.get( .get(
`${localStorage.getItem( `${localStorage.getItem(
@@ -1910,7 +1927,8 @@ export const contactBook = async (objectId) => {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token":
localStorage.getItem("accesstoken")
} }
} }
) )
@@ -1928,9 +1946,12 @@ export const contactBook = async (objectId) => {
}; };
//function for getting document details from contract_Documents class //function for getting document details from contract_Documents class
export const contractDocument = async (documentId) => { export const contractDocument = async (
documentId,
) => {
const data = { docId: documentId }; const data = { docId: documentId };
const token = { sessionToken: localStorage.getItem("accesstoken") }; const token =
{ sessionToken: localStorage.getItem("accesstoken") };
const documentDeatils = await axios const documentDeatils = await axios
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, { .post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
headers: { headers: {
@@ -2132,13 +2153,18 @@ export const getAppLogo = async () => {
} }
} }
}; };
export const getTenantDetails = async (objectId, contactId) => { export const getTenantDetails = async (
objectId,
contactId
) => {
try { try {
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`; const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken"); const accesstoken = localStorage.getItem("accesstoken");
const token = { "X-Parse-Session-Token": accesstoken }; const token =
const data = { userId: objectId, contactId: contactId }; { "X-Parse-Session-Token": accesstoken };
const data =
{ userId: objectId, contactId: contactId };
const res = await axios.post(url, data, { const res = await axios.post(url, data, {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -2219,7 +2245,7 @@ export const handleSendOTP = async (email) => {
"X-Parse-Application-Id": localStorage.getItem("parseAppId") "X-Parse-Application-Id": localStorage.getItem("parseAppId")
}; };
const body = { const body = {
email: email email: email,
}; };
await axios.post(url, body, { headers: headers }); await axios.post(url, body, { headers: headers });
} catch (error) { } catch (error) {
@@ -2227,7 +2253,8 @@ export const handleSendOTP = async (email) => {
} }
}; };
export const fetchUrl = async (url, pdfName) => { export const fetchUrl = async (url, pdfName) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
try { try {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
@@ -2241,7 +2268,11 @@ export const fetchUrl = async (url, pdfName) => {
console.error("Error downloading the file:", error); console.error("Error downloading the file:", error);
} }
}; };
export const getSignedUrl = async (pdfUrl, docId, templateId) => { export const getSignedUrl = async (
pdfUrl,
docId,
templateId
) => {
//use only axios here due to public template sign //use only axios here due to public template sign
const axiosRes = await axios.post( const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
@@ -2302,7 +2333,10 @@ export const handleDownloadPdf = async (
setIsDownloading && setIsDownloading("pdf"); setIsDownloading && setIsDownloading("pdf");
const docId = pdfDetails?.[0]?.objectId || ""; const docId = pdfDetails?.[0]?.objectId || "";
try { try {
const url = await getSignedUrl(pdfUrl, docId); const url = await getSignedUrl(
pdfUrl,
docId,
);
await fetchUrl(url, pdfName); await fetchUrl(url, pdfName);
setIsDownloading && setIsDownloading(""); setIsDownloading && setIsDownloading("");
} catch (err) { } catch (err) {
@@ -2332,7 +2366,7 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ {
url: pdfUrl, url: pdfUrl,
docId: docId docId: docId,
}, },
{ {
headers: { headers: {
@@ -2376,7 +2410,8 @@ export const handleDownloadCertificate = async (
setIsDownloading, setIsDownloading,
isZip isZip
) => { ) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) { if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
try { try {
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl); await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
@@ -2460,13 +2495,14 @@ export const handleDownloadCertificate = async (
export function escapeRegExp(string) { export function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
} }
export async function findContact(value) { export async function findContact(
value,
) {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsigners`; const url = `${baseURL}functions/getsigners`;
const token = { const token =
"X-Parse-Session-Token": localStorage.getItem("accesstoken") { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
};
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
@@ -2678,7 +2714,10 @@ export function base64ToArrayBuffer(base64) {
return bytes.buffer; return bytes.buffer;
} }
export const convertBase64ToFile = async (pdfName, pdfBase64) => { export const convertBase64ToFile = async (
pdfName,
pdfBase64,
) => {
const fileName = sanitizeFileName(pdfName) + ".pdf"; const fileName = sanitizeFileName(pdfName) + ".pdf";
try { try {
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 }); const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
@@ -2981,10 +3020,13 @@ export const flattenPdf = async (pdfFile) => {
}; };
export const mailTemplate = (param) => { export const mailTemplate = (param) => {
const appName = "OpenSign™"; const appName =
const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`; "OpenSign™";
const logo =
`<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`; const opurl =
` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
const subject = `${param.senderName} has requested you to sign "${param.title}"`; const subject = `${param.senderName} has requested you to sign "${param.title}"`;
const body = const body =
@@ -3027,7 +3069,14 @@ export function formatDateTime(date, dateFormat, timeZone, is12Hour) {
: formatTimeInTimezone(date, timeZone); : formatTimeInTimezone(date, timeZone);
} }
export const updateDateWidgetsRes = (placeHolders, signerId) => { export const updateDateWidgetsRes = (documentData, signerId, journey) => {
const extUser =
localStorage.getItem("Extand_Class");
const contactUser = documentData?.Signers.find(
(data) => data.objectId === signerId
);
const placeHolders = documentData?.Placeholders;
const userDetails = extUser ? JSON.parse(extUser)[0] : contactUser;
return placeHolders?.map((item) => { return placeHolders?.map((item) => {
if (item?.signerObjId === signerId) { if (item?.signerObjId === signerId) {
return { return {
@@ -3046,6 +3095,18 @@ export const updateDateWidgetsRes = (placeHolders, signerId) => {
) )
} }
}; };
} else if (
["name", "email", "job title", "company"].includes(widget.type) &&
!widget.options.defaultValue &&
!widget.options.response
) {
return {
...widget,
options: {
...widget.options,
response: widgetDataValue(widget.type, userDetails)
}
};
} }
return widget; return widget;
}) })
-3
View File
@@ -11,9 +11,6 @@ export const appInfo = {
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign", appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
baseUrl: serverUrl_fn(), baseUrl: serverUrl_fn(),
defaultRole: "contracts_User", defaultRole: "contracts_User",
fbAppId: process.env.REACT_APP_FBAPPID
? `${process.env.REACT_APP_FBAPPID}`
: "",
fev_Icon: fev_Icon:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC",
googleClietId: process.env.REACT_APP_GOOGLECLIENTID googleClietId: process.env.REACT_APP_GOOGLECLIENTID
-4
View File
@@ -1,4 +0,0 @@
export default function linkURL(url) {
let newURL = (url && decodeURIComponent(url)) || "";
return newURL.substring(newURL.indexOf(".com/") + 5, newURL.indexOf("_"));
}
@@ -13,15 +13,13 @@ import {
MouseTransition, MouseTransition,
Preview Preview
} from "react-dnd-multi-backend"; } from "react-dnd-multi-backend";
import DragElement from "./components/pdf/DragElement"; import DragElement from "./components/pdf/DragElement.jsx";
import Parse from "parse"; import Parse from "parse";
import "./polyfills"; import "./polyfills";
import { serverUrl_fn } from "./constant/appinfo"; import { serverUrl_fn } from "./constant/appinfo";
import "./i18n"; import "./i18n";
const appId = process.env.REACT_APP_APPID const appId = import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign";
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = serverUrl_fn(); const serverUrl = serverUrl_fn();
Parse.initialize(appId); Parse.initialize(appId);
Parse.serverURL = serverUrl; Parse.serverURL = serverUrl;
@@ -55,6 +53,7 @@ const generatePreview = (props) => {
); );
}; };
const root = ReactDOM.createRoot(document.getElementById("root")); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<CookiesProvider defaultSetOptions={{ path: "/" }}> <CookiesProvider defaultSetOptions={{ path: "/" }}>
+1 -1
View File
@@ -36,7 +36,7 @@ const dashboardJson = [
queryType: "", queryType: "",
class: "contracts_Document", class: "contracts_Document",
query: query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"SignedUrl":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1', 'where={"Type":null,"Signers":{"#*exists":true},"Placeholders":{"#*exists":true},"SignedUrl":{"#*exists":true},"IsCompleted":false,"IsDeclined":false,"IsArchive":null,"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&keys=Name,ExpiryDate,SignedUrl,Signers&count=1',
key: "count", key: "count",
Redirect_type: "Report", Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF", Redirect_id: "1MwEuxLEkF",
@@ -15,7 +15,8 @@ import { showHeader } from "../redux/reducers/showHeader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const HomeLayout = () => { const HomeLayout = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -16,7 +16,8 @@ import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import { emailRegex } from "../constant/const";
const AddAdmin = () => { const AddAdmin = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -175,7 +176,6 @@ const AddAdmin = () => {
localStorage.setItem("accesstoken", sessionToken); localStorage.setItem("accesstoken", sessionToken);
localStorage.setItem("UserInformation", JSON.stringify(_user)); localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("accesstoken", _user.sessionToken); localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) { if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic); localStorage.setItem("profileImg", _user.ProfilePic);
} else { } else {
@@ -5,7 +5,7 @@ import {
getBase64FromUrl, getBase64FromUrl,
handleDownloadCertificate, handleDownloadCertificate,
handleDownloadPdf, handleDownloadPdf,
handleToPrint handleToPrint,
} from "../constant/Utils"; } from "../constant/Utils";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
@@ -7,7 +7,9 @@ import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer"; import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
@@ -42,7 +42,8 @@ function Form() {
} }
const Forms = (props) => { const Forms = (props) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const abortController = new AbortController(); const abortController = new AbortController();
const inputFileRef = useRef(null); const inputFileRef = useRef(null);
@@ -239,7 +240,9 @@ const Forms = (props) => {
if (fileRes.url) { if (fileRes.url) {
setFileUpload(fileRes.url); setFileUpload(fileRes.url);
removeFile(); removeFile();
const title = generateTitleFromFilename(files?.[0]?.name); const title = generateTitleFromFilename(
files?.[0]?.name
);
setFormData((obj) => ({ ...obj, Name: title })); setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId); SaveFileSize(size, fileRes.url, tenantId);
@@ -451,7 +454,8 @@ const Forms = (props) => {
setSigners([]); setSigners([]);
setBcc([]); setBcc([]);
setFolder({ ObjectId: "", Name: "" }); setFolder({ ObjectId: "", Name: "" });
const notifySign = extUserData?.NotifyOnSignatures const notifySign =
extUserData?.NotifyOnSignatures
? extUserData?.NotifyOnSignatures ? extUserData?.NotifyOnSignatures
: true; : true;
setFormData({ setFormData({
@@ -529,7 +533,8 @@ const Forms = (props) => {
setSigners([]); setSigners([]);
setBcc([]); setBcc([]);
setFolder({ ObjectId: "", Name: "" }); setFolder({ ObjectId: "", Name: "" });
const notifySign = extUserData?.NotifyOnSignatures const notifySign =
extUserData?.NotifyOnSignatures
? extUserData?.NotifyOnSignatures ? extUserData?.NotifyOnSignatures
: true; : true;
let obj = { let obj = {
@@ -744,7 +749,9 @@ const Forms = (props) => {
)} )}
<div className="text-xs"> <div className="text-xs">
<label className="block"> <label className="block">
{`${`${t("report-heading.File")} (${t("file-type")}`}${")"}`} {`${`${t("report-heading.File")} (${t("file-type")}`}${
")"
}`}
<span className="text-red-500 text-[13px]">*</span> <span className="text-red-500 text-[13px]">*</span>
</label> </label>
{fileupload.length > 0 ? ( {fileupload.length > 0 ? (
@@ -768,7 +775,9 @@ const Forms = (props) => {
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={(e) => handleFileInput(e)} onChange={(e) => handleFileInput(e)}
ref={inputFileRef} ref={inputFileRef}
accept={"application/pdf,image/png,image/jpeg"} accept={
"application/pdf,image/png,image/jpeg"
}
onInvalid={(e) => onInvalid={(e) =>
e.target.setCustomValidity(t("input-required")) e.target.setCustomValidity(t("input-required"))
} }
@@ -1058,7 +1067,10 @@ const Forms = (props) => {
{isAdvanceOpt && ( {isAdvanceOpt && (
<div <div
style={{ style={{
height: props.title === "New Template" ? "100px" : "280px" height:
props.title === "New Template"
? "100px"
: "280px"
}} }}
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block" className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
></div> ></div>
@@ -1167,7 +1179,11 @@ const Forms = (props) => {
</Tooltip> </Tooltip>
</label> </label>
<div className="flex flex-col md:flex-row md:gap-4"> <div className="flex flex-col md:flex-row md:gap-4">
<div className={`flex items-center gap-2 ml-2 mb-1`}> <div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -1176,7 +1192,11 @@ const Forms = (props) => {
/> />
<div className="text-center">{t("yes")}</div> <div className="text-center">{t("yes")}</div>
</div> </div>
<div className={`flex items-center gap-2 ml-2 mb-1`}> <div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -1,8 +1,13 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import axios from "axios"; import axios from "axios";
import { emailRegex } from "../constant/const"; import {
import { contractUsers, saveLanguageInLocal } from "../constant/Utils"; emailRegex,
} from "../constant/const";
import {
contractUsers,
saveLanguageInLocal
} from "../constant/Utils";
import logo from "../assets/images/logo.png"; import logo from "../assets/images/logo.png";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import Parse from "parse"; import Parse from "parse";
@@ -33,6 +38,7 @@ function GuestLogin() {
const [sendmail, setSendmail] = useState(); const [sendmail, setSendmail] = useState();
const [contact, setContact] = useState({ name: "", phone: "", email: "" }); const [contact, setContact] = useState({ name: "", phone: "", email: "" });
const navigateToDoc = async (docId, contactId) => { const navigateToDoc = async (docId, contactId) => {
try { try {
const docDetails = await Parse.Cloud.run("getDocument", { const docDetails = await Parse.Cloud.run("getDocument", {
@@ -62,12 +68,16 @@ function GuestLogin() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
//function generate serverUrl and parseAppId from url and save it in local storage //function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = async () => { const handleServerUrl = async () => {
setAppLogo(logo); setAppLogo(logo);
localStorage.clear(); // Clears everything localStorage.clear(); // Clears everything
localStorage.setItem("appname", "OpenSign™"); localStorage.setItem(
"appname",
"OpenSign™"
);
//save isGuestSigner true in local to handle login flow header in mobile view //save isGuestSigner true in local to handle login flow header in mobile view
localStorage.setItem("isGuestSigner", true); localStorage.setItem("isGuestSigner", true);
saveLanguageInLocal(i18n); saveLanguageInLocal(i18n);
@@ -119,7 +129,7 @@ function GuestLogin() {
try { try {
const params = { const params = {
email: email?.toLowerCase()?.replace(/\s/g, "")?.toString(), email: email?.toLowerCase()?.replace(/\s/g, "")?.toString(),
docId: documentId docId: documentId,
}; };
const Otp = await Parse.Cloud.run("SendOTPMailV1", params); const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
if (Otp) { if (Otp) {
@@ -200,6 +210,7 @@ function GuestLogin() {
} }
}; };
const handleUserData = async (e) => { const handleUserData = async (e) => {
e.preventDefault(); e.preventDefault();
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) { if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) {
@@ -7,7 +7,9 @@ import { NavLink, useNavigate, useLocation } from "react-router";
import login_img from "../assets/images/login_img.svg"; import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize"; import { useWindowSize } from "../hook/useWindowSize";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer"; import { fetchAppInfo } from "../redux/reducers/infoReducer";
@@ -22,7 +24,8 @@ import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage"; import SelectLanguage from "../components/pdf/SelectLanguage";
function Login() { function Login() {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -30,17 +33,12 @@ function Login() {
const { width } = useWindowSize(); const { width } = useWindowSize();
const [state, setState] = useState({ const [state, setState] = useState({
email: "", email: "",
password: "",
alertType: "success", alertType: "success",
alertMsg: "", alertMsg: "",
password: "",
passwordVisible: false, passwordVisible: false,
mobile: "",
phone: "",
scanResult: "",
baseUrl: localStorage.getItem("baseUrl"),
parseAppId: localStorage.getItem("parseAppId"),
loading: false, loading: false,
thirdpartyLoader: false thirdpartyLoader: false,
}); });
const [userDetails, setUserDetails] = useState({ const [userDetails, setUserDetails] = useState({
Company: "", Company: "",
@@ -54,6 +52,18 @@ function Login() {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
const setLocalVar = (user) => {
localStorage.setItem("accesstoken", user.sessionToken);
localStorage.setItem("UserInformation", JSON.stringify(user));
localStorage.setItem("userEmail", user.email);
if (user.ProfilePic) {
localStorage.setItem("profileImg", user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
};
const showToast = (type, msg) => { const showToast = (type, msg) => {
setState({ ...state, loading: false, alertType: type, alertMsg: msg }); setState({ ...state, loading: false, alertType: type, alertMsg: msg });
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
@@ -62,7 +72,9 @@ function Login() {
const app = await getAppLogo(); const app = await getAppLogo();
if (app?.error === "invalid_json") { if (app?.error === "invalid_json") {
setErrMsg(t("server-down", { appName: appName })); setErrMsg(t("server-down", { appName: appName }));
} else if (app?.user === "not_exist") { } else if (
app?.user === "not_exist"
) {
navigate("/addadmin"); navigate("/addadmin");
} }
if (app?.logo) { if (app?.logo) {
@@ -109,21 +121,11 @@ function Login() {
// Get extended user data (including 2FA status) using cloud function // Get extended user data (including 2FA status) using cloud function
try { try {
await Parse.User.become(_user.sessionToken); await Parse.User.become(_user.sessionToken);
// Store basic user info needed for further steps setLocalVar(_user);
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("userEmail", email);
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
await continueLoginFlow(); await continueLoginFlow();
} catch (error) { } catch (error) {
console.error("Error checking 2FA status:", error); console.error("Error checking 2FA status:", error);
showToast("danger", t("something-went-wrong-mssg")); showToast("danger", t("something-went-wrong-mssg"));
setState({ ...state, loading: false });
} }
} catch (error) { } catch (error) {
console.error("Error while logging in user", error); console.error("Error while logging in user", error);
@@ -134,6 +136,7 @@ function Login() {
const setThirdpartyLoader = (value) => { const setThirdpartyLoader = (value) => {
setState({ ...state, thirdpartyLoader: value }); setState({ ...state, thirdpartyLoader: value });
}; };
const thirdpartyLoginfn = async (sessionToken) => { const thirdpartyLoginfn = async (sessionToken) => {
const baseUrl = localStorage.getItem("baseUrl"); const baseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
@@ -148,37 +151,25 @@ function Login() {
}); });
if (res.data) { if (res.data) {
let _user = res.data; let _user = res.data;
localStorage.setItem("UserInformation", JSON.stringify(_user)); setLocalVar(_user);
localStorage.setItem("userEmail", _user.email);
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 // Check extended class user role and tenentId
try { try {
const userSettings = appInfo.settings; const userSettings = appInfo.settings;
await Parse.Cloud.run("getUserDetails") const extUser = await Parse.Cloud.run("getUserDetails");
.then(async (extUser) => {
if (extUser) { if (extUser) {
const IsDisabled = extUser?.get("IsDisabled") || false; const IsDisabled = extUser?.get("IsDisabled") || false;
if (!IsDisabled) { if (!IsDisabled) {
const userRole = extUser?.get("UserRole"); const userRole = extUser?.get("UserRole");
const menu = const menu =
userRole && userRole && userSettings.find((menu) => menu.role === userRole);
userSettings.find((menu) => menu.role === userRole);
if (menu) { if (menu) {
const _currentRole = userRole; const _currentRole = userRole;
const redirectUrl = const redirectUrl =
location?.state?.from || `/${menu.pageType}/${menu.pageId}`; location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
const _role = _currentRole.replace("contracts_", ""); const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
const results = [extUser];
const extUser_stringify = JSON.stringify(results);
localStorage.setItem("Extand_Class", extUser_stringify);
const extInfo = JSON.parse(JSON.stringify(extUser)); const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("_user_role", _role);
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
localStorage.setItem("userEmail", extInfo?.Email); localStorage.setItem("userEmail", extInfo?.Email);
localStorage.setItem("username", extInfo?.Name); localStorage.setItem("username", extInfo?.Name);
if (extInfo?.TenantId) { if (extInfo?.TenantId) {
@@ -206,16 +197,11 @@ function Login() {
showToast("danger", t("user-not-found")); showToast("danger", t("user-not-found"));
logOutUser(); logOutUser();
} }
}) } catch (error) {
.catch((err) => {
console.error("err in fetching extUser", err); console.error("err in fetching extUser", err);
showToast("danger", `${err.message}`); showToast("danger", `${err.message}`);
const payload = { sessionToken: sessionToken }; const payload = { sessionToken: _user.sessionToken };
handleSubmitbtn(payload); handleSubmitbtn(payload);
});
} catch (error) {
showToast("danger", `${error.message}`);
console.log(error);
} finally { } finally {
setThirdpartyLoader(false); setThirdpartyLoader(false);
} }
@@ -227,14 +213,7 @@ function Login() {
try { try {
const user = await Parse.User.become(localStorage.getItem("accesstoken")); const user = await Parse.User.become(localStorage.getItem("accesstoken"));
const _user = user.toJSON(); const _user = user.toJSON();
localStorage.setItem("UserInformation", JSON.stringify(_user)); setLocalVar(_user);
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
const userSettings = appInfo.settings; const userSettings = appInfo.settings;
const extUser = await Parse.Cloud.run("getUserDetails"); const extUser = await Parse.Cloud.run("getUserDetails");
if (extUser) { if (extUser) {
@@ -245,14 +224,12 @@ function Login() {
const menu = const menu =
userRole && userSettings.find((menu) => menu.role === userRole); userRole && userSettings.find((menu) => menu.role === userRole);
if (menu) { if (menu) {
const extInfo = JSON.parse(JSON.stringify(extUser));
const _role = _currentRole.replace("contracts_", ""); const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role); localStorage.setItem("_user_role", _role);
const redirectUrl = const redirectUrl =
location?.state?.from || `/${menu.pageType}/${menu.pageId}`; location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
const results = [extUser]; localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
const extendedInfo_stringify = JSON.stringify(results);
localStorage.setItem("Extand_Class", extendedInfo_stringify);
const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", extInfo.Email); localStorage.setItem("userEmail", extInfo.Email);
localStorage.setItem("username", extInfo.Name); localStorage.setItem("username", extInfo.Name);
if (extInfo?.TenantId) { if (extInfo?.TenantId) {
@@ -267,7 +244,6 @@ function Login() {
localStorage.setItem("PageLanding", menu.pageId); localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", menu.menuId); localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType); localStorage.setItem("pageType", menu.pageType);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl); navigate(redirectUrl);
} else { } else {
setState({ ...state, loading: false }); setState({ ...state, loading: false });
@@ -377,18 +353,15 @@ function Login() {
const _currentRole = userRole; const _currentRole = userRole;
const redirectUrl = const redirectUrl =
location?.state?.from || `/${menu.pageType}/${menu.pageId}`; location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
let _role = _currentRole.replace("contracts_", ""); const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role); localStorage.setItem("_user_role", _role);
const checkLanguage = extUser?.get("Language"); const checkLanguage = extUser?.get("Language");
if (checkLanguage) { if (checkLanguage) {
checkLanguage && i18n.changeLanguage(checkLanguage); checkLanguage && i18n.changeLanguage(checkLanguage);
} }
// Continue with storing user data and redirecting
const results = [extUser];
const extUser_str = JSON.stringify(results);
localStorage.setItem("Extand_Class", extUser_str);
const extInfo = JSON.parse(JSON.stringify(extUser)); const extInfo = JSON.parse(JSON.stringify(extUser));
// Continue with storing user data and redirecting
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
localStorage.setItem("userEmail", extInfo.Email); localStorage.setItem("userEmail", extInfo.Email);
localStorage.setItem("username", extInfo.Name); localStorage.setItem("username", extInfo.Name);
if (extInfo?.TenantId) { if (extInfo?.TenantId) {
@@ -403,7 +376,6 @@ function Login() {
localStorage.setItem("PageLanding", menu.pageId); localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", menu.menuId); localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType); localStorage.setItem("pageType", menu.pageType);
setState({ ...state, loading: false }); setState({ ...state, loading: false });
navigate(redirectUrl); navigate(redirectUrl);
} else { } else {
@@ -489,14 +461,18 @@ function Login() {
<div className="relative"> <div className="relative">
<input <input
id="password" id="password"
type={state.passwordVisible ? "text" : "password"} type={
state.passwordVisible ? "text" : "password"
}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="password" name="password"
value={state.password} value={state.password}
autoComplete="current-password" autoComplete="current-password"
onChange={handleChange} onChange={handleChange}
onInvalid={(e) => onInvalid={(e) =>
e.target.setCustomValidity(t("input-required")) e.target.setCustomValidity(
t("input-required")
)
} }
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
required required
@@ -1,7 +1,11 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import "../styles/opensigndrive.css"; import "../styles/opensigndrive.css";
import { iconColor } from "../constant/const"; import {
import { getDrive } from "../constant/Utils"; iconColor,
} from "../constant/const";
import {
getDrive
} from "../constant/Utils";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import Title from "../components/Title"; import Title from "../components/Title";
import Parse from "parse"; import Parse from "parse";
@@ -25,7 +29,8 @@ const AppLoader = () => {
); );
}; };
function Opensigndrive() { function Opensigndrive() {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -669,9 +674,7 @@ function Opensigndrive() {
</button> </button>
<div <div
id="folder-menu" id="folder-menu"
className={`${ className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`}
isOptions ? "dropdown show dropDownStyle" : "dropdown"
} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`}
onClick={() => setIsOptions(!isOptions)} onClick={() => setIsOptions(!isOptions)}
> >
<div data-tut="reactourSecond"> <div data-tut="reactourSecond">
@@ -789,18 +792,14 @@ function Opensigndrive() {
onClick={() => setIsList(!isList)} onClick={() => setIsList(!isList)}
> >
<i <i
className={`${ className={`${isList ? "fa-light fa-th-large" : "fa-light fa-list"} text-[20px]`}
isList ? "fa-light fa-th-large" : "fa-light fa-list"
} text-[20px]`}
style={{ color: `${iconColor}` }} style={{ color: `${iconColor}` }}
aria-hidden="true" aria-hidden="true"
></i> ></i>
</div> </div>
<div <div
id="folder-menu" id="folder-menu"
className={`${ className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} md:hidden`}
isOptions ? "dropdown show dropDownStyle" : "dropdown"
} md:hidden`}
onClick={() => setIsOptions(!isOptions)} onClick={() => setIsOptions(!isOptions)}
> >
<div <div
@@ -42,7 +42,8 @@ import {
textWidget, textWidget,
compressedFileSize, compressedFileSize,
mailTemplate, mailTemplate,
updateDateWidgetsRes updateDateWidgetsRes,
widgetDataValue
} from "../constant/Utils"; } from "../constant/Utils";
import Header from "../components/pdf/PdfHeader"; import Header from "../components/pdf/PdfHeader";
import RenderPdf from "../components/pdf/RenderPdf"; import RenderPdf from "../components/pdf/RenderPdf";
@@ -65,9 +66,11 @@ import WidgetComponent from "../components/pdf/WidgetComponent";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy"; import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import TextFontSetting from "../components/pdf/TextFontSetting"; import TextFontSetting from "../components/pdf/TextFontSetting";
function PdfRequestFiles() { function PdfRequestFiles(
) {
const { t } = useTranslation(); const { t } = useTranslation();
const appName = "OpenSign™"; const appName =
"OpenSign™";
const [pdfDetails, setPdfDetails] = useState([]); const [pdfDetails, setPdfDetails] = useState([]);
const [signedSigners, setSignedSigners] = useState([]); const [signedSigners, setSignedSigners] = useState([]);
const [unsignedSigners, setUnSignedSigners] = useState([]); const [unsignedSigners, setUnSignedSigners] = useState([]);
@@ -171,8 +174,10 @@ function PdfRequestFiles() {
let sendmail; let sendmail;
let getDocId = ""; let getDocId = "";
let contactBookId = ""; let contactBookId = "";
const route = window.location.pathname; const route =
const getQuery = window.location?.search?.split("?"); //['','sendmail=false'] window.location.pathname;
const getQuery =
window.location?.search?.split("?"); //['','sendmail=false']
if (getQuery) { if (getQuery) {
sendmail = getQuery?.[1]?.split("=")[1]; //false sendmail = getQuery?.[1]?.split("=")[1]; //false
} }
@@ -195,7 +200,9 @@ function PdfRequestFiles() {
getDocumentDetails(getDocumentId); getDocumentDetails(getDocumentId);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [getDocumentId]); }, [
getDocumentId
]);
useEffect(() => { useEffect(() => {
const updateSize = () => { const updateSize = () => {
if (divRef.current) { if (divRef.current) {
@@ -252,21 +259,22 @@ function PdfRequestFiles() {
alert(t("user-not-exist")); alert(t("user-not-exist"));
} }
}; };
//function for get document details for perticular signer with signer'object id //function for get document details for perticular signer with signer'object id
const getDocumentDetails = async ( const getDocumentDetails = async (
docId, docId,
isNextUser, isNextUser,
isSuccessPage = false isSuccessPage = false,
publiccontactId
) => { ) => {
try { try {
let isUpdatedSubscribe;
const senderUser = localStorage.getItem( const senderUser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const jsonSender = JSON.parse(senderUser); const jsonSender = JSON.parse(senderUser);
const contactId = jsonSender?.objectId const contactId = jsonSender?.objectId
? "" ? ""
: contactBookId || signerObjectId || ""; : contactBookId || publiccontactId || signerObjectId || "";
const tenantSignTypes = await fetchTenantDetails(contactId); const tenantSignTypes = await fetchTenantDetails(contactId);
// `currUserId` will be contactId or extUserId // `currUserId` will be contactId or extUserId
let currUserId; let currUserId;
@@ -314,7 +322,7 @@ function PdfRequestFiles() {
currUserId = getCurrentSigner?.objectId currUserId = getCurrentSigner?.objectId
? getCurrentSigner.objectId ? getCurrentSigner.objectId
: contactBookId || signerObjectId || ""; //signerObjectId is contactBookId refer from public template flow : contactBookId || publiccontactId || signerObjectId || ""; //signerObjectId is contactBookId refer from public template flow
if (currUserId) { if (currUserId) {
setSignerObjectId(currUserId); setSignerObjectId(currUserId);
} }
@@ -434,9 +442,7 @@ function PdfRequestFiles() {
//in unsignedsigners array //in unsignedsigners array
setUnSignedSigners(placeholdersOrSigners); setUnSignedSigners(placeholdersOrSigners);
} }
setSignerPos( setSignerPos(updateDateWidgetsRes(documentData[0], currUserId));
updateDateWidgetsRes(documentData[0].Placeholders, currUserId)
);
setPdfDetails(documentData); setPdfDetails(documentData);
//checking if condition current user already sign or owner does not exist as a signer or document has been declined by someone or document has been expired //checking if condition current user already sign or owner does not exist as a signer or document has been declined by someone or document has been expired
//then stop to display tour message //then stop to display tour message
@@ -548,7 +554,9 @@ function PdfRequestFiles() {
} }
setIsUiLoading(false); setIsUiLoading(false);
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
return updatedPdfDetails; return {
updatedPdfDetails,
};
} else if ( } else if (
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
@@ -573,7 +581,16 @@ function PdfRequestFiles() {
} }
}; };
//function for embed signature or image url in pdf //function for embed signature or image url in pdf
async function embedWidgetsData() { async function embedWidgetsData(publiccontactId, publicDocId) {
let contactId = publiccontactId || signerObjectId;
let docId = publicDocId || documentId;
const addExtraDays = pdfDetails[0]?.TimeToCompleteDays
? pdfDetails[0].TimeToCompleteDays
: 15;
let updateExpiryDate;
updateExpiryDate = new Date();
updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays);
const expiry = updateExpiryDate || pdfDetails?.[0].ExpiryDate.iso;
//for emailVerified data checking first in localstorage //for emailVerified data checking first in localstorage
const localuser = localStorage.getItem( const localuser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
@@ -759,7 +776,7 @@ function PdfRequestFiles() {
let pdfArrBuffer; let pdfArrBuffer;
//`contractDocument` function used to get updated SignedUrl //`contractDocument` function used to get updated SignedUrl
// to resolve issue of widgets get remove automatically when more than 1 signers try to sign doc at a time // to resolve issue of widgets get remove automatically when more than 1 signers try to sign doc at a time
const documentData = await contractDocument(documentId); const documentData = await contractDocument(docId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
const url = documentData[0]?.SignedUrl || documentData[0]?.URL; const url = documentData[0]?.SignedUrl || documentData[0]?.URL;
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib //convert document url in array buffer format to use embed widgets in pdf using pdf-lib
@@ -791,7 +808,7 @@ function PdfRequestFiles() {
//embed document's object id to all pages in pdf document //embed document's object id to all pages in pdf document
if (!HeaderDocId) { if (!HeaderDocId) {
if (!isDocId) { if (!isDocId) {
await embedDocId(pdfDoc, documentId, allPages); await embedDocId(pdfDoc, docId, allPages);
} }
} }
//embed multi signature in pdf //embed multi signature in pdf
@@ -808,8 +825,8 @@ function PdfRequestFiles() {
//function for call to embed signature in pdf and get digital signature pdf //function for call to embed signature in pdf and get digital signature pdf
const resSign = await signPdfFun( const resSign = await signPdfFun(
pdfBytes, pdfBytes,
documentId, docId,
signerObjectId, contactId,
objectId, objectId,
widgets widgets
); );
@@ -821,10 +838,11 @@ function PdfRequestFiles() {
const isSuccessRoute = pdfDetails?.[0]?.RedirectUrl const isSuccessRoute = pdfDetails?.[0]?.RedirectUrl
? false ? false
: window.location?.pathname?.includes("load"); : window.location?.pathname?.includes("load");
const updateDoc = await getDocumentDetails( const updatedDoc = await getDocumentDetails(
documentId, docId,
true, true,
isSuccessRoute isSuccessRoute,
contactId
); );
const index = pdfDetails?.[0]?.Signers.findIndex( const index = pdfDetails?.[0]?.Signers.findIndex(
(x) => x.objectId === signerObjectId (x) => x.objectId === signerObjectId
@@ -837,23 +855,25 @@ function PdfRequestFiles() {
? usermail ? usermail
: pdfDetails?.[0]?.Signers[newIndex]; : pdfDetails?.[0]?.Signers[newIndex];
if (sendmail !== "false" && sendInOrder) { if (sendmail !== "false" && sendInOrder) {
const requestBody = pdfDetails?.[0]?.RequestBody; const requestBody =
const requestSubject = pdfDetails?.[0]?.RequestSubject; updatedDoc.updatedPdfDetails?.[0]?.RequestBody;
const requestSubject =
updatedDoc.updatedPdfDetails?.[0]?.RequestSubject;
if (user) { if (user) {
const expireDate = pdfDetails?.[0].ExpiryDate.iso; const expireDate = expiry;
const newDate = new Date(expireDate); const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString( const localExpireDate = newDate.toLocaleDateString(
"en-US", "en-US",
{ day: "numeric", month: "long", year: "numeric" } { day: "numeric", month: "long", year: "numeric" }
); );
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; let senderEmail =
pdfDetails?.[0]?.ExtUserPtr?.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = pdfDetails?.[0].ExtUserPtr.Name; const senderName =
pdfDetails?.[0].ExtUserPtr.Name;
const documentName = pdfDetails?.[0].Name; const documentName = pdfDetails?.[0].Name;
try { try {
let url = `${localStorage.getItem( let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
"baseUrl"
)}functions/sendmailv3`;
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": "X-Parse-Application-Id":
@@ -866,19 +886,21 @@ function PdfRequestFiles() {
let encodeBase64; let encodeBase64;
if (objectId) { if (objectId) {
encodeBase64 = btoa( encodeBase64 = btoa(
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` `${docId}/${user.Email}/${objectId}`
); );
} else { } else {
encodeBase64 = btoa( encodeBase64 = btoa(`${docId}/${user.Email}`);
`${pdfDetails?.[0].objectId}/${user.Email}`
);
} }
let signPdf = `${hostUrl}/login/${encodeBase64}`; let signPdf =
`${hostUrl}/login/${encodeBase64}`;
const orgName = pdfDetails[0]?.ExtUserPtr.Company const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company
: ""; : "";
let replaceVar; let replaceVar;
if (requestBody && requestSubject) { if (
requestBody &&
requestSubject
) {
const replacedRequestBody = requestBody.replace( const replacedRequestBody = requestBody.replace(
/"/g, /"/g,
"'" "'"
@@ -923,7 +945,8 @@ function PdfRequestFiles() {
subject: replaceVar?.subject subject: replaceVar?.subject
? replaceVar?.subject ? replaceVar?.subject
: mailTemplate(mailparam).subject, : mailTemplate(mailparam).subject,
from: senderEmail, from:
senderEmail,
html: replaceVar?.body html: replaceVar?.body
? replaceVar?.body ? replaceVar?.body
: mailTemplate(mailparam).body : mailTemplate(mailparam).body
@@ -938,16 +961,15 @@ function PdfRequestFiles() {
setIsredirectCanceled(false); setIsredirectCanceled(false);
} else { } else {
const url = const url =
updateDoc?.[0]?.SignedUrl || updateDoc?.[0]?.URL; updatedDoc.updatedPdfDetails?.[0]?.SignedUrl ||
const fileAdapter = ""; updatedDoc.updatedPdfDetails?.[0]?.URL;
const isCompleted = updateDoc?.[0]?.IsCompleted const fileAdapter =
"";
const isCompleted = updatedDoc.updatedPdfDetails?.[0]
?.IsCompleted
? `&completed=true` ? `&completed=true`
: ""; : "";
const params = `docid=${ const params = `docid=${updatedDoc.updatedPdfDetails[0].objectId}&docurl=${encodeURIComponent(url)}${isCompleted}${fileAdapter}`;
updateDoc[0].objectId
}&docurl=${encodeURIComponent(
url
)}${isCompleted}${fileAdapter}`;
window.location.href = `/success?${params}`; window.location.href = `/success?${params}`;
} }
} else { } else {
@@ -1000,6 +1022,32 @@ function PdfRequestFiles() {
} }
} }
const handleSignPdf = async () => {
if (props.templateId) {
const params = {
...contact,
templateid: pdfDetails[0]?.objectId,
role: pdfDetails[0]?.PublicRole[0]
};
const linkRes = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}/functions/publicuserlinkcontacttodoc`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
const contactId = linkRes.data.result?.contactId;
const docId = linkRes.data?.result?.docId;
await embedWidgetsData(contactId, docId);
} else {
await embedWidgetsData();
}
};
//function for save x and y position and show signature tab on that position //function for save x and y position and show signature tab on that position
const handleTabDrag = (key) => { const handleTabDrag = (key) => {
setDragKey(key); setDragKey(key);
@@ -1528,6 +1576,7 @@ function PdfRequestFiles() {
); );
}; };
const clickOnZoomIn = () => { const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent); onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
}; };
@@ -1650,6 +1699,9 @@ function PdfRequestFiles() {
defaultWidthHeight(dragTypeValue).width * containerScale; defaultWidthHeight(dragTypeValue).width * containerScale;
const widgetHeight = const widgetHeight =
defaultWidthHeight(dragTypeValue).height * containerScale; defaultWidthHeight(dragTypeValue).height * containerScale;
const extUser = localStorage.getItem("Extand_Class");
const parseUser = extUser && JSON.parse(extUser)[0];
const widgetValue = widgetDataValue(dragTypeValue, parseUser);
//adding and updating drop position in array when user drop signature button in div //adding and updating drop position in array when user drop signature button in div
if (item === "onclick") { if (item === "onclick") {
const divHeight = divRef.current.getBoundingClientRect().height; const divHeight = divRef.current.getBoundingClientRect().height;
@@ -1664,7 +1716,7 @@ function PdfRequestFiles() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue, owner, widgetValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1691,7 +1743,7 @@ function PdfRequestFiles() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue, owner, widgetValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1842,7 +1894,11 @@ function PdfRequestFiles() {
}; };
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} /> <Title
title={
"Request Sign"
}
/>
{isLoading.isLoad ? ( {isLoading.isLoad ? (
<LoaderWithMsg isLoading={isLoading} /> <LoaderWithMsg isLoading={isLoading} />
) : handleError ? ( ) : handleError ? (
@@ -1899,7 +1955,9 @@ function PdfRequestFiles() {
: "auto" : "auto"
}} }}
className={`${ className={`${
isGuestSignFlow ? "border-[0.5px] border-gray-300" : "op-card" isGuestSignFlow
? "border-[0.5px] border-gray-300"
: "op-card"
} relative overflow-hidden flex flex-col md:flex-row justify-between bg-base-300`} } relative overflow-hidden flex flex-col md:flex-row justify-between bg-base-300`}
> >
{!requestSignTour && {!requestSignTour &&
@@ -2030,7 +2088,8 @@ function PdfRequestFiles() {
setIsCompleted((prev) => ({ ...prev, isModal: false })) setIsCompleted((prev) => ({ ...prev, isModal: false }))
} }
reduceWidth={ reduceWidth={
!isCompleted?.message && "md:min-w-[440px] md:max-w-[400px]" !isCompleted?.message &&
"md:min-w-[440px] md:max-w-[400px]"
} }
> >
<div className="h-full p-[20px] text-base-content"> <div className="h-full p-[20px] text-base-content">
@@ -2039,7 +2098,9 @@ function PdfRequestFiles() {
<p>{isCompleted?.message}</p> <p>{isCompleted?.message}</p>
{!isredirectCanceled && redirectUrl && ( {!isredirectCanceled && redirectUrl && (
<div className="flex flex-row gap-1 items-center justify-center mb-3 mt-2"> <div className="flex flex-row gap-1 items-center justify-center mb-3 mt-2">
<p>Redirecting you in {redirectTimeLeft} sec...</p> <p>
Redirecting you in {redirectTimeLeft} sec...
</p>
<button <button
onClick={handleRedirectCancel} onClick={handleRedirectCancel}
className="underline cursor-pointer op-text-primary focus:outline-none ml-2" className="underline cursor-pointer op-text-primary focus:outline-none ml-2"
@@ -2058,7 +2119,9 @@ function PdfRequestFiles() {
<div className="flex flex-col mt-3 gap-1 px-[10px] justify-center items-center"> <div className="flex flex-col mt-3 gap-1 px-[10px] justify-center items-center">
{!isredirectCanceled && redirectUrl && ( {!isredirectCanceled && redirectUrl && (
<div className="flex flex-row gap-1 items-center justify-center mb-3"> <div className="flex flex-row gap-1 items-center justify-center mb-3">
<p>Redirecting you in {redirectTimeLeft} sec...</p> <p>
Redirecting you in {redirectTimeLeft} sec...
</p>
<button <button
onClick={handleRedirectCancel} onClick={handleRedirectCancel}
className="underline cursor-pointer op-text-primary focus:outline-none ml-2" className="underline cursor-pointer op-text-primary focus:outline-none ml-2"
@@ -2152,7 +2215,7 @@ function PdfRequestFiles() {
</div> </div>
</ModalUi> </ModalUi>
{/* this component is used for signature pad modal */} {/* this component is used for signature pad modal */}
{documentId && isSignPad && ( {currentSigner && isSignPad && (
<SignPad <SignPad
saveSignCheckbox={saveSignCheckbox} saveSignCheckbox={saveSignCheckbox}
setSaveSignCheckbox={setSaveSignCheckbox} setSaveSignCheckbox={setSaveSignCheckbox}
@@ -2184,7 +2247,9 @@ function PdfRequestFiles() {
)} )}
{/* pdf header which contain funish back button */} {/* pdf header which contain funish back button */}
<Header <Header
isPdfRequestFiles={true} isPdfRequestFiles={
true
}
pageNumber={pageNumber} pageNumber={pageNumber}
allPages={allPages} allPages={allPages}
changePage={changePage} changePage={changePage}
@@ -2192,7 +2257,9 @@ function PdfRequestFiles() {
signerPos={signerPos} signerPos={signerPos}
isSigned={isSigned} isSigned={isSigned}
isCompleted={isCompleted.isCertificate} isCompleted={isCompleted.isCertificate}
embedWidgetsData={embedWidgetsData} embedWidgetsData={
handleSignPdf
}
isShowHeader={true} isShowHeader={true}
setIsDecline={setIsDecline} setIsDecline={setIsDecline}
decline={true} decline={true}
@@ -3,7 +3,9 @@ import axios from "axios";
import Parse from "parse"; import Parse from "parse";
import "../styles/signature.css"; import "../styles/signature.css";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import { maxTitleLength } from "../constant/const"; import {
maxTitleLength
} from "../constant/const";
import { DndProvider } from "react-dnd"; import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrop } from "react-dnd"; import { useDrop } from "react-dnd";
@@ -70,7 +72,8 @@ import AddContact from "../primitives/AddContact";
function PlaceHolderSign() { function PlaceHolderSign() {
const { t } = useTranslation(); const { t } = useTranslation();
const copyUrlRef = useRef(null); const copyUrlRef = useRef(null);
const appName = "OpenSign™"; const appName =
"OpenSign™";
const editorRef = useRef(); const editorRef = useRef();
const { state } = useLocation(); const { state } = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -869,7 +872,11 @@ function PlaceHolderSign() {
scale scale
); );
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, ""); const pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64,
"",
);
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBase64); const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId); SaveFileSize(buffer.length, pdfUrl, tenantId);
@@ -881,7 +888,11 @@ function PlaceHolderSign() {
} else if (pdfBase64Url) { } else if (pdfBase64Url) {
try { try {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, ""); const pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64Url,
"",
);
return pdfUrl; return pdfUrl;
} catch (err) { } catch (err) {
console.log("error to convertBase64ToFile in placeholder flow", err); console.log("error to convertBase64ToFile in placeholder flow", err);
@@ -987,7 +998,11 @@ function PlaceHolderSign() {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, ""); pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64Url,
"",
);
} }
try { try {
const docCls = new Parse.Object("contracts_Document"); const docCls = new Parse.Object("contracts_Document");
@@ -1058,9 +1073,7 @@ function PlaceHolderSign() {
ExpiryDate: { iso: updateExpiryDate, __type: "Date" } ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
}; };
await axios.put( await axios.put(
`${localStorage.getItem( `${localStorage.getItem("baseUrl")}classes/contracts_Document/${documentId}`,
"baseUrl"
)}classes/contracts_Document/${documentId}`,
data, data,
{ {
headers: { headers: {
@@ -1152,7 +1165,8 @@ function PlaceHolderSign() {
year: "numeric" year: "numeric"
}); });
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; let senderEmail =
pdfDetails?.[0]?.ExtUserPtr?.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
let signerMail = signersdata.slice(); let signerMail = signersdata.slice();
@@ -1178,11 +1192,16 @@ function PlaceHolderSign() {
const orgName = pdfDetails[0]?.ExtUserPtr.Company const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company
: ""; : "";
const senderName = pdfDetails?.[0].ExtUserPtr.Name; const senderName =
pdfDetails?.[0].ExtUserPtr.Name;
const documentName = `${pdfDetails?.[0].Name}`; const documentName = `${pdfDetails?.[0].Name}`;
let replaceVar; let replaceVar;
if (requestBody && requestSubject && isCustomize) { if (
requestBody &&
requestSubject &&
isCustomize
) {
const replacedRequestBody = requestBody.replace(/"/g, "'"); const replacedRequestBody = requestBody.replace(/"/g, "'");
htmlReqBody = htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
@@ -1207,7 +1226,10 @@ function PlaceHolderSign() {
htmlReqBody, htmlReqBody,
variables variables
); );
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) { } else if (
tenantMailTemplate?.body &&
tenantMailTemplate?.subject
) {
const mailBody = tenantMailTemplate?.body; const mailBody = tenantMailTemplate?.body;
const mailSubject = tenantMailTemplate?.subject; const mailSubject = tenantMailTemplate?.subject;
const replacedRequestBody = mailBody.replace(/"/g, "'"); const replacedRequestBody = mailBody.replace(/"/g, "'");
@@ -1246,7 +1268,8 @@ function PlaceHolderSign() {
? replaceVar?.subject ? replaceVar?.subject
: mailTemplate(mailparam).subject, : mailTemplate(mailparam).subject,
replyto: senderEmail, replyto: senderEmail,
from: senderEmail, from:
senderEmail,
html: replaceVar?.body html: replaceVar?.body
? replaceVar?.body ? replaceVar?.body
: mailTemplate(mailparam).body : mailTemplate(mailparam).body
@@ -1261,13 +1284,20 @@ function PlaceHolderSign() {
setMailStatus("success"); setMailStatus("success");
try { try {
let data; let data;
if (requestBody && requestSubject && isCustomize) { if (
requestBody &&
requestSubject &&
isCustomize
) {
data = { data = {
RequestBody: htmlReqBody, RequestBody: htmlReqBody,
RequestSubject: requestSubject, RequestSubject: requestSubject,
SendMail: true SendMail: true
}; };
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) { } else if (
tenantMailTemplate?.body &&
tenantMailTemplate?.subject
) {
data = { data = {
RequestBody: tenantMailTemplate?.body, RequestBody: tenantMailTemplate?.body,
RequestSubject: tenantMailTemplate?.subject, RequestSubject: tenantMailTemplate?.subject,
@@ -1305,7 +1335,8 @@ function PlaceHolderSign() {
setIsSend(true); setIsSend(true);
setIsMailSend(true); setIsMailSend(true);
setIsUiLoading(false); setIsUiLoading(false);
} else { }
else {
setMailStatus("failed"); setMailStatus("failed");
setIsSend(true); setIsSend(true);
setIsMailSend(true); setIsMailSend(true);
@@ -1546,7 +1577,8 @@ function PlaceHolderSign() {
status: defaultdata?.status || "required", status: defaultdata?.status || "required",
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: {}, validation:
{},
fontSize: fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12, fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor: fontColor:
@@ -2029,13 +2061,16 @@ function PlaceHolderSign() {
{!isCustomize && ( {!isCustomize && (
<span>{t("placeholder-alert-3")}</span> <span>{t("placeholder-alert-3")}</span>
)} )}
{isCustomize && ( {
isCustomize && (
<> <>
<EmailBody <EmailBody
editorRef={editorRef} editorRef={editorRef}
requestBody={requestBody} requestBody={requestBody}
requestSubject={requestSubject} requestSubject={requestSubject}
handleOnchangeRequest={handleOnchangeRequest} handleOnchangeRequest={
handleOnchangeRequest
}
setRequestSubject={setRequestSubject} setRequestSubject={setRequestSubject}
/> />
<div <div
@@ -2048,7 +2083,8 @@ function PlaceHolderSign() {
<span>{t("reset-to-default")}</span> <span>{t("reset-to-default")}</span>
</div> </div>
</> </>
)} )
}
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2"> <div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<button <button
@@ -2066,14 +2102,16 @@ function PlaceHolderSign() {
</button> </button>
)} )}
</div> </div>
{!isCustomize && ( {
!isCustomize && (
<span <span
className="op-link op-link-accent text-sm" className="op-link op-link-accent text-sm"
onClick={() => setIsCustomize(!isCustomize)} onClick={() => setIsCustomize(!isCustomize)}
> >
{t("cutomize-email")} {t("cutomize-email")}
</span> </span>
)} )
}
</div> </div>
</> </>
)} )}
@@ -23,7 +23,8 @@ import EditorToolbar, {
import DateFormatSelector from "../components/shared/fields/DateFormatSelector"; import DateFormatSelector from "../components/shared/fields/DateFormatSelector";
const Preferences = () => { const Preferences = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const editorRef = useRef(); const editorRef = useRef();
const editorRefCom = useRef(); const editorRefCom = useRef();
@@ -44,7 +45,11 @@ const Preferences = () => {
const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState(); const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState();
const [timezone, setTimezone] = useState(usertimezone); const [timezone, setTimezone] = useState(usertimezone);
const [activeTab, setactiveTab] = useState(0); const [activeTab, setactiveTab] = useState(0);
const generaltab = { title: t("general"), icon: "fa-light fa-gears" }; const generaltab = {
name: "general",
title: t("general"),
icon: "fa-light fa-gears"
};
const [tab, setTab] = useState([generaltab]); const [tab, setTab] = useState([generaltab]);
const [sendinOrder, setSendinOrder] = useState(true); const [sendinOrder, setSendinOrder] = useState(true);
const [isTourEnabled, setIsTourEnabled] = useState(false); const [isTourEnabled, setIsTourEnabled] = useState(false);
@@ -60,9 +65,12 @@ const Preferences = () => {
setIsTopLoader(true); setIsTopLoader(true);
const EmailTab = const EmailTab =
extClass?.[0]?.UserRole === "contracts_Admin" extClass?.[0]?.UserRole === "contracts_Admin"
? [{ title: t("email"), icon: "fa-light fa-envelope" }] ? [{ name: "email", title: t("email"), icon: "fa-light fa-envelope" }]
: []; : [];
const arr = [generaltab, ...EmailTab]; const arr = [
generaltab,
...EmailTab,
];
setTab(arr); setTab(arr);
try { try {
const user = JSON.parse( const user = JSON.parse(
@@ -326,7 +334,7 @@ const Preferences = () => {
const handleTourInput = () => setIsTourEnabled(!isTourEnabled); const handleTourInput = () => setIsTourEnabled(!isTourEnabled);
const handleSendinOrderInput = () => setSendinOrder(!sendinOrder); const handleSendinOrderInput = () => setSendinOrder(!sendinOrder);
const tabName = (ind) => tab.find((t, i) => i === ind)?.name;
return ( return (
<React.Fragment> <React.Fragment>
<Title title={t("Preferences")} /> <Title title={t("Preferences")} />
@@ -364,9 +372,7 @@ const Preferences = () => {
onClick={() => setactiveTab(ind)} onClick={() => setactiveTab(ind)}
key={ind} key={ind}
role="tab" role="tab"
className={`${ className={`${activeTab === ind ? "op-tab-active" : ""} op-tab text-xs md:text-base pb-2 md:pb-0 transition-all `}
activeTab === ind ? "op-tab-active" : ""
} op-tab text-xs md:text-base pb-2 md:pb-0 transition-all `}
aria-selected={activeTab === ind} aria-selected={activeTab === ind}
aria-controls={`panel-${tabData.title}`} aria-controls={`panel-${tabData.title}`}
> >
@@ -382,7 +388,7 @@ const Preferences = () => {
aria-labelledby="tab-general" aria-labelledby="tab-general"
role="tabpanel" role="tabpanel"
> >
{activeTab === 0 ? ( {tabName(activeTab) === "general" && (
<div className="grid grid-cols-1 md:grid-cols-12 md:gap-x-8"> <div className="grid grid-cols-1 md:grid-cols-12 md:gap-x-8">
{/* Left Column - Signature Settings */} {/* Left Column - Signature Settings */}
<div className="md:col-span-5 flex flex-col"> <div className="md:col-span-5 flex flex-col">
@@ -487,7 +493,11 @@ const Preferences = () => {
</ReactTooltip> </ReactTooltip>
</label> </label>
<div className="flex flex-row gap-6"> <div className="flex flex-row gap-6">
<div className={"flex items-center gap-2"}> <div
className={
"flex items-center gap-2"
}
>
<input <input
id="notify-yes" id="notify-yes"
className="op-radio op-radio-xs" className="op-radio op-radio-xs"
@@ -502,7 +512,11 @@ const Preferences = () => {
{t("yes")} {t("yes")}
</label> </label>
</div> </div>
<div className={"flex items-center gap-2"}> <div
className={
"flex items-center gap-2"
}
>
<input <input
id="notify-no" id="notify-no"
className="op-radio op-radio-xs" className="op-radio op-radio-xs"
@@ -706,7 +720,8 @@ const Preferences = () => {
</button> </button>
</div> </div>
</div> </div>
) : activeTab === 1 ? ( )}
{tabName(activeTab) === "email" && (
<div className="flex flex-col mb-4"> <div className="flex flex-col mb-4">
<div className="flex flex-col"> <div className="flex flex-col">
<h1 className="text-[14px] mb-[0.7rem] font-medium"> <h1 className="text-[14px] mb-[0.7rem] font-medium">
@@ -722,9 +737,7 @@ const Preferences = () => {
{t("subject")}{" "} {t("subject")}{" "}
<Tooltip <Tooltip
id={"request-sub-tooltip"} id={"request-sub-tooltip"}
message={`${t( message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
"variables-use"
)}: {{sender_name}} {{document_title}}`}
/> />
</label> </label>
<input <input
@@ -733,9 +746,7 @@ const Preferences = () => {
onChange={(e) => onChange={(e) =>
setRequestSubject(e.target.value) setRequestSubject(e.target.value)
} }
placeholder={`{{sender_name}} ${t( placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
"send-to-sign"
)} {{document_title}}`}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
/> />
</div> </div>
@@ -744,9 +755,7 @@ const Preferences = () => {
{t("body")}{" "} {t("body")}{" "}
<Tooltip <Tooltip
id={"request-body-tooltip"} id={"request-body-tooltip"}
message={`${t( message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
"variables-use"
)}: {{sender_name}} {{document_title}}`}
/> />
</label> </label>
<EditorToolbar containerId="toolbar1" /> <EditorToolbar containerId="toolbar1" />
@@ -792,9 +801,7 @@ const Preferences = () => {
{t("subject")}{" "} {t("subject")}{" "}
<Tooltip <Tooltip
id={"complete-sub-tooltip"} id={"complete-sub-tooltip"}
message={`${t( message={`${t("variables-use")}:{{sender_name}} {{document_title}}`}
"variables-use"
)}:{{sender_name}} {{document_title}}`}
/> />
</label> </label>
<input <input
@@ -803,9 +810,7 @@ const Preferences = () => {
onChange={(e) => onChange={(e) =>
setCompletionSubject(e.target.value) setCompletionSubject(e.target.value)
} }
placeholder={`{{sender_name}} ${t( placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
"send-to-sign"
)} {{document_title}}`}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
/> />
</div> </div>
@@ -814,9 +819,7 @@ const Preferences = () => {
{t("body")}{" "} {t("body")}{" "}
<Tooltip <Tooltip
id={"complete-body-tooltip"} id={"complete-body-tooltip"}
message={`${t( message={`${t("variables-use")}:{{sender_name}} {{document_title}} {{signing_url}}`}
"variables-use"
)}:{{sender_name}} {{document_title}} {{signing_url}}`}
/> />
</label> </label>
<EditorToolbar containerId="toolbar2" /> <EditorToolbar containerId="toolbar2" />
@@ -851,8 +854,10 @@ const Preferences = () => {
</div> </div>
</div> </div>
</div> </div>
) : ( )}
<></> {tabName(activeTab) === "security" && (
<>
</>
)} )}
</div> </div>
</div> </div>
@@ -67,7 +67,8 @@ import LoaderWithMsg from "../primitives/LoaderWithMsg";
function SignYourSelf() { function SignYourSelf() {
const { t } = useTranslation(); const { t } = useTranslation();
const { docId } = useParams(); const { docId } = useParams();
const appName = "OpenSign™"; const appName =
"OpenSign™";
const divRef = useRef(null); const divRef = useRef(null);
const nodeRef = useRef(null); const nodeRef = useRef(null);
const imageRef = useRef(null); const imageRef = useRef(null);
@@ -520,7 +521,11 @@ function SignYourSelf() {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, ""); pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64Url,
"",
);
} }
const widgetsType = ["signature", "stamp", "image", "initials"]; const widgetsType = ["signature", "stamp", "image", "initials"];
let updatedXYPosition; let updatedXYPosition;
@@ -706,7 +711,10 @@ function SignYourSelf() {
if (tenantDetails && tenantDetails === "user does not exist!") { if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist")); alert(t("user-not-exist"));
} else { } else {
if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) { if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject
) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
} }
@@ -742,7 +750,7 @@ function SignYourSelf() {
pdfFile: base64Url, pdfFile: base64Url,
docId: documentId, docId: documentId,
isCustomCompletionMail: isCustomCompletionMail, isCustomCompletionMail: isCustomCompletionMail,
signature: suffixbase64 signature: suffixbase64,
}; };
const resSignPdf = await Parse.Cloud.run("signPdf", params); const resSignPdf = await Parse.Cloud.run("signPdf", params);
if (resSignPdf) { if (resSignPdf) {
@@ -166,6 +166,7 @@ const TemplatePlaceholder = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]); }, [divRef.current, isHeader]);
//function to fetch tenant Details //function to fetch tenant Details
const fetchTenantDetails = async () => { const fetchTenantDetails = async () => {
const user = JSON.parse( const user = JSON.parse(
@@ -777,7 +778,11 @@ const TemplatePlaceholder = () => {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, ""); pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64Url,
"",
);
} }
if (signersdata?.length > 0) { if (signersdata?.length > 0) {
signersdata.forEach((x) => { signersdata.forEach((x) => {
@@ -844,7 +849,11 @@ const TemplatePlaceholder = () => {
scale scale
); );
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, ""); const pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64,
"",
);
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBase64); const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId); SaveFileSize(buffer.length, pdfUrl, tenantId);
@@ -856,7 +865,11 @@ const TemplatePlaceholder = () => {
} else if (pdfBase64Url) { } else if (pdfBase64Url) {
try { try {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, ""); const pdfUrl = await convertBase64ToFile(
pdfName,
pdfBase64Url,
"",
);
return pdfUrl; return pdfUrl;
} catch (err) { } catch (err) {
console.log("error to convertBase64ToFile in placeholder flow", err); console.log("error to convertBase64ToFile in placeholder flow", err);
@@ -1280,7 +1293,6 @@ const TemplatePlaceholder = () => {
const handleCloseRoleModal = () => { const handleCloseRoleModal = () => {
setIsModalRole(false); setIsModalRole(false);
}; };
const handleSaveWidgetsOptions = ( const handleSaveWidgetsOptions = (
dropdownName, dropdownName,
dropdownOptions, dropdownOptions,
@@ -1457,7 +1469,9 @@ const TemplatePlaceholder = () => {
status: defaultdata?.status || "required", status: defaultdata?.status || "required",
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: {}, isReadOnly: defaultdata?.isReadOnly || false,
validation:
{},
fontSize: fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12, fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor: fontColor:
@@ -7,7 +7,8 @@ import Title from "../components/Title";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import { emailRegex } from "../constant/const";
const UpdateExistUserAdmin = () => { const UpdateExistUserAdmin = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const [formdata, setFormdata] = useState({ email: "", masterkey: "" }); const [formdata, setFormdata] = useState({ email: "", masterkey: "" });
-394
View File
@@ -1,394 +0,0 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { useLocation } from "react-router";
import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
import Tooltip from "../primitives/Tooltip";
import AddUser from "../components/AddUser";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
const UserList = () => {
const { t } = useTranslation();
const [userList, setUserList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isModal, setIsModal] = useState({
form: false,
addseats: false,
options: false
});
const location = useLocation();
const isDashboard =
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
const [currentPage, setCurrentPage] = useState(1);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActiveModal, setIsActiveModal] = useState({});
const [isActLoader, setIsActLoader] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
const [formHeader, setFormHeader] = useState(t("add-user"));
const recordperPage = 10;
const startIndex = (currentPage - 1) * recordperPage; // user per page
const getPaginationRange = () => {
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
const pages = [];
const totalPages = Math.ceil(userList.length / recordperPage);
if (totalPages <= totalPageNumbers) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
const leftSiblingIndex = Math.max(currentPage - 1, 1);
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
const showLeftDots = leftSiblingIndex > 2;
const showRightDots = rightSiblingIndex < totalPages - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPages;
if (!showLeftDots && showRightDots) {
let leftItemCount = 3;
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
pages.push(...leftRange);
pages.push("...");
pages.push(totalPages);
} else if (showLeftDots && !showRightDots) {
let rightItemCount = 3;
let rightRange = Array.from(
{ length: rightItemCount },
(_, i) => totalPages - rightItemCount + i + 1
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...rightRange);
} else if (showLeftDots && showRightDots) {
let middleRange = Array.from(
{ length: 3 },
(_, i) => leftSiblingIndex + i
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...middleRange);
pages.push("...");
pages.push(lastPageIndex);
}
}
return pages;
};
const pageNumbers = getPaginationRange();
// to slice out 10 objects from array for current page
const indexOfLastDoc = currentPage * recordperPage;
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
const currentList = userList?.slice(indexOfFirstDoc, indexOfLastDoc);
useEffect(() => {
fetchUserList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function fetchUserList() {
try {
setIsLoader(true);
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
const admin =
extUser?.UserRole &&
(extUser?.UserRole === "contracts_Admin" ||
extUser?.UserRole === "contracts_OrgAdmin")
? true
: false;
setIsAdmin(admin);
}
const res = await Parse.Cloud.run("getuserlistbyorg", {
organizationId: extUser.OrganizationId.objectId
});
const _userRes = JSON.parse(JSON.stringify(res));
setUserList(_userRes);
} catch (err) {
console.log("Err in fetch userlist", err);
showAlert("danger", t("something-went-wrong-mssg"));
} finally {
setIsLoader(false);
}
}
const handleModal = (modalName) => {
setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] }));
};
// Change page
const paginateFront = () => {
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
if (currentPage < lastValue) {
setCurrentPage(currentPage + 1);
}
};
const paginateBack = () => {
if (startIndex > 0) {
setCurrentPage(currentPage - 1);
}
};
const handleUserData = (userData) => {
if (userData) {
setUserList((prev) => [userData, ...prev]);
}
};
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
// if no data available it will show hyphen "-"
const formatRow = (row) => {
if (Array.isArray(row)) {
let updateArr = row.map((x) => x.Name);
return updateArr.join(", ");
} else if (typeof row === "object" && row !== null) {
return row?.Name || "-";
} else {
return "-";
}
};
const handleClose = () => setIsActiveModal({});
const handleToggleSubmit = async (user) => {
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
if (index !== -1) {
setIsActiveModal({});
setIsActLoader({ [user.objectId]: true });
const newArray = [...userList];
const IsDisabled = newArray[index]?.IsDisabled;
newArray[index] = { ...newArray[index], IsDisabled: !IsDisabled };
setUserList(newArray);
try {
const extUser = new Parse.Object("contracts_Users");
extUser.id = user.objectId;
extUser.set("IsDisabled", !IsDisabled);
await extUser.save();
showAlert(
!IsDisabled === true ? "danger" : "success",
!IsDisabled === true ? t("user-deactivated") : t("user-activated")
);
} catch (err) {
showAlert("danger", t("something-went-wrong-mssg"));
console.log("err in disable team", err);
} finally {
setIsActLoader({});
}
}
};
const handleToggleBtn = (user) => {
setIsActiveModal({ [user.objectId]: true });
};
// `showAlert` handle show/hide alert
const showAlert = (type, msg) => {
setIsAlert({ type, msg });
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
};
return (
<div className="relative">
<Title title={isAdmin ? "Users" : "Page not found"} />
{isLoader && (
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
{Object.keys(isActLoader)?.length > 0 && (
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box">
<Loader />
</div>
)}
{!isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{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")}{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={t("users-from-teams")} />
</span>
</div>
<div className="flex flex-row gap-2 items-center">
<div
className="cursor-pointer"
onClick={() => handleModal("form")}
>
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
</div>
</div>
</div>
<div className="w-full overflow-x-auto">
<table className="op-table border-collapse w-full mb-[50px]">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<th key={index} className="px-4 py-2">
{t(`report-heading.${item}`)}
</th>
))}
</tr>
</thead>
{userList?.length > 0 && (
<tbody className="text-[12px]">
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
<td className="px-4 py-2 font-semibold">
{item?.Name}{" "}
</td>
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
<td className="px-4 py-2">{item?.Phone || "-"}</td>
<td className="px-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<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={t("user-status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
{t("are-you-sure")}{" "}
{item?.IsDisabled
? t("activate")
: t("deactivate")}{" "}
{t("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"
>
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
{t("no")}
</button>
</div>
</div>
</ModalUi>
)}
</td>
) : (
<td className="px-4 py-2 font-semibold"></td>
)}
</tr>
))}
</tbody>
)}
</table>
</div>
<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>
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
isOpen={isModal.form}
title={formHeader}
handleClose={() => handleModal("form")}
>
<AddUser
showAlert={showAlert}
handleUserData={handleUserData}
closePopup={() => handleModal("form")}
setFormHeader={setFormHeader}
/>
</ModalUi>
</div>
) : (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold">
404
</h1>
<p className="text-[30px] lg:text-[50px]">
{t("page-not-found")}
</p>
</div>
</div>
)}
</>
)}
</div>
);
};
export default UserList;
+406
View File
@@ -0,0 +1,406 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { useLocation } from "react-router";
import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
import Tooltip from "../primitives/Tooltip";
import AddUser from "../components/AddUser";
import Title from "../components/Title";
import {
useTranslation
} from "react-i18next";
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
const UserList = () => {
const { t } = useTranslation();
const [userList, setUserList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isModal, setIsModal] = useState({
form: false,
addseats: false,
options: false
});
const location = useLocation();
const isDashboard =
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
const [currentPage, setCurrentPage] = useState(1);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActiveModal, setIsActiveModal] = useState({});
const [isActLoader, setIsActLoader] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
const [formHeader, setFormHeader] = useState(t("add-user"));
const recordperPage = 10;
const startIndex = (currentPage - 1) * recordperPage; // user per page
const getPaginationRange = () => {
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
const pages = [];
const totalPages = Math.ceil(userList.length / recordperPage);
if (totalPages <= totalPageNumbers) {
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
const leftSiblingIndex = Math.max(currentPage - 1, 1);
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
const showLeftDots = leftSiblingIndex > 2;
const showRightDots = rightSiblingIndex < totalPages - 2;
const firstPageIndex = 1;
const lastPageIndex = totalPages;
if (!showLeftDots && showRightDots) {
let leftItemCount = 3;
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
pages.push(...leftRange);
pages.push("...");
pages.push(totalPages);
} else if (showLeftDots && !showRightDots) {
let rightItemCount = 3;
let rightRange = Array.from(
{ length: rightItemCount },
(_, i) => totalPages - rightItemCount + i + 1
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...rightRange);
} else if (showLeftDots && showRightDots) {
let middleRange = Array.from(
{ length: 3 },
(_, i) => leftSiblingIndex + i
);
pages.push(firstPageIndex);
pages.push("...");
pages.push(...middleRange);
pages.push("...");
pages.push(lastPageIndex);
}
}
return pages;
};
const pageNumbers = getPaginationRange();
// to slice out 10 objects from array for current page
const indexOfLastDoc = currentPage * recordperPage;
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
const currentList = userList?.slice(indexOfFirstDoc, indexOfLastDoc);
useEffect(() => {
fetchUserList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function fetchUserList() {
try {
setIsLoader(true);
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
const admin =
extUser?.UserRole &&
(extUser?.UserRole === "contracts_Admin" ||
extUser?.UserRole === "contracts_OrgAdmin")
? true
: false;
setIsAdmin(admin);
}
const res = await Parse.Cloud.run("getuserlistbyorg", {
organizationId: extUser.OrganizationId.objectId
});
const _userRes = JSON.parse(JSON.stringify(res));
setUserList(_userRes);
} catch (err) {
console.log("Err in fetch userlist", err);
showAlert("danger", t("something-went-wrong-mssg"));
} finally {
setIsLoader(false);
}
}
const handleModal = (modalName) => {
setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] }));
};
// Change page
const paginateFront = () => {
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
if (currentPage < lastValue) {
setCurrentPage(currentPage + 1);
}
};
const paginateBack = () => {
if (startIndex > 0) {
setCurrentPage(currentPage - 1);
}
};
const handleUserData = (userData) => {
if (userData) {
setUserList((prev) => [userData, ...prev]);
}
};
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
// if no data available it will show hyphen "-"
const formatRow = (row) => {
if (Array.isArray(row)) {
let updateArr = row.map((x) => x.Name);
return updateArr.join(", ");
} else if (typeof row === "object" && row !== null) {
return row?.Name || "-";
} else {
return "-";
}
};
const handleClose = () => setIsActiveModal({});
const handleToggleSubmit = async (user) => {
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
if (index !== -1) {
setIsActiveModal({});
setIsActLoader({ [user.objectId]: true });
const newArray = [...userList];
const IsDisabled = newArray[index]?.IsDisabled;
newArray[index] = { ...newArray[index], IsDisabled: !IsDisabled };
setUserList(newArray);
try {
const extUser = new Parse.Object("contracts_Users");
extUser.id = user.objectId;
extUser.set("IsDisabled", !IsDisabled);
await extUser.save();
showAlert(
!IsDisabled === true ? "danger" : "success",
!IsDisabled === true ? t("user-deactivated") : t("user-activated")
);
} catch (err) {
showAlert("danger", t("something-went-wrong-mssg"));
console.log("err in disable team", err);
} finally {
setIsActLoader({});
}
}
};
const handleToggleBtn = (user) => {
setIsActiveModal({ [user.objectId]: true });
};
// `showAlert` handle show/hide alert
const showAlert = (type, msg) => {
setIsAlert({ type, msg });
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
};
return (
<div className="relative">
<Title title={isAdmin ? "Users" : "Page not found"} />
{isLoader && (
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
<Loader />
</div>
)}
{Object.keys(isActLoader)?.length > 0 && (
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box">
<Loader />
</div>
)}
{
!isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{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")}{" "}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={t("users-from-teams")} />
</span>
</div>
<div className="flex flex-row gap-2 items-center">
<div
className="cursor-pointer"
onClick={() => handleModal("form")}
>
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
</div>
</div>
</div>
<div className="w-full overflow-x-auto">
<table className="op-table border-collapse w-full mb-[50px]">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<th key={index} className="px-4 py-2">
{t(`report-heading.${item}`)}
</th>
))}
</tr>
</thead>
{userList?.length > 0 && (
<tbody className="text-[12px]">
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
<td className="px-4 py-2 font-semibold">
{item?.Name}{" "}
</td>
<td className="px-4 py-2 ">
{item?.Email || "-"}
</td>
<td className="px-4 py-2">
{item?.Phone || "-"}
</td>
<td className="px-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<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={t("user-status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
{t("are-you-sure")}{" "}
{item?.IsDisabled
? t("activate")
: t("deactivate")}{" "}
{t("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"
>
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
{t("no")}
</button>
</div>
</div>
</ModalUi>
)}
</td>
) : (
<td className="px-4 py-2 font-semibold"></td>
)}
</tr>
))}
</tbody>
)}
</table>
</div>
<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>
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
isOpen={isModal.form}
title={formHeader}
handleClose={() => handleModal("form")}
>
<AddUser
showAlert={showAlert}
handleUserData={handleUserData}
closePopup={() => handleModal("form")}
setFormHeader={setFormHeader}
/>
</ModalUi>
</div>
) : (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold">
404
</h1>
<p className="text-[30px] lg:text-[50px]">
{t("page-not-found")}
</p>
</div>
</div>
)}
</>
)
}
</div>
);
};
export default UserList;
@@ -1,4 +1,7 @@
import React, { useState, useEffect } from "react"; import React, {
useState,
useEffect,
} from "react";
import { Navigate, useNavigate } from "react-router"; import { Navigate, useNavigate } from "react-router";
import Parse from "parse"; import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
@@ -7,7 +10,10 @@ import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName"; import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios"; import axios from "axios";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
import { getSecureUrl, handleSendOTP } from "../constant/Utils"; import {
getSecureUrl,
handleSendOTP,
} from "../constant/Utils";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -121,7 +127,7 @@ function UserProfile() {
Name: obj.Name, Name: obj.Name,
JobTitle: jobTitle, JobTitle: jobTitle,
Company: company, Company: company,
Language: obj?.language || "" Language: obj?.language || "",
}; };
await axios.put( await axios.put(
@@ -33,9 +33,8 @@ const AddContact = (props) => {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/isuserincontactbook`; const url = `${baseURL}functions/isuserincontactbook`;
const token = { const token =
"X-Parse-Session-Token": localStorage.getItem("accesstoken") { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
};
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
@@ -64,13 +63,16 @@ const AddContact = (props) => {
) )
); );
const userId = user?.objectId || ""; const userId = user?.objectId || "";
const tenantDetails = await getTenantDetails(userId); const tenantDetails = await getTenantDetails(
userId,
);
const tenantId = tenantDetails?.objectId || ""; const tenantId = tenantDetails?.objectId || "";
if (tenantId) { if (tenantId) {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/savecontact`; const url = `${baseURL}functions/savecontact`;
const token = { const token =
{
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
}; };
const data = { name, email, phone, tenantId }; const data = { name, email, phone, tenantId };
@@ -5,7 +5,9 @@ import { Link, useLocation, useNavigate } from "react-router";
import axios from "axios"; import axios from "axios";
import ModalUi from "./ModalUi"; import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner"; import AddSigner from "../components/AddSigner";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
import Alert from "./Alert"; import Alert from "./Alert";
import Tooltip from "./Tooltip"; import Tooltip from "./Tooltip";
import { RWebShare } from "react-web-share"; import { RWebShare } from "react-web-share";
@@ -39,7 +41,8 @@ import EditContactForm from "../components/EditContactForm";
const ReportTable = (props) => { const ReportTable = (props) => {
const copyUrlRef = useRef(null); const copyUrlRef = useRef(null);
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -405,7 +408,8 @@ const ReportTable = (props) => {
setSelectedTeam(formatedList); setSelectedTeam(formatedList);
} }
setIsShareWith({ [item.objectId]: true }); setIsShareWith({ [item.objectId]: true });
} else if (act.action === "duplicate") { }
else if (act.action === "duplicate") {
setIsModal({ [`duplicate_${item.objectId}`]: true }); setIsModal({ [`duplicate_${item.objectId}`]: true });
} else if (act.action === "rename") { } else if (act.action === "rename") {
setIsModal({ [`rename_${item.objectId}`]: true }); setIsModal({ [`rename_${item.objectId}`]: true });
@@ -483,7 +487,8 @@ const ReportTable = (props) => {
setActLoader({}); setActLoader({});
} }
}; };
const handleClose = () => { const handleClose = (
) => {
setIsRevoke({}); setIsRevoke({});
setIsDeleteModal({}); setIsDeleteModal({});
setReason(""); setReason("");
@@ -632,7 +637,11 @@ const ReportTable = (props) => {
if (isCompleted) { if (isCompleted) {
setIsDownloadModal({ [item.objectId]: true }); setIsDownloadModal({ [item.objectId]: true });
} else { } else {
const signedUrl = await getSignedUrl(url, docId, templateId); const signedUrl = await getSignedUrl(
url,
docId,
templateId
);
await fetchUrl(signedUrl, pdfName); await fetchUrl(signedUrl, pdfName);
} }
setActLoader({}); setActLoader({});
@@ -660,8 +669,10 @@ const ReportTable = (props) => {
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
note: doc?.Note || "", note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name:
sender_mail: doc.ExtUserPtr.Email, doc.ExtUserPtr.Name,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails?.Name || "", receiver_name: userDetails?.Name || "",
receiver_email: userDetails?.Email, receiver_email: userDetails?.Email,
@@ -689,8 +700,10 @@ const ReportTable = (props) => {
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
note: doc?.Note || "", note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name:
sender_mail: doc.ExtUserPtr.Email, doc.ExtUserPtr.Name,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails?.Name || "", receiver_name: userDetails?.Name || "",
receiver_email: userDetails?.Email || "", receiver_email: userDetails?.Email || "",
@@ -731,8 +744,10 @@ const ReportTable = (props) => {
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
note: doc?.Note || "", note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name:
sender_mail: doc.ExtUserPtr.Email, doc.ExtUserPtr.Name,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: user?.signerPtr?.Name || "", receiver_name: user?.signerPtr?.Name || "",
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email, receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
@@ -763,11 +778,14 @@ const ReportTable = (props) => {
sessionToken: localStorage.getItem("accesstoken") sessionToken: localStorage.getItem("accesstoken")
}; };
let params = { let params = {
replyto: doc?.ExtUserPtr?.Email || "", replyto:
doc?.ExtUserPtr?.Email ||
"",
extUserId: doc?.ExtUserPtr?.objectId, extUserId: doc?.ExtUserPtr?.objectId,
recipient: userDetails?.Email, recipient: userDetails?.Email,
subject: mail.subject, subject: mail.subject,
from: doc?.ExtUserPtr?.Email, from:
doc?.ExtUserPtr?.Email,
html: mail.body html: mail.body
}; };
try { try {
@@ -775,7 +793,8 @@ const ReportTable = (props) => {
if (res?.data?.result?.status === "success") { if (res?.data?.result?.status === "success") {
showAlert("success", t("mail-sent-alert")); showAlert("success", t("mail-sent-alert"));
setIsResendMail({}); setIsResendMail({});
} else { }
else {
showAlert("danger", t("something-went-wrong-mssg")); showAlert("danger", t("something-went-wrong-mssg"));
} }
} catch (err) { } catch (err) {
@@ -1718,7 +1737,9 @@ const ReportTable = (props) => {
<i className={act.btnIcon}></i> <i className={act.btnIcon}></i>
{act.btnLabel && ( {act.btnLabel && (
<span className="uppercase font-medium"> <span className="uppercase font-medium">
{`${t(`btnLabel.${act.btnLabel}`)}`} {
`${t(`btnLabel.${act.btnLabel}`)}`
}
</span> </span>
)} )}
{/* template report */} {/* template report */}
@@ -1802,9 +1823,7 @@ const ReportTable = (props) => {
title={t(`btnLabel.${act.hoverLabel}`)} title={t(`btnLabel.${act.hoverLabel}`)}
className={ className={
act.action !== "option" act.action !== "option"
? `${ ? `${act?.btnColor || ""} op-btn op-btn-sm mr-1`
act?.btnColor || ""
} op-btn op-btn-sm mr-1`
: "text-base-content focus:outline-none text-lg mr-2 relative" : "text-base-content focus:outline-none text-lg mr-2 relative"
} }
> >
@@ -1930,7 +1949,9 @@ const ReportTable = (props) => {
} }
> >
<i className="fa-light fa-plus"></i>{" "} <i className="fa-light fa-plus"></i>{" "}
{`${t(`btnLabel.Quick send`)}`} {
`${t(`btnLabel.Quick send`)}`
}
</button> </button>
<button <button
className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative" className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative"
@@ -2026,7 +2047,9 @@ const ReportTable = (props) => {
</div> </div>
</div> </div>
<button <button
onClick={(e) => handleShareWith(e, item)} onClick={(e) =>
handleShareWith(e, item)
}
className="op-btn op-btn-primary ml-[10px] my-3" className="op-btn op-btn-primary ml-[10px] my-3"
> >
{t("submit")} {t("submit")}
@@ -2121,7 +2144,9 @@ const ReportTable = (props) => {
{isBulkSend[item.objectId] && ( {isBulkSend[item.objectId] && (
<ModalUi <ModalUi
isOpen isOpen
title={t("quick-send")} title={
t("quick-send")
}
handleClose={() => setIsBulkSend({})} handleClose={() => setIsBulkSend({})}
> >
{isLoader[item.objectId] ? ( {isLoader[item.objectId] ? (
@@ -2228,7 +2253,9 @@ const ReportTable = (props) => {
{isResendMail[item.objectId] && ( {isResendMail[item.objectId] && (
<ModalUi <ModalUi
isOpen isOpen
title={t("resend-mail")} title={
t("resend-mail")
}
handleClose={handleResendClose} handleClose={handleResendClose}
> >
<div className="overflow-y-auto max-h-[340px] md:max-h-[400px]"> <div className="overflow-y-auto max-h-[340px] md:max-h-[400px]">

Some files were not shown because too many files have changed in this diff Show More