Compare commits

..
Author SHA1 Message Date
allcontributors[bot]andGitHub f0f27bc1c5 docs: update .all-contributorsrc 2024-05-24 13:51:16 +00:00
allcontributors[bot]andGitHub 77e79183f8 docs: update README.md 2024-05-24 13:51:15 +00:00
202 changed files with 15644 additions and 46627 deletions
+2 -10
View File
@@ -215,22 +215,14 @@
"code"
]
},
{
"login": "daniel-mutwiri",
"name": "Daniel Mutwiri",
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
"profile": "https://github.com/daniel-mutwiri",
"contributions": [
"code"
]
},
{
"login": "Zathiel",
"name": "Zathiel",
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
"profile": "https://github.com/Zathiel",
"contributions": [
"code"
"code",
"security"
]
}
]
+12
View File
@@ -0,0 +1,12 @@
# Clone the repo and rename this file to .env if you want to run the frontend react app locally for some quick contributions to the project.
# When used as it is, this env config will connect to our hosted UAT backend
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
# Set it to the URL form where the app home page will be accessed
PUBLIC_URL=http://localhost:3000
# Set it to true if you want to generate the Sourcemap for debugging
GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
REACT_APP_APPID=opensign
+1 -1
View File
@@ -40,7 +40,7 @@ DO_SECRET_ACCESS_KEY=
# Digital ocean spaces region or AWS s3 region
DO_REGION=us-west
# local storage
USE_LOCAL=true
USE_LOCAL=TRUE
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
MAILGUN_API_KEY=
+1 -1
View File
@@ -2,7 +2,7 @@ name: ci
on:
push:
branches:
- 'main'
- 'staging'
jobs:
docker:
+1 -2
View File
@@ -135,8 +135,7 @@ We would like to thank all our contributors and users for their support and feed
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a><a href="#security-Zathiel" title="Security">🛡️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a> <a href="#security-Zathiel" title="Security">🛡️</a></td>
</tr>
</tbody>
</table>
+3926 -4483
View File
File diff suppressed because it is too large Load Diff
+20 -28
View File
@@ -4,24 +4,21 @@
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.6.4",
"@radix-ui/themes": "^3.0.5",
"@radix-ui/themes": "^2.0.3",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.5",
"axios": "^1.7.2",
"@reduxjs/toolkit": "^2.2.1",
"axios": "^1.6.8",
"file-saver": "^2.0.5",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
"nth-check": "^2.1.1",
"parse": "^5.1.0",
"parse": "^4.3.1",
"pdf-lib": "^1.17.1",
"print-js": "^1.6.0",
"radix-ui": "^1.0.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
"react-confetti": "^6.1.0",
"react-cookie": "^7.1.4",
"react-datepicker": "^6.9.0",
"react-datepicker": "^6.4.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
@@ -30,25 +27,25 @@
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-konva": "^18.2.10",
"react-pdf": "^9.0.0",
"react-modal": "^3.16.1",
"react-pdf": "^7.7.1",
"react-quill": "^2.0.0",
"react-redux": "^9.1.2",
"react-rnd": "^10.4.11",
"react-redux": "^9.1.0",
"react-rnd": "^10.4.10",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.4",
"react-tooltip": "^5.26.3",
"react-web-share": "^2.0.2",
"reactour": "^1.19.4",
"reactour": "^1.19.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"serve": "^14.2.3",
"serve": "^14.2.1",
"styled-components": "^4.4.1",
"web-vitals": "^4.0.1",
"ws": "^8.17.1"
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "serve -s build",
@@ -86,21 +83,16 @@
}
},
"devDependencies": {
"@babel/runtime-corejs2": "^7.24.7",
"@babel/runtime-corejs2": "^7.24.5",
"autoprefixer": "^10.4.19",
"commitizen": "^4.3.0",
"daisyui": "^4.12.2",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.3",
"lint-staged": "^15.2.7",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.34.1",
"lint-staged": "^15.2.2",
"postcss": "^8.4.38",
"prettier": "^3.3.2",
"pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.4"
},
"overrides": {
"nth-check": "$nth-check",
"ws":"$ws"
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"tailwindcss": "^3.4.1"
}
}
File diff suppressed because it is too large Load Diff
+38 -5
View File
@@ -1,20 +1,53 @@
<!DOCTYPE html>
<html lang="en" data-theme="opensigncss">
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<!-- <link rel="icon" type="image/png"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
sizes="40x40" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<link rel="stylesheet" href="/css/fonts.css" />
<title>OpenSign™</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
</html>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -9
View File
@@ -16,8 +16,6 @@ import PlaceHolderSign from "./pages/PlaceHolderSign";
import PdfRequestFiles from "./pages/PdfRequestFiles";
import LazyPage from "./primitives/LazyPage";
import { isEnableSubscription } from "./constant/const";
import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader";
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
@@ -30,12 +28,25 @@ const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
const ManageSign = lazy(() => import("./pages/Managesign"));
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook"));
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => {
const Loader = () => {
return (
<div className="flex justify-center items-center h-[100vh]">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
};
@@ -64,9 +75,9 @@ function App() {
};
return (
<div className="bg-base-200">
<div className="bg-[#eef1f5]">
{isloading ? (
<AppLoader />
<Loader />
) : (
<BrowserRouter>
<Routes>
@@ -170,7 +181,6 @@ function App() {
element={<PdfRequestFiles />}
/>
</Route>
<Route path="/sso" element={<SSOVerify />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</BrowserRouter>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

+172 -167
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import Loader from "../primitives/Loader";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
const AddSigner = (props) => {
const [name, setName] = useState("");
@@ -23,7 +23,7 @@ const AddSigner = (props) => {
);
if (savedUserDetails && addYourself) {
setName(savedUserDetails.name);
setPhone(savedUserDetails?.phone || "");
setPhone(savedUserDetails.phone);
setEmail(savedUserDetails.email);
}
}, [addYourself]);
@@ -49,162 +49,144 @@ const AddSigner = (props) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
try {
const user = Parse.User.current();
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", email);
const res = await query.first();
// console.log(res);
if (!res) {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
if (props.closePopup) {
props.closePopup();
}
} else {
alert("Contact already exist!");
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err in fetch contact", err);
setIsLoader(false);
alert("something went wrong, please try again later");
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
}
} else {
} catch (err) {
// console.log("err", err);
setIsLoader(false);
alert("something went wrong, please try again later");
alert("something went wrong!");
}
};
@@ -227,29 +209,38 @@ const AddSigner = (props) => {
<div className="h-full p-[20px]">
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<div className="w-full mx-auto p-2 text-base-content">
<div className="w-full mx-auto p-2">
{isUserExist && (
<div className="mb-3 flex items-center">
<div className="mb-3">
<input
type="checkbox"
id="addYourself"
checked={addYourself}
onChange={handleAddYourselfChange}
className="op-checkbox op-checkbox-sm"
className="form-checkbox"
/>
<label htmlFor="addYourself" className="ml-2 mb-0">
<label htmlFor="addYourself" className="ml-2 text-gray-700">
Add Yourself
</label>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="name" className="block text-xs font-semibold">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
@@ -258,45 +249,59 @@ const AddSigner = (props) => {
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="email" className="block text-xs font-semibold">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
onChange={(e) => setEmail(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="phone" className="block text-xs font-semibold">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mt-4 flex gap-x-2 justify-start">
<button type="submit" className="op-btn op-btn-primary">
<div className="mt-4 flex justify-start">
<button
type="submit"
style={{ backgroundColor: modalSubmitBtnColor }}
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
>
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
style={{ backgroundColor: modalCancelBtnColor }}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
>
Reset
</button>
+15 -12
View File
@@ -3,7 +3,6 @@ import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
const AddUser = (props) => {
const [name, setName] = useState("");
@@ -63,10 +62,8 @@ const AddUser = (props) => {
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
@@ -196,7 +193,13 @@ const AddUser = (props) => {
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
<Loader />
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<div className="w-full mx-auto">
@@ -208,7 +211,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
@@ -216,7 +219,7 @@ const AddUser = (props) => {
value={name}
onChange={(e) => setName(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
@@ -225,7 +228,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="email"
@@ -233,7 +236,7 @@ const AddUser = (props) => {
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
@@ -242,7 +245,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span className="text-[red] text-[13px]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
@@ -250,7 +253,7 @@ const AddUser = (props) => {
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
-283
View File
@@ -1,283 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput";
import Loader from "../primitives/Loader";
const BulkSendUi = (props) => {
const [forms, setForms] = useState([]);
const [formId, setFormId] = useState(2);
const formRef = useRef(null);
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [allowedForm, setAllowedForm] = useState(0);
const [isSignatureExist, setIsSignatureExist] = useState();
const allowedSigners = 50;
useEffect(() => {
signatureExist();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function to check atleast one signature field exist
const signatureExist = () => {
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
};
useEffect(() => {
if (scrollOnNextUpdate && formRef.current) {
formRef.current.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
});
setScrollOnNextUpdate(false);
}
}, [forms, scrollOnNextUpdate]);
useEffect(() => {
(() => {
if (props?.Placeholders?.length > 0) {
let users = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
users = [
...users,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
const totalForms = Math.floor(allowedSigners / users?.length);
setAllowedForm(totalForms);
}
})();
// eslint-disable-next-line
}, []);
const handleInputChange = (index, signer, fieldIndex) => {
const newForms = [...forms];
newForms[index].fields[fieldIndex].email = signer?.Email
? signer?.Email
: signer || "";
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
setForms(newForms);
};
const handleAddForm = (e) => {
e.preventDefault();
// Check if the quick send limit has been reached
if (forms?.length < allowedForm) {
if (props?.Placeholders.length > 0) {
let newForm = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
newForm = [
...newForm,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms([...forms, { Id: formId, fields: newForm }]);
}
setFormId(formId + 1);
setScrollOnNextUpdate(true);
} else {
// If the limit has been reached, throw an error with the appropriate message
alert("Quick send reached limit.");
}
};
const handleRemoveForm = (index) => {
const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms);
};
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmit(true);
// Create a copy of Placeholders array from props.item
let Placeholders = [...props.item.Placeholders];
// Initialize an empty array to store updated documents
let Documents = [];
// Loop through each form
forms.forEach((form) => {
//checking if user enter email which already exist as a signer then add user in a signers array
let existSigner = [];
form.fields.map((data) => {
if (data.signer) {
existSigner.push(data.signer);
}
});
// Map through the copied Placeholders array to update email values
const updatedPlaceholders = Placeholders.map((placeholder) => {
// Find the field in the current form that matches the placeholder Id
const field = form.fields.find(
(element) => parseInt(element.fieldId) === placeholder.Id
);
// If a matching field is found, update the email value in the placeholder
const signer = field?.signer?.objectId ? field.signer : "";
if (field) {
if (signer) {
return {
...placeholder,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
} else {
return {
...placeholder,
email: field.email,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
}
}
// If no matching field is found, keep the placeholder as is
return placeholder;
});
// Push a new document object with updated Placeholders into the Documents array
if (existSigner?.length > 0) {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders,
Signers: props.item.Signers
? [...props.item.Signers, ...existSigner]
: [...existSigner]
});
} else {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders
});
}
});
//console.log("Documents ", Documents);
await batchQuery(Documents);
};
const batchQuery = async (Documents) => {
const serverUrl = localStorage.getItem("baseUrl");
const functionsUrl = `${serverUrl}functions/batchdocuments`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = {
Documents: JSON.stringify(Documents)
};
try {
const res = await axios.post(functionsUrl, params, { headers: headers });
// console.log("res ", res);
if (res.data && res.data.result) {
props.handleClose("success", Documents?.length);
}
} catch (err) {
console.log("Err ", err);
props.handleClose("error", 0);
} finally {
setIsSubmit(false);
}
};
return (
<>
{isSubmit && (
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
{props.Placeholders?.length > 0 ? (
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className="min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col" key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
>
<i className="fa-light fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-light fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="op-btn op-btn-secondary focus:outline-none"
>
<i className="fa-light fa-paper-plane"></i>{" "}
<span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to
any contact.
</div>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please ensure there&#39;s at least one signature widget added for
all recipients.
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please add at least one role to this template in order to &apos;quick
send&apos; copies of it to multiple signers.
</div>
)}
</>
);
};
export default BulkSendUi;
+8 -12
View File
@@ -45,23 +45,19 @@ const Footer = () => {
};
return (
<>
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
<aside>
<p>
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</p>
</aside>
</footer>
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</div>
<button
className={`${
showButton ? "block" : "hidden"
} fixed bottom-4 right-4 px-3 p-2 text-xl op-bg-secondary text-white rounded focus:outline-none`}
} fixed bottom-4 right-4 px-3 p-2 text-xl bg-blue-500 text-white rounded focus:outline-none`}
onClick={scrollToTop}
>
<i className="fa-light fa-angle-up"></i>
<i className="fa-solid fa-angle-up"></i>
</button>
</>
);
@@ -67,12 +67,12 @@ const FullScreenButton = () => {
<div className="flex items-center">
<button
onClick={toggleFullScreen}
className="text-base-content p-2 text-sm focus:outline-none"
className="text-black p-2 text-sm focus:outline-none"
>
{isFullScreen ? (
<i className="fa-light fa-compress"></i>
<i className="fa-solid fa-compress"></i>
) : (
<i className="fa-light fa-maximize"></i>
<i className="fa fa-maximize"></i>
)}
</button>
</div>
+64 -79
View File
@@ -44,13 +44,7 @@ const Header = ({ showSidebar }) => {
const closeDropdown = () => {
setIsOpen(false);
if (Parse?.User?.current()) {
try {
Parse.User.logOut();
} catch (err) {
console.log("Err", err);
}
}
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
@@ -93,16 +87,14 @@ const Header = ({ showSidebar }) => {
}, [isOpen]);
return (
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
<button
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
onClick={showSidebar}
>
<i className="fa-light fa-bars text-xl text-base-content"></i>
</button>
</div>
<div className="flex-1 ml-2">
<div className="flex flex-row justify-between items-center gap-x-3 md:gap-x-4 bg-white md:px-4 px-2 shadow h-[50px] md:w-full ">
<button
className="cursor-pointer focus:outline-none"
onClick={showSidebar}
>
<i className={`fa-solid fa-bars text-xl `}></i>
</button>
<div className="flex-1">
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
<img
className="object-contain h-full w-auto"
@@ -111,11 +103,14 @@ const Header = ({ showSidebar }) => {
/>
</div>
</div>
<div id="profile-menu" className="flex-none gap-2">
<div
id="profile-menu"
className="flex justify-between items-center gap-x-3"
>
{!isSubscribe && (
<div>
<button
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
onClick={() => navigate("/subscription")}
>
Upgrade Now
@@ -123,7 +118,7 @@ const Header = ({ showSidebar }) => {
</div>
)}
{isPro && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
<div className="w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
PRO
</div>
)}
@@ -143,70 +138,60 @@ const Header = ({ showSidebar }) => {
</div>
)}
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer text-base-content text-sm"
>
<div onClick={toggleDropdown} className="cursor-pointer text-sm">
{username && username}
</div>
)}
<div className="op-dropdown op-dropdown-end" id="profile-menu">
<div
tabIndex={0}
role="button"
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
>
<i
tabIndex={0}
role="button"
onClick={toggleDropdown}
className="fa-light fa-angle-down text-base-content"
></i>
<div className="relative">
<div className="cursor-pointer">
<i onClick={toggleDropdown} className="fa-solid fa-angle-down"></i>
</div>
<ul
tabIndex={0}
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
<div
className={`dropdown absolute text-sm text-gray-700 font-light right-0 mt-4 p-1 w-48 bg-white border rounded-lg shadow-lg z-10 ${
isOpen ? "block" : "hidden"
}`}
>
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
<span>
<i className="fa-light fa-book"></i> Docs
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<span>
<i className="fa-light fa-user"></i> Profile
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<span>
<i className="fa-light fa-lock"></i> Change Password
</span>
</li>
<li
onClick={() => {
window.open("https://console.opensignlabs.com/");
}}
>
<span>
<i className="fa-light fa-id-card"></i> Console
</span>
</li>
<li onClick={closeDropdown}>
<span>
<i className="fa-light fa-arrow-right-from-bracket"></i> Log Out
</span>
</li>
</ul>
<ul>
<li
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
>
<i className="fa-solid fa-book"></i> Docs
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<i className="fa-regular fa-user"></i> Profile
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<i className="fa-solid fa-lock"></i> Change Password
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
window.open("https://console.opensignlabs.com/");
}}
>
<i className="fa-regular fa-id-card"></i> Console
</li>
<li
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
onClick={closeDropdown}
>
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
</li>
</ul>
</div>
</div>
</div>
</div>
+47 -16
View File
@@ -1,6 +1,5 @@
import React, { useState } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
// import FacebookLogin from "react-facebook-login";
const LoginFacebook = ({
@@ -50,7 +49,7 @@ const LoginFacebook = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const params = { ...details, Phone: extRes.get("Phone") };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
@@ -60,7 +59,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: details.Name,
email: details.Email,
phone: extRes?.get("Phone") || "",
phone: extRes.get("Phone"),
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -76,7 +75,7 @@ const LoginFacebook = ({
const handleSubmitbtn = async () => {
if (userDetails.Phone && userDetails.Company) {
setThirdpartyLoader(true);
const params = { ...fBDetails, Phone: userDetails?.Phone || "" };
const params = { ...fBDetails, Phone: userDetails.Phone };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
@@ -86,7 +85,7 @@ const LoginFacebook = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
role: "contracts_User",
company: userDetails.Company
}
@@ -97,7 +96,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
company: userDetails.Company
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -113,10 +112,29 @@ const LoginFacebook = ({
}
};
return (
<div className="relative">
<div style={{ position: "relative" }}>
{thirdpartyLoader && (
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
</div>
)}
{/* <FacebookLogin
@@ -128,10 +146,11 @@ const LoginFacebook = ({
/> */}
{isModal && (
<div
className="modal fade show block z-[1]"
className="modal fade show"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{ display: "block", zIndex: 1 }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
@@ -144,8 +163,13 @@ const LoginFacebook = ({
<div className="modal-body">
<form>
<div className="form-group">
<label htmlFor="Phone" className="col-form-label">
Phone <span className="text-[red] text-[13px]">*</span>
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="col-form-label"
>
Phone{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
@@ -162,8 +186,13 @@ const LoginFacebook = ({
/>
</div>
<div className="form-group">
<label htmlFor="Company" className="col-form-label">
Company <span className="text-[red] text-[13px]">*</span>
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
@@ -183,14 +212,16 @@ const LoginFacebook = ({
<button
type="button"
onClick={() => handleSubmitbtn()}
className="btn btn-info mr-[10px]"
className="btn btn-info"
style={{ marginRight: 10 }}
>
Sign up
</button>
<button
type="button"
className="btn btn-secondary w-[90px]"
className="btn btn-secondary"
onClick={() => setIsModal(false)}
style={{ width: 90 }}
>
Cancel
</button>
+55 -22
View File
@@ -3,7 +3,7 @@ import Parse from "parse";
import { jwtDecode } from "jwt-decode";
import { useScript } from "../hook/useScript";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
/*
* `GoogleSignInBtn`as it's name indicates it render google sign in button
@@ -110,7 +110,7 @@ const GoogleSignInBtn = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const params = { ...details, Phone: extRes.get("Phone") };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
@@ -121,7 +121,7 @@ const GoogleSignInBtn = ({
const LocalUserDetails = {
name: details.Name,
email: details.Gmail,
phone: extRes?.get("Phone") || "",
phone: extRes.get("Phone"),
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -139,7 +139,7 @@ const GoogleSignInBtn = ({
setThirdpartyLoader(true);
// e.preventDefault()
// console.log("handelSubmit", userDetails);
const params = { ...googleDetails, Phone: userDetails?.Phone };
const params = { ...googleDetails, Phone: userDetails.Phone };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
@@ -149,7 +149,7 @@ const GoogleSignInBtn = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
@@ -161,7 +161,7 @@ const GoogleSignInBtn = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails?.Phone || "",
phone: userDetails.Phone,
company: userDetails.Company
// jobTitle: userDetails.JobTitle
};
@@ -207,22 +207,45 @@ const GoogleSignInBtn = ({
localStorage.setItem("parseAppId", appid);
};
return (
<div className="relative">
<div style={{ position: "relative" }}>
{thirdpartyLoader && (
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<form className="px-4 py-3 text-base-content">
<form className="px-4 py-3">
<div className="mb-3">
<label htmlFor="Phone" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
@@ -235,12 +258,16 @@ const GoogleSignInBtn = ({
/>
</div>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
@@ -253,12 +280,16 @@ const GoogleSignInBtn = ({
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title <span className="text-[13px] text-[red]">*</span>
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
@@ -270,18 +301,20 @@ const GoogleSignInBtn = ({
required
/>
</div>
<div className="flex flex-row gap-2">
<div>
<button
type="button"
className="op-btn op-btn-primary"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
>
Sign up
</button>
<button
type="button"
className="op-btn op-btn-ghost"
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
+33 -28
View File
@@ -1,19 +1,38 @@
import React from "react";
import { Document, Page } from "react-pdf";
import { Stage, Layer, Rect, Text } from "react-konva";
import { Stage, Layer, Rect } from "react-konva";
const RenderDebugPdf = (props) => {
return (
<div>
<div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
<div
style={{
position: "sticky",
top: 0,
padding: 10,
zIndex: 1,
backgroundColor: "white",
border: "1px solid grey",
margin: "5px 0"
}}
>
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
</div>
<div
className="relative flex-1 cursor-crosshair border-[1px] border-[gray] overflow-auto"
style={{
flex: 1,
position: "relative",
border: "1px solid grey",
overflow: "auto"
}}
onMouseMove={props.handleMouseMoveDiv}
>
<Document
onLoadError={() => {
props.setPdfLoadFail(false);
const load = {
status: false,
type: "failed"
};
props.setPdfLoadFail(load);
}}
loading={"Loading Document.."}
onLoadSuccess={props.pageDetails}
@@ -37,36 +56,22 @@ const RenderDebugPdf = (props) => {
onMouseUp={props.handleMouseUp}
width={props.pdfDimension.width}
height={props.pdfDimension.height}
className="absolute top-0 right-0"
style={{ position: "absolute", top: 0, left: 0 }}
>
<Layer>
{props.annotations
.filter((value) => value.page === props.pageNumber)
.map((value) => {
const textX = value.x + value.width / 3;
const textY = value.y + value.height / 2.5;
return (
<React.Fragment key={value.key}>
<Rect
x={value.x}
y={value.y}
width={value.width}
height={value.height}
fill="#cbe9ed"
stroke="black"
/>
<Text
x={textX}
y={textY}
text={
value?.key > 0 ? "box " + value.key.toString() : "box"
}
fontSize={16}
fill="black"
align="center"
verticalAlign="middle"
/>
</React.Fragment>
<Rect
key={value.key}
x={value.x}
y={value.y}
width={value.width}
height={value.height}
fill="transparent"
stroke="black"
/>
);
})}
</Layer>
@@ -1,4 +1,5 @@
import React from "react";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../../constant/Utils";
@@ -25,17 +26,19 @@ const DashboardButton = (props) => {
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full shadow-md px-3 py-2 op-card bg-base-100`}
} w-full px-3 py-2 flex text-white rounded-md shadow overflow-hidden`}
>
<div className="flex flex-row items-center">
<span className="rounded-full bg-base-content bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa-light fa-info"
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className=" text-lg ml-3 text-base-content">{props.Label}</div>
<div className="">
<div className="text-base lg:text-lg text-black"> {props.Label}</div>
</div>
</div>
</div>
);
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
import axios from "axios";
import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import Tooltip from "../../primitives/Tooltip";
@@ -261,14 +262,21 @@ const DashboardCard = (props) => {
}
}
body = str;
const response = await axios.post(url, body, { headers: headers });
if (response.data.result.length > 0) {
setresponse(response.data.result[0][props.FilterData.key]);
setLoading(false);
} else {
setresponse("0");
setLoading(false);
}
await axios
.post(url, body, { headers: headers })
.then((response) => {
try {
if (response.data.result.length > 0) {
setresponse(response.data.result[0][props.FilterData.key]);
setLoading(false);
} else {
setresponse("0");
setLoading(false);
}
} catch (error) {
setLoading(false);
}
});
} catch (error) {
setLoading(false);
}
@@ -328,29 +336,25 @@ const DashboardCard = (props) => {
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
}`}
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden relative`}
>
<div className="flex items-center justify-start gap-5 text-white">
<span className="rounded-full bg-base-300 bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa-light fa-info"
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="font-medium">
<div className="">
<div className="text-base lg:text-lg"> {props.Label}</div>
<div className="text-2xl font-light">
{loading ? <div className="loader-01"></div> : setFormat(response)}
</div>
</div>
</div>
<div className="text-xs absolute top-3 right-2">
<Tooltip
id={props.Label}
iconColor={"white"}
message={props?.Data?.tourMessage}
/>
<div className="text-xs absolute top-2 right-1">
<Tooltip id={props.Label} iconColor={"white"} message={props?.Data?.tourMessage} />
</div>
</div>
);
@@ -3,7 +3,6 @@ import Parse from "parse";
import ReportTable from "../../primitives/GetReportDisplay";
import reportJson from "../../json/ReportJson";
import axios from "axios";
import Loader from "../../primitives/Loader";
function DashboardReport(props) {
// console.log("props ", props)
const [List, setList] = useState([]);
@@ -122,8 +121,21 @@ function DashboardReport(props) {
return (
<>
{isLoader ? (
<div className="h-[250px] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
@@ -7,37 +7,62 @@ const buttonList = [
label: "Sign yourself",
redirectId: "sHAnZphf69",
redirectType: "Form",
icon: "fa-light fa-pen-nib"
icon: "fas fa-pen-nib"
},
{
label: "Request signature",
redirectId: "8mZzFxbG1z",
redirectType: "Form",
icon: "fa-light fa-paper-plane"
icon: "fa-solid fa-paper-plane"
}
];
const GetDashboard = (props) => {
const Button = ({ label, redirectId, redirectType, icon }) => (
<DashboardButton
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
<div className={"bg-white rounded-md shadow w-full"}>
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<DashboardButton
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
</Suspense>
</div>
);
const renderSwitchWithTour = (col) => {
switch (col.widget.type) {
case "Card":
return (
<div
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
data-tut={col.widget.data.tourSection}
style={{ background: col.widget.bgColor }}
>
<Suspense
fallback={
<div className="h-[150px] w-full flex justify-center items-center">
loading...
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
@@ -47,6 +72,7 @@ const GetDashboard = (props) => {
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -63,7 +89,7 @@ const GetDashboard = (props) => {
);
}
default:
return <></>;
return null;
}
};
const renderSwitch = (col) => {
@@ -71,9 +97,8 @@ const GetDashboard = (props) => {
case "Card":
return (
<div
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
style={{ background: col.widget.bgColor }}
>
<Suspense fallback={<div>please wait</div>}>
<DashboardCard
@@ -82,6 +107,7 @@ const GetDashboard = (props) => {
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -96,7 +122,7 @@ const GetDashboard = (props) => {
);
}
default:
return <></>;
return null;
}
};
return (
@@ -104,7 +130,7 @@ const GetDashboard = (props) => {
<div className="mb-3">
<div
data-tut={"tourbutton"}
className="flex flex-col md:flex-row gap-4"
className="flex flex-col md:flex-row gap-6 md:gap-8"
>
{buttonList.map((btn) => (
<Button
@@ -117,19 +143,21 @@ const GetDashboard = (props) => {
))}
</div>
</div>
<div className="grid grid-cols-12 w-full gap-x-4">
{props?.dashboard?.columns?.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={col?.colsize}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={col?.colsize}>
{renderSwitch(col)}
</div>
)
)}
</div>
{props.dashboard.map((val, key) => (
<div key={"a" + key} className="row">
{val.columns.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitch(col)}
</div>
)
)}
</div>
))}
</div>
);
};
@@ -1,5 +1,7 @@
import React, { useState, useEffect, useRef } from "react";
import folder from "../../assets/images/folder.png";
import "../../styles/opensigndrive.css";
import pdfLogo from "../../assets/images/pdf3.png";
import axios from "axios";
import * as ContextMenu from "@radix-ui/react-context-menu";
import { saveAs } from "file-saver";
@@ -7,6 +9,7 @@ import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import ModalUi from "../../primitives/ModalUi";
import { themeColor } from "../../constant/const";
import FolderModal from "../shared/fields/FolderModal";
function DriveBody(props) {
@@ -17,10 +20,10 @@ function DriveBody(props) {
const [selectDoc, setSelectDoc] = useState();
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
const contextMenu = [
{ type: "Download", icon: "fa-light fa-arrow-down" },
{ type: "Rename", icon: "fa-light fa-font" },
{ type: "Move", icon: "fa-light fa-file-export" },
{ type: "Delete", icon: "fa-light fa-trash" }
{ type: "Download", icon: "fa-solid fa-arrow-down" },
{ type: "Rename", icon: "fa-solid fa-font" },
{ type: "Move", icon: "fa-solid fa-file-export" },
{ type: "Delete", icon: "fa-solid fa-trash" }
];
const navigate = useNavigate();
@@ -310,26 +313,32 @@ function DriveBody(props) {
const signersName = () => {
const getSignersName = signerExist.map((data) => data.Name);
const signerName = getSignersName.join(", ");
const signerName = getSignersName.join(",");
return (
<span className="text-[12px] font-medium w-[90%] break-words">
{signerName}
<span
className="statusSpan"
style={{ width: "90%", wordWrap: "break-word" }}
>
{signerName}{" "}
</span>
);
};
return listType === "table" ? (
data.Type === "Folder" ? (
<tr onClick={() => handleOnclikFolder(data)}>
<td className="cursor-pointer flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="w-[26px] h-[26px] fill-current op-text-secondary"
>
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg>
<span className="text-[12px] font-medium">{data.Name}</span>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-folder"
aria-hidden="true"
style={{ color: "#f0ad26", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
</td>
<td>_</td>
<td>Folder</td>
@@ -338,15 +347,17 @@ function DriveBody(props) {
</tr>
) : (
<tr onClick={() => checkPdfStatus(data)}>
<td className="cursor-pointer flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
className="w-[26px] h-[26px] fill-current op-text-primary"
>
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
</svg>
<span className="text-[12px] font-medium">{data.Name}</span>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-file-pdf"
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
</td>
<td>{createddate}</td>
<td>Pdf</td>
@@ -357,29 +368,32 @@ function DriveBody(props) {
e.stopPropagation();
handleMenuItemClick("Download", data);
}}
className="fa-light fa-download mr-[8px] op-text-primary"
className="fa fa-download"
aria-hidden="true"
style={{ color: "#ed280e", marginRight: "8px" }}
></i>
</td>
</tr>
)
) : listType === "list" && data.Type === "Folder" ? (
<div key={ind} className="relative w-[100px] h-[100px] mx-2 my-3">
<div key={ind} className="folderBox">
<ContextMenu.Root>
<ContextMenu.Trigger className="flex flex-col justify-center items-center">
{/* folder */}
<ContextMenu.Trigger className="ContextMenuTrigger">
<div
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer"
}}
data-tut={props.dataTutSeventh}
onClick={() => handleOnclikFolder(data)}
className="cursor-pointer"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="w-[100px] h-[100px] fill-current op-text-secondary"
>
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg>
<img
alt="no img"
onClick={() => handleOnclikFolder(data)}
src={folder}
width={100}
height={103}
/>
{rename === data.objectId ? (
<input
onFocus={() => {
@@ -397,10 +411,15 @@ function DriveBody(props) {
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px"
}}
/>
) : (
<span className="fileName">{data.Name}</span>
<span className="foldName">{data.Name}</span>
)}
</div>
</ContextMenu.Trigger>
@@ -415,8 +434,8 @@ function DriveBody(props) {
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
<i className="fa-light fa-font mr-[8px]"></i>
<span>Rename</span>
<i className="fa-solid fa-font"></i>
<span style={{ marginLeft: "8px" }}>Rename</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
@@ -427,65 +446,63 @@ function DriveBody(props) {
<HoverCard.Trigger asChild>
<div>
<ContextMenu.Root>
<div className="relative w-[100px] h-[100px] mx-2 my-3">
<ContextMenu.Trigger className="flex flex-col justify-center items-center">
{/* pdf */}
<div
data-tut={props.dataTutSixth}
<div className="icon-container">
<ContextMenu.Trigger className="ContextMenuTrigger">
<img
alt="PDF"
className="pdf-icon"
src={pdfLogo}
onClick={() => checkPdfStatus(data)}
className="cursor-pointer"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
className="w-[100px] h-[100px] fill-current op-text-primary"
>
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
</svg>
{rename === data.objectId ? (
<input
autoFocus={true}
type="text"
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
/>
) : (
<span className="fileName">{data.Name}</span>
)}
</div>
data-tut={props.dataTutSixth}
/>
{rename === data.objectId ? (
<input
autoFocus={true}
type="text"
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px"
}}
/>
) : (
<span className="fileName">{data.Name}</span>
)}
</ContextMenu.Trigger>
{status === "Completed" ? (
<div className="status-badge completed">
<i className="fa-light fa-check-circle"></i>
<i className="fas fa-check-circle"></i>
</div>
) : status === "Declined" ? (
<div className="status-badge declined">
<i className="fa-light fa-thumbs-down"></i>
<i className="fa fa-thumbs-down"></i>
</div>
) : status === "Expired" ? (
<div className="status-badge expired">
<i className="fa-light fa-hourglass-end"></i>
<i className="fa fa-hourglass-end"></i>
</div>
) : status === "Draft" ? (
<div className="status-badge draft">
<i className="fa-light fa-file"></i>
<i className="fa fa-file"></i>
</div>
) : (
status === "In Progress" && (
<div className="status-badge in-progress">
<i className="fa-light fa-paper-plane"></i>
<i className="fa fa-paper-plane"></i>
</div>
)
)}
@@ -505,7 +522,7 @@ function DriveBody(props) {
className="ContextMenuItem"
>
<i className={menu.icon}></i>
<span className="ml-[8px]">{menu.type}</span>
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
</ContextMenu.Item>
);
})}
@@ -516,18 +533,22 @@ function DriveBody(props) {
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong className="text-[13px]">Title: </strong>
<span className="text-[12px] font-medium mb-0"> {data.Name}</span>
<strong style={{ fontSize: "13px" }}>Title: </strong>
<span className="statusSpan" style={{ marginBottom: "0px" }}>
{" "}
{data.Name}
</span>
<br />
<strong className="text-[13px]">Status: </strong>
<span className="text-[12px] font-medium"> {status}</span>
<strong style={{ fontSize: "13px" }}>Status: </strong>
<span className="statusSpan"> {status}</span>
<br />
<strong className="text-[13px]">Created Date: </strong>
<span className="text-[12px] font-medium">{createddate}</span>
<strong style={{ fontSize: "13px" }}>Created Date: </strong>
<span className="statusSpan">{createddate}</span>
<br />
{signerExist && (
<>
<strong className="text-[13px]">Signers: </strong>
<strong style={{ fontSize: "13px" }}>Signers: </strong>
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
{signersName()}
</>
)}
@@ -566,9 +587,13 @@ function DriveBody(props) {
</Table>
</div>
) : (
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
<div className="pdfContainer">
{props.pdfData.map((data, ind) => {
return <div key={ind}>{handleFolderData(data, ind, "list")}</div>;
return (
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
</div>
);
})}
</div>
)}
@@ -584,23 +609,44 @@ function DriveBody(props) {
)}
<ModalUi
isOpen={isDeleteDoc}
headerColor={themeColor}
title={"Delete Document"}
handleClose={() => setIsDeleteDoc(false)}
handleClose={() => {
setIsDeleteDoc(false);
}}
>
<div className="h-full p-[20px] text-base-content">
<div style={{ height: "100%", padding: 20 }}>
<p>Are you sure you want to delete this document?</p>
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() => handleDeleteDocument(selectDoc)}
onClick={() => {
handleDeleteDocument(selectDoc);
}}
style={{
background: themeColor,
color: "white"
}}
type="button"
className="op-btn op-btn-primary mr-2"
className="finishBtn"
>
Yes
</button>
<button
onClick={() => setIsDeleteDoc(false)}
onClick={() => {
setIsDeleteDoc(false);
}}
type="button"
className="op-btn op-btn-neutral"
className="finishBtn cancelBtn"
>
No
</button>
@@ -8,8 +8,11 @@ const AddRoleModal = (props) => {
isOpen={props.isModalRole}
handleClose={props.handleCloseRoleModal}
>
<div className="h-full py-[10px] px-[20px]">
<form className="flex flex-col" onSubmit={props.handleAddRole}>
<div className="addusercontainer">
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={props.handleAddRole}
>
<input
value={props.roleName}
onChange={(e) => props.setRoleName(e.target.value)}
@@ -18,20 +21,39 @@ const AddRoleModal = (props) => {
? "User " + (props.signersdata.length + 1)
: "User 1"
}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
className="addUserInput"
/>
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
<p
style={{
color: "grey",
fontSize: 11,
margin: "10px 0 10px 5px"
}}
>
e.g: Customer, Hr, Director, Manager, Student, etc...
</p>
<div>
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
<button type="submit" className="op-btn op-btn-primary">
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginBottom: "15px"
}}
></div>
<button
type="submit"
style={{
background: "#00a2b7"
}}
className="finishBtn"
>
Add
</button>
<button
onClick={props.handleCloseRoleModal}
type="button"
className="op-btn op-btn-ghost ml-2"
className="finishBtn cancelBtn"
>
Close
</button>
@@ -3,9 +3,13 @@ import React from "react";
function BorderResize({ right, top }) {
return (
<div
className={`${right ? `-right-[12px]` : "-right-[1px]"} ${
top ? `-bottom-[11px]` : "-bottom-[1px]"
} absolute inline-block w-[14px] h-[14px] hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
className="borderResize"
style={{
right: right ? right + "px" : "-1px",
bottom: top ? top + "px" : "-1px",
borderRight: "3px solid #188ae2",
borderBottom: "3px solid #188ae2"
}}
></div>
);
}
@@ -1,4 +1,5 @@
import React from "react";
import { themeColor } from "../../constant/const";
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
@@ -16,26 +17,55 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
};
return (
<div data-tut="reactourThird">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<div>
<div
style={{
background: themeColor,
color: "white",
padding: "5px",
fontFamily: "sans-serif",
marginTop: "5px"
}}
>
Signature
</div>
<div className="flex flex-col items-center mt-[10px] font-semibold">
<p className="text-base-content">Your Signature</p>
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
<img
alt="signature"
className="w-full h-full object-contain"
src={defaultSignImg}
/>
</div>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginTop: "10px",
fontWeight: "600"
}}
>
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
src={defaultSignImg}
/>
</div>
<button
style={{
background: themeColor,
color: "white",
marginTop: "20px",
cursor: "pointer",
marginBottom: "10px"
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
</div>
</div>
);
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
import Loader from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError";
function DraftDocument() {
@@ -86,7 +86,7 @@ function DraftDocument() {
return (
<div>
{isLoading.isLoader ? (
<LoaderWithMsg isLoading={isLoading} />
<Loader isLoading={isLoading} />
) : (
<HandleError handleError={isLoading.message} />
)}
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { isEnableSubscription } from "../../constant/const";
import { isEnableSubscription, themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
@@ -147,7 +147,7 @@ function DropdownWidgetOption(props) {
return (
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
<div className="h-full p-[15px] text-base-content">
<div className="h-full p-[15px]">
<form
onSubmit={(e) => {
e.preventDefault();
@@ -155,25 +155,38 @@ function DropdownWidgetOption(props) {
}}
>
<div>
<label className="text-[13px] font-semibold">
Name<span className="text-[red] text-[13px]"> *</span>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
required
defaultValue={dropdownName}
value={dropdownName}
onChange={(e) => setDropdownName(e.target.value)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="drodown-input"
/>
<label className="text-[13px] font-semibold mt-[5px]">
<label
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
>
Options
</label>
<div className="flex flex-col">
<div
style={{
display: "flex",
flexDirection: "column"
}}
>
{dropdownOptionList.map((option, index) => (
<div
key={index}
className="flex flex-row mb-[5px] items-center"
style={{
display: "flex",
flexDirection: "row",
marginBottom: "5px",
alignItems: "center"
}}
>
{props.type === "checkbox" && !props.isSignYourself && (
<input
@@ -193,34 +206,51 @@ function DropdownWidgetOption(props) {
setDefaultCheckbox(removeOption);
}
}}
className="op-checkbox focus:outline-none hover:border-base-content mr-[5px]"
style={{
width: "23px",
height: "19px",
marginRight: "5px"
}}
/>
)}
<input
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="drodown-input"
type="text"
value={option}
onChange={(e) => handleInputChange(index, e.target.value)}
/>
<i
className="fa-light fa-rectangle-xmark text-[25px] ml-[10px] text-accent cursor-pointer"
className="fa-solid fa-rectangle-xmark"
onClick={() => handleDeleteInput(index)}
style={{
color: "red",
fontSize: "25px",
marginLeft: "10px"
}}
></i>
</div>
))}
<i
onClick={handleAddInput}
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
style={{
cursor: "pointer",
color: themeColor,
fontSize: "25px",
marginLeft: "10px"
}}
className="fa-solid fa-square-plus"
></i>
<div>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
style={{
fontSize: "13px",
fontWeight: "600",
color: !props.isSubscribe && "gray"
}}
>
Minimun check
</label>
@@ -233,16 +263,18 @@ function DropdownWidgetOption(props) {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className={`${
className={
props.isSubscribe || !isEnableSubscription
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
? "drodown-input"
: "disabled drodown-input"
}
/>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
style={{
fontSize: "13px",
fontWeight: "600",
color: !props.isSubscribe && "gray"
}}
>
Maximum check
</label>
@@ -254,11 +286,11 @@ function DropdownWidgetOption(props) {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className={`${
className={
props.isSubscribe || !isEnableSubscription
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
? "drodown-input"
: "disabled drodown-input"
}
/>
</>
)}
@@ -266,22 +298,32 @@ function DropdownWidgetOption(props) {
</div>
{["dropdown", radioButtonWidget].includes(props.type) && (
<>
<label className="text-[13px] font-semibold mt-[5px]">
<label
style={{
fontSize: "13px",
fontWeight: "600",
marginTop: "5px"
}}
>
Default value
</label>
<select
onChange={(e) => setDefaultValue(e.target.value)}
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
className="drodown-input"
name="defaultvalue"
value={defaultValue}
placeholder="select default value"
>
<option value="" disabled hidden className="text-[13px]">
<option value="" disabled hidden style={{ fontSize: "13px" }}>
Select...
</option>
{dropdownOptionList.map((data, ind) => {
return (
<option className="text-[13px]" key={ind} value={data}>
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
{data}
</option>
);
@@ -291,21 +333,35 @@ function DropdownWidgetOption(props) {
)}
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
<>
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginTop: "0.5rem"
}}
>
{statusArr.map((data, ind) => {
return (
<div
key={ind}
className="flex flex-row gap-[5px] items-center"
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
>
<input
className="op-radio op-radio-xs my-1"
style={{ accentColor: "red" }}
type="radio"
name="status"
onChange={() => setStatus(data.toLowerCase())}
checked={status.toLowerCase() === data.toLowerCase()}
/>
<div className="text-[13px] font-500">{data}</div>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
</div>
);
})}
@@ -313,31 +369,33 @@ function DropdownWidgetOption(props) {
</>
)}
{["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 my-2 items-center text-center">
<div className="flex flex-row gap-5 mt-2 items-center text-center">
{!props.isSignYourself && (
<div className="flex items-center">
<div>
<input
id="isreadonly"
type="checkbox"
checked={isReadOnly}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsReadOnly(e.target.checked)}
onChange={(e) => {
setIsReadOnly(e.target.checked);
}}
/>
<label className="ml-1 mb-0" htmlFor="isreadonly">
<label className="ml-1" htmlFor="isreadonly">
Is read only
</label>
</div>
)}
<div className="flex items-center">
<div>
<input
id="ishidelabel"
type="checkbox"
checked={isHideLabel}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsHideLabel(e.target.checked)}
onChange={(e) => {
setIsHideLabel(e.target.checked);
}}
/>
<label className="ml-1 mb-0" htmlFor="ishidelabel">
<label className="ml-1" htmlFor="ishidelabel">
Hide labels
</label>
</div>
@@ -363,15 +421,19 @@ function DropdownWidgetOption(props) {
<button
disabled={dropdownOptionList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="submit"
className="op-btn op-btn-primary"
className={"finishBtn"}
>
Save
</button>
{props.currWidgetsDetails?.options?.values?.length > 0 && (
<button
type="submit"
className="op-btn op-btn-ghost ml-1"
className="finishBtn cancelBtn"
onClick={() => {
props.handleClose && props.handleClose();
resetState();
+55 -109
View File
@@ -1,8 +1,6 @@
import React, { useState, useEffect } from "react";
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import React, { useState } from "react";
import "../../styles/AddUser.css";
import { getFileName } from "../../constant/Utils";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
@@ -11,21 +9,9 @@ const EditTemplate = ({ template, onSuccess }) => {
Name: template?.Name || "",
Note: template?.Note || "",
Description: template?.Description || "",
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
AutomaticReminders: template?.AutomaticReminders || false,
RemindOnceInEvery: template?.RemindOnceInEvery || 5
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
});
const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => {
fetchSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
};
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
@@ -39,40 +25,34 @@ const EditTemplate = ({ template, onSuccess }) => {
e.preventDefault();
e.stopPropagation();
const isChecked = formData.SendinOrder === "true" ? true : false;
const AutoReminder = formData?.AutomaticReminders || false;
let reminderDate = {};
if (AutoReminder) {
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
const ReminderDate = new Date(template?.createdAt);
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
reminderDate = { NextReminderDate: ReminderDate };
}
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
const data = { ...formData, SendinOrder: isChecked };
onSuccess(data);
};
const handleAutoReminder = () => {
setFormData((prev) => ({
...prev,
AutomaticReminders: !formData.AutomaticReminders
}));
};
return (
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
<div className="text-base-content">
<div className="addusercontainer">
<div className="form-wrapper">
<form onSubmit={handleSubmit}>
<div className="mb-[0.35rem]">
<label htmlFor="name" className="text-[13px]">
<div>
<label htmlFor="name" style={{ fontSize: 13 }}>
File
</label>
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
<div
style={{
padding: "0.5rem 0.75rem",
border: "1px solid #d1d5db",
borderRadius: "0.375rem",
fontSize: "0.75rem",
fontWeight: "700"
}}
>
{getFileName(template.URL)}
</div>
</div>
<div className="mb-[0.35rem]">
<label htmlFor="name" className="text-[13px]">
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Name
<span className="text-[13px] text-[red]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
@@ -80,11 +60,11 @@ const EditTemplate = ({ template, onSuccess }) => {
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
/>
</div>
<div className="mb-[0.35rem]">
<label htmlFor="Note" className="text-[13px]">
<div className="form-section">
<label htmlFor="Note" style={{ fontSize: 13 }}>
Note
</label>
<input
@@ -93,11 +73,11 @@ const EditTemplate = ({ template, onSuccess }) => {
id="Note"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
/>
</div>
<div className="mb-[0.35rem]">
<label htmlFor="Description" className="text-[13px]">
<div className="form-section">
<label htmlFor="Description" style={{ fontSize: 13 }}>
Description
</label>
<input
@@ -106,85 +86,51 @@ const EditTemplate = ({ template, onSuccess }) => {
id="Description"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
/>
</div>
<div className="mb-[0.35rem]">
<label className="text-[13px]">Send In Order</label>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<div className="form-section">
<label style={{ fontSize: 13 }}>Send In Order</label>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div className="text-[12px]">Yes</div>
<div style={{ fontSize: 12 }}>Yes</div>
</div>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"false"}
name="SendinOrder"
className="op-radio op-radio-xs"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-[12px]">No</div>
<div style={{ fontSize: 12 }}>No</div>
</div>
</div>
<div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span
className={
isSubscribe || !isEnableSubscription
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
className={`${
isSubscribe || !isEnableSubscription
? "cursor-pointer "
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
checked={formData.AutomaticReminders}
onChange={handleAutoReminder}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
/>
</label>
</div>
{isSubscribe && formData?.AutomaticReminders === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
value={formData.RemindOnceInEvery}
name="RemindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
required
/>
</div>
)}
<div className="mt-[1rem] flex justify-start">
<button type="submit" className="op-btn op-btn-primary">
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
<div className="buttoncontainer">
<button type="submit" className="submitbutton">
Submit
</button>
</div>
+40 -30
View File
@@ -1,42 +1,52 @@
import React from "react";
import Tooltip from "../../primitives/Tooltip";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import EditorToolbar, { module1, formats } from "./EditorToolbar";
export function EmailBody(props) {
return (
<form className="flex flex-col text-base-content text-lg font-normal">
<div className="m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
<label className="text-sm ml-2">
Subject <Tooltip message={"email subject"} />
</label>
<input
required
value={props.requestSubject}
onChange={(e) => props.setRequestSubject(e.target.value)}
placeholder='${senderName} has requested you to sign "${documentName}"'
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<label className="text-sm ml-2 mt-3">
Body <Tooltip message={"email body"} />
</label>
<div className="px-3 py-2 w-full focus:outline-none text-xs">
<EditorToolbar containerId="toolbar1" />
<ReactQuill
theme="snow"
value={props.requestBody}
// value={typedText}
placeholder="add body of email "
// readOnly={loading}
// onChangeSelection={handleTextSelection} // Listen for text selection
ref={props.editorRef}
modules={module1}
formats={formats}
onChange={props.handleOnchangeRequest}
/>
<React.Fragment>
<div className={`flex justify-center py-1 p-2`}>
<div className=" flex flex-col">
<form>
<div className="text-lg font-normal text-black">
<div className=" m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
<label className="text-sm ml-2">
Subject <Tooltip message={"email subject"} />
</label>
<input
required
value={props.requestSubject}
onChange={(e) => props.setRequestSubject(e.target.value)}
placeholder='${senderName} has requested you to sign "${documentName}"'
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
<label className="text-sm ml-2 mt-3">
Body <Tooltip message={"email body"} />
</label>
<div className="px-3 py-2 w-full focus:outline-none text-xs">
<EditorToolbar containerId="toolbar1" />
<ReactQuill
theme="snow"
value={props.requestBody}
// value={typedText}
placeholder="add body of email "
// readOnly={loading}
// onChangeSelection={handleTextSelection} // Listen for text selection
ref={props.editorRef}
modules={module1}
formats={formats}
onChange={props.handleOnchangeRequest}
/>
</div>
</div>
</div>
</form>
</div>
</div>
</form>
</React.Fragment>
);
}
+259 -133
View File
@@ -1,15 +1,16 @@
import React, { useState } from "react";
import { saveAs } from "file-saver";
import celebration from "../../assets/images/newCeleb.gif";
import axios from "axios";
import { getBase64FromUrl } from "../../constant/Utils";
import { themeColor } from "../../constant/const";
import printModule from "print-js";
import Loader from "../../primitives/Loader";
import ModalUi from "../../primitives/ModalUi";
import loader from "../../assets/images/loader2.gif";
function EmailComponent({
isEmail,
pdfUrl,
isCeleb,
setIsEmail,
setSuccessEmail,
pdfName,
@@ -165,141 +166,266 @@ function EmailComponent({
<div>
{/* isEmail */}
{isEmail && (
<ModalUi isOpen showHeader={false}>
{isLoading && (
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2]/70">
<Loader />
<span className="text-[12px] text-base-content">
This might take some time
</span>
</div>
)}
<div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content">
<span className="text-base-content font-semibold">
Successfully signed!
</span>
<div className="flex flex-row">
<div></div>
{!isAndroid && (
<button
onClick={handleToPrint}
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
>
<i className="fa-light fa-print" aria-hidden="true"></i>
Print
</button>
)}
<button
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
onClick={() => handleDownloadPdf()}
<div className="modaloverlay">
<div className="modalcontainer">
{isLoading && (
<div
style={{
position: "absolute",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
zIndex: "20",
backgroundColor: "#e6f2f2",
opacity: 0.8
}}
>
<i className="fa-light fa-download" aria-hidden="true"></i>
Download
<img
alt="no img"
src={loader}
style={{ width: "70px", height: "70px" }}
/>
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
This might take some time
</span>
</div>
)}
<div style={{ background: "#32a3ac" }} className="modalheader">
<span style={{ color: "white" }}>Successfully signed!</span>
<div style={{ display: "flex", flexDirection: "row" }}>
<div></div>
{!isAndroid && (
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="btn btn-primary btn-sm"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
</button>
)}
<button
className="btn btn-danger btn-sm"
onClick={() => handleDownloadPdf()}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
</button>
</div>
</div>
<div style={{ height: "100%", padding: 20 }}>
{isCeleb && (
<div
style={{
position: "absolute",
marginLeft: "50px"
}}
>
<img
alt="print img"
width={300}
height={250}
// style={styles.gifCeleb}
src={celebration}
/>
</div>
)}
<p
style={{
fontFamily: "system-ui",
verticalAlign: "baseline",
fontWeight: "500",
color: "#403f3e",
fontSize: "15px",
marginBottom: "5px"
}}
>
Recipients added here will get a copy of the signed document.
</p>
{emailList.length > 0 ? (
<>
<div className="addEmail">
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}
>
{emailList.map((data, ind) => {
return (
<div
className="emailChip"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
key={ind}
>
<span
style={{
color: "white",
fontSize: "13px"
}}
>
{data}
</span>
<span
style={{
color: "white",
fontSize: 13,
fontWeight: 600,
marginLeft: 7,
cursor: "pointer"
}}
onClick={() => removeChip(ind)}
>
<i className="fa-solid fa-xmark"></i>
</span>
</div>
);
})}
</div>
{emailList.length <= 9 && (
<input
type="text"
value={emailValue}
className="addEmailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
)}
</div>
</>
) : (
<div>
<input
type="text"
value={emailValue}
className="emailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
</div>
)}
<span
style={{
cursor: emailValue && "pointer"
}}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i
style={{
backgroundColor: themeColor,
padding: "5px 7px",
marginTop: "10px",
color: "white",
borderRadius: "2px"
}}
className="fa fa-plus"
aria-hidden="true"
></i>
</span>
<div
style={{
background: "#e3e2e1",
marginTop: "10px",
padding: "5px",
borderRadius: "3px"
}}
>
<span style={{ fontWeight: "700" }}>Note:</span>
<span style={{ fontSize: "15px" }}>
{" "}
You can only send to ten recipients at a time.
</span>
</div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
disabled={emailList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="button"
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
onClick={() => sendEmail()}
>
Send
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={() => {
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}}
>
Close
</button>
</div>
</div>
<div className="h-full p-[20px]">
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
Recipients added here will get a copy of the signed document.
</p>
{emailList.length > 0 ? (
<>
<div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]">
<div className="flex flex-row flex-wrap">
{emailList.map((data, ind) => {
return (
<div
className="flex flex-row items-center op-bg-primary m-[4px] rounded-md py-[5px] px-[10px]"
key={ind}
>
<span className="text-base-100 text-[13px]">
{data}
</span>
<span
className="text-base-100 text-[13px] font-semibold ml-[7px] cursor-pointer"
onClick={() => removeChip(ind)}
>
<i className="fa-light fa-xmark"></i>
</span>
</div>
);
})}
</div>
{emailList.length <= 9 && (
<input
type="text"
value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-transparent outline-none"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
)}
</div>
</>
) : (
<div>
<input
type="text"
value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
onBlur={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
required
/>
</div>
)}
<button
className={`${
emailValue ? "cursor-pointer" : "cursor-default"
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i className="fa-light fa-plus" aria-hidden="true"></i>
</button>
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
<span className="font-bold">Note: </span>
<span className="text-[15px]">
You can only send to ten recipients at a time.
</span>
</div>
<hr className="w-full my-[15px] bg-base-content" />
<button
type="button"
className="op-btn op-btn-secondary"
onClick={() => emailList.length > 0 && sendEmail()}
>
Send
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-2"
onClick={() => {
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}}
>
Close
</button>
</div>
</ModalUi>
</div>
)}
</div>
);
+287 -196
View File
@@ -1,15 +1,15 @@
import React, { useState } from "react";
import PrevNext from "./PrevNext";
import {
handleDownloadCertificate,
handleDownloadPdf,
handleToPrint
} from "../../constant/Utils";
import printModule from "print-js";
import { getBase64FromUrl } from "../../constant/Utils";
import { saveAs } from "file-saver";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import { themeColor } from "../../constant/const";
import axios from "axios";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
import Parse from "parse";
function Header({
isPdfRequestFiles,
@@ -29,6 +29,7 @@ function Header({
isShowHeader,
decline,
currentSigner,
dataTut4,
alreadySign,
isSignYourself,
setIsEmail,
@@ -39,9 +40,16 @@ function Header({
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isDownloading, setIsDownloading] = useState("");
const [isCertificate, setIsCertificate] = useState(false);
const isGuestSigner = localStorage.getItem("isGuestSigner");
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
//function for show decline alert
const handleDeclinePdfAlert = async () => {
const currentDecline = {
@@ -51,8 +59,101 @@ function Header({
setIsDecline(currentDecline);
};
//function for print digital sign pdf
const handleToPrint = async (event) => {
event.preventDefault();
try {
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const pdf = await getBase64FromUrl(url);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === "MacIntel" &&
navigator.maxTouchPoints > 1)) &&
!window.MSStream;
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(
atob(pdf)
.split("")
.map((char) => char.charCodeAt(0))
);
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
} catch (err) {
console.log("err in getsignedurl", err);
alert("something went wrong, please try again later.");
}
};
//handle download signed pdf
const handleDownloadPdf = async () => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
try {
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
} catch (err) {
console.log("err in getsignedurl", err);
alert("something went wrong, please try again later.");
}
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
//handle download signed pdf
const handleDownloadCertificate = async () => {
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
try {
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
} catch (err) {
console.log("err in download in certificate", err);
}
} else {
setIsCertificate(true);
try {
const data = {
docId: pdfDetails[0]?.objectId
};
const docDetails = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getDocument`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
);
if (docDetails.data && docDetails.data.result) {
const doc = docDetails.data.result;
if (doc?.CertificateUrl) {
await fetch(doc?.CertificateUrl);
const certificateUrl = doc?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
setIsCertificate(false);
} else {
setIsCertificate(true);
}
}
} catch (err) {
console.log("err in download in certificate", err);
alert("something went wrong, please try again later.");
}
}
};
return (
<div className="flex py-[5px]">
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
{isMobile && isShowHeader ? (
<div
id="navbar"
@@ -63,26 +164,57 @@ function Header({
: window.innerWidth - 30 + "px"
}}
>
<div className="flex justify-between items-center py-[5px] px-[10px] ">
<div className="preBtn2">
<div onClick={() => navigate(-1)}>
<i
className="fa-light fa-arrow-left text-base-content"
className="fa fa-arrow-left"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</div>
<div>
<i
onClick={() => {
if (pageNumber > 1) {
previousPage();
}
}}
className="fa fa-backward"
style={{ color: "gray", cursor: "pointer" }}
></i>
<span
style={{
fontSize: "13px",
fontWeight: "600",
marginLeft: "10px",
marginRight: "10px"
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<i
onClick={() => {
if (pageNumber < allPages) {
nextPage();
}
}}
className="fa fa-forward"
style={{ color: "gray", cursor: "pointer" }}
></i>
</div>
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{pdfUrl && alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
<i
className="fa-light fa-ellipsis-v"
aria-hidden="true"
></i>
<div
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px",
padding: "0px 3px 0px 5px"
}}
>
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
@@ -92,14 +224,18 @@ function Header({
>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
onClick={() => handleDownloadPdf()}
>
<div className="flex flex-row">
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa-light fa-arrow-down mr-[3px]"
className="fa fa-arrow-down"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Download
</div>
@@ -107,16 +243,17 @@ function Header({
{isCompleted && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() =>
handleDownloadCertificate(
pdfDetails,
setIsDownloading
)
}
onClick={() => handleDownloadCertificate()}
>
<div className="border-none bg-[#fff]">
<div
style={{
border: "none",
backgroundColor: "#fff"
}}
>
<i
className="fa-light fa-award mr-[3px]"
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
@@ -128,9 +265,15 @@ function Header({
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div className="flex flex-row">
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa-light fa-envelope mr-[3px]"
className="fa fa-envelope"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Mail
@@ -139,14 +282,18 @@ function Header({
)}
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
onClick={handleToPrint}
>
<div className="flex flex-row">
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa-light fa-print mr-[3px]"
className="fa fa-print"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Print
</div>
@@ -155,16 +302,24 @@ function Header({
</DropdownMenu.Portal>
</DropdownMenu.Root>
) : (
<div className="flex justify-around items-center">
<div style={{ display: "flex", justifyContent: "space-around" }}>
{/* current signer is checking user send request and check status of pdf sign than if current
user exist than show finish button else no
*/}
{currentSigner && (
<div className="flex items-center" data-tut="reactourFifth">
<>
{decline && (
<div
onClick={() => handleDeclinePdfAlert()}
className="text-[red] border-none font-[650] text-[14px] mr-2"
onClick={() => {
handleDeclinePdfAlert();
}}
style={{
color: "red",
border: "none",
fontWeight: "650",
fontSize: "16px",
marginRight: "10px"
}}
>
Decline
</div>
@@ -176,10 +331,13 @@ function Header({
alertSendEmail();
}
}}
className={`${
isMailSend ? "" : "op-link-primary"
} op-link no-underline font-[650] text-[14px]`}
data-tut="headerArea"
style={{
color: isMailSend ? "gray" : themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
data-tut={dataTut4}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
</div>
@@ -193,52 +351,24 @@ function Header({
embedWidgetsData();
}
}}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
>
Finish
</div>
)}
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="font-[650] text-[18px] px-2 ml-[4px] text-base-content no-underline">
<i
className="fa-light fa-ellipsis-v"
aria-hidden="true"
></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="bg-white shadow-md rounded px-2 py-1"
sideOffset={5}
>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
className="fa-light fa-arrow-down mr-[3px]"
aria-hidden="true"
></i>
<span className="font-[500]">Download</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</div>
</>
)}
</div>
)}
</div>
</div>
) : (
<div className="flex flex-wrap justify-between items-center w-full gap-y-1 ml-1">
<div className="flex flex-wrap justify-between items-center mt-[5px]">
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
@@ -252,12 +382,12 @@ function Header({
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span className="text-[13px] text-[#f5405e]">
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span className="text-[13px] text-[#f5405e]">
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
@@ -269,192 +399,166 @@ function Header({
{setIsEditTemplate && (
<button
onClick={() => setIsEditTemplate(true)}
className="outline-none border-none text-center mr-[3px]"
className="outline-none border-none text-center mr-[5px]"
>
<i className="fa-light fa-gear fa-lg"></i>
<i className="fa-solid fa-gear fa-lg"></i>
</button>
)}
<button
onClick={() => navigate(-1)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
disabled={isMailSend && true}
data-tut="headerArea"
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
onClick={() => alertSendEmail()}
data-tut="reactourFour"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] font-[500] text-sm mr-[5px]"
style={{
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
color: isMailSend ? "rgb(77, 75, 75)" : "white"
}}
onClick={() => {
alertSendEmail();
}}
>
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
? "Sent"
: "Send"}
</button>
</div>
</>
) : isPdfRequestFiles ? (
alreadySign ? (
<div className="flex flex-row">
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i
className="fa-light fa-award py-[3px]"
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Certificate</span>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
onClick={handleToPrint}
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i
className="fa-light fa-print py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf()}
>
<i
className="fa-light fa-download py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Download</span>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
</div>
) : (
<div className="flex" data-tut="reactourFifth">
<div className="flex">
<button
onClick={() => navigate(-1)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
{currentSigner && (
<>
<button
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
onClick={() => handleDeclinePdfAlert()}
style={{ background: "#de4337" }}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
onClick={() => embedWidgetsData()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
embedWidgetsData();
}}
>
Finish
</button>
<div className="op-dropdown op-dropdown-end">
<div
tabIndex={0}
role="button"
className="op-btn op-btn-info op-btn-sm shadow"
>
<i
className="fa-light fa-ellipsis-v"
aria-hidden="true"
></i>
</div>
<ul
tabIndex={0}
className="op-dropdown-content z-[1] op-menu op-menu-sm p-1 shadow bg-base-100 rounded-box mt-1"
>
<li
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<span className="font-semibold text-[12px]">
<i
className="fa-light fa-arrow-down"
aria-hidden="true"
></i>{" "}
Download
</span>
</li>
</ul>
</div>
</>
)}
</div>
)
) : isCompleted ? (
<div className="flex flex-row">
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i className="fa-light fa-award" aria-hidden="true"></i>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
onClick={handleToPrint}
type="button"
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i className="fa-light fa-print" aria-hidden="true"></i>
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf()}
>
<i className="fa-light fa-download" aria-hidden="true"></i>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
<button
type="button"
className="op-btn op-btn-info op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#3ba7e5]"
onClick={() => setIsEmail(true)}
>
<i className="fa-light fa-envelope" aria-hidden="true"></i>
<i className="fa fa-envelope py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Mail</span>
</button>
</div>
) : (
<div className="flex">
<button
onClick={() => navigate(-1)}
onClick={() => {
navigate(-1);
}}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
@@ -467,27 +571,14 @@ function Header({
)}
</div>
)}
{isDownloading === "pdf" && (
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<ModalUi
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
}
handleClose={() => setIsDownloading("")}
isOpen={isCertificate}
title={"Generating certificate"}
handleClose={() => setIsCertificate(false)}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
{isDownloading === "certificate"}{" "}
<p>
Your completion certificate is being generated. Please wait
momentarily. If the download doesn&apos;t start shortly, click the
button again.
</p>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
<p>Completion certificate is generating,</p>
<p>please wait for some time if not download try again later</p>
</div>
</ModalUi>
</div>
@@ -1,53 +0,0 @@
import React from "react";
function PdfZoom(props) {
const onClickZoomIn = () => {
props.setScale(props.scale + 0.1 * props.scale);
props.setZoomPercent(props.zoomPercent + 10);
};
const onClickZoomOut = () => {
if (props.zoomPercent > 0) {
if (props.zoomPercent === 10) {
props.setScale(1);
} else {
props.setScale(props.scale - 0.1 * props.scale);
}
props.setZoomPercent(props.zoomPercent - 10);
}
};
const handleReset = () => {
props.setScale(1);
props.setZoomPercent(0);
};
return (
// md:mt-[41px] xl:mt-[63px]mt=[]
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[50px] 2xl:mt-[63px]">
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={onClickZoomIn}
title="Zoom in"
>
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={handleReset}
title="Reset"
>
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
onClick={onClickZoomOut}
style={{
cursor: props.zoomPercent > 0 ? "pointer" : "default"
}}
title="Zoom out"
>
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
</span>
</span>
);
}
export default PdfZoom;
+41 -138
View File
@@ -5,7 +5,7 @@ import { Rnd } from "react-rnd";
import {
defaultWidthHeight,
handleCopyNextToWidget,
isTabAndMobile,
isMobile,
onChangeInput,
radioButtonWidget,
textInputWidget,
@@ -88,10 +88,8 @@ const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
function Placeholder(props) {
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
const [containerScale, setContainerScale] = useState();
const [selectDate, setSelectDate] = useState({
date:
props.pos.type === "date"
@@ -121,14 +119,6 @@ function Placeholder(props) {
width: null,
height: null
});
useEffect(() => {
const getPdfPageWidth = props.pdfOriginalWH.find(
(data) => data.pageNumber === props.pageNumber
);
setContainerScale(props.containerWH.width / getPdfPageWidth.width);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.containerWH.width, props.pdfOriginalWH]);
const dateFormatArr = [
"L",
"DD-MM-YYYY",
@@ -303,27 +293,12 @@ function Placeholder(props) {
props?.setShowDropdown(true);
} else if (props.pos.type === "checkbox") {
props?.setIsCheckbox(true);
} else if (
[
textInputWidget,
textWidget,
"name",
"company",
"job title",
"email"
].includes(props.pos.type)
) {
props.handleTextSettingModal(true);
} else {
props?.handleNameModal && props?.handleNameModal(true);
props?.handleNameModal(true);
}
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
} else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId);
props.setUniqueId(props?.data?.Id);
if (props.isPlaceholder && props.type !== textWidget) {
props.setUniqueId(props.data.Id);
}
props.setSignKey(props.pos.key);
props.setWidgetType(props.pos.type);
@@ -400,15 +375,7 @@ function Placeholder(props) {
<>
{(props.isPlaceholder || props.isSignYourself) && (
<>
{[
"checkbox",
textInputWidget,
textWidget,
"name",
"company",
"job title",
"email"
].includes(props.pos.type) && props.isSignYourself ? (
{props.pos.type === "checkbox" && props.isSignYourself ? (
<i
onClick={(e) => {
e.stopPropagation();
@@ -418,7 +385,7 @@ function Placeholder(props) {
e.stopPropagation();
handleOnClickSettingIcon();
}}
className="fa-light fa-gear icon"
className="fa-solid fa-gear settingIcon"
style={{
color: "#188ae2",
right: "29px",
@@ -428,7 +395,9 @@ function Placeholder(props) {
) : (
((!props?.pos?.type && props.pos.isStamp) ||
(props?.pos?.type &&
!["date", "signature"].includes(props.pos.type) &&
!["date", textWidget, "signature"].includes(
props.pos.type
) &&
!props.isSignYourself)) && (
<i
onClick={(e) => {
@@ -439,12 +408,8 @@ function Placeholder(props) {
e.stopPropagation();
handleOnClickSettingIcon();
}}
className="fa-light fa-gear icon"
style={{
color: "#188ae2",
right: props?.pos?.type === textWidget ? "32px" : "47px",
top: "-19px"
}}
className="fa-solid fa-gear settingIcon"
style={{ color: "#188ae2", right: "47px", top: "-19px" }}
></i>
)
)}
@@ -452,7 +417,7 @@ function Placeholder(props) {
{props.pos.type !== textWidget && !props.isSignYourself && (
<i
data-tut="assignSigner"
className="fa-light fa-user icon"
className="fa-regular fa-user signUserIcon"
onClick={(e) => {
e.stopPropagation();
props.handleLinkUser(props.data.Id);
@@ -476,7 +441,11 @@ function Placeholder(props) {
top: "-19px",
right: props.isPlaceholder ? "60px" : "44px"
}}
className={`${isShowDateFormat ? "show" : ""} dropdown icon`}
className={
isShowDateFormat
? "dropdown signUserIcon show"
: "dropdown signUserIcon"
}
onClick={(e) => {
setIsShowDateFormat(!isShowDateFormat);
e.stopPropagation();
@@ -495,7 +464,7 @@ function Placeholder(props) {
}}
>
<i
className="fa-light fa-gear icon"
className="fa-solid fa-gear settingIcon"
style={{
color: "#188ae2",
fontSize: "14px"
@@ -523,7 +492,8 @@ function Placeholder(props) {
setSelectDate(data);
handleSaveDate(data);
}}
className="dropdown-item text-[13px]"
className="dropdown-item itemColor"
style={{ fontSize: "12px" }}
>
{data?.date ? data?.date : "nodata"}
</span>
@@ -533,13 +503,13 @@ function Placeholder(props) {
</div>
)}
<i
className="fa-light fa-copy icon"
className="fa-regular fa-copy signCopy"
onClick={(e) => handleCopyPlaceholder(e)}
onTouchEnd={(e) => handleCopyPlaceholder(e)}
style={{ color: "#188ae2", right: "12px", top: "-18px" }}
></i>
<i
className="fa-light fa-circle-xmark icon"
className="fa-regular fa-circle-xmark signCloseBtn"
onClick={(e) => {
e.stopPropagation();
@@ -565,61 +535,6 @@ function Placeholder(props) {
)
);
};
const xPos = (pos, signYourself) => {
const getPdfPageWidth = props.pdfOriginalWH.find(
(data) => data.pageNumber === props.pageNumber
);
const containerScale = props.containerWH.width / getPdfPageWidth?.width;
const resizePos = pos.xPosition;
if (signYourself) {
return resizePos * containerScale * props.scale;
} else {
//checking both condition mobile and desktop view
if (pos.isMobile && pos.scale) {
if (props.scale > 1) {
return resizePos * pos.scale * containerScale * props.scale;
} else {
return resizePos * pos.scale * containerScale;
}
} else {
// if (pos.scale === containerScale) {
// return resizePos * pos.scale * props.scale;
// } else {
return resizePos * containerScale * props.scale;
// }
}
}
};
const yPos = (pos, signYourself) => {
const getPdfPageWidth = props.pdfOriginalWH.find(
(data) => data.pageNumber === props.pageNumber
);
const containerScale = props.containerWH.width / getPdfPageWidth.width;
const resizePos = pos.yPosition;
if (signYourself) {
return resizePos * containerScale * props.scale;
} else {
//checking both condition mobile and desktop view
if (pos.isMobile && pos.scale) {
if (props.scale > 1) {
return resizePos * pos.scale * containerScale * props.scale;
} else {
return resizePos * pos.scale * containerScale;
}
}
// else if (pos.scale === containerScale) {
// if (props.scale > 1) {
// return resizePos * pos.scale * props.scale;
// } else {
// return resizePos * pos.scale;
// }
// }
else {
return resizePos * containerScale * props.scale;
}
}
};
return (
<Rnd
@@ -705,20 +620,8 @@ function Placeholder(props) {
setDraggingEnabled(true);
props.setIsResize && props.setIsResize(true);
}}
onResizeStop={(e, direction, ref) => {
onResizeStop={() => {
props.setIsResize && props.setIsResize(false);
props.handleSignYourselfImageResize &&
props.handleSignYourselfImageResize(
ref,
props.pos.key,
props.xyPostion,
props.setXyPostion,
props.index,
containerScale,
props.scale,
props.data && props.data.Id,
props.isResize
);
}}
disableDragging={
props.isNeedSign
@@ -731,15 +634,21 @@ function Placeholder(props) {
props.handleStop &&
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
}
position={{
x: xPos(props.pos, props.isSignYourself),
y: yPos(props.pos, props.isSignYourself)
default={{
x: props.xPos(props.pos, props.isSignYourself),
y: props.yPos(props.pos, props.isSignYourself)
}}
onResize={(e, direction, ref) => {
setPlaceholderBorder({
w: ref.offsetWidth / (props.scale * containerScale),
h: ref.offsetHeight / (props.scale * containerScale)
});
props.handleSignYourselfImageResize &&
props.handleSignYourselfImageResize(
ref,
props.pos.key,
props.xyPostion,
props.setXyPostion,
props.index,
props.data && props.data.Id,
false
);
}}
onClick={() => handleOnClickPlaceholder()}
>
@@ -768,23 +677,20 @@ function Placeholder(props) {
pos={props.pos}
isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
scale={props.scale}
containerScale={containerScale}
placeholderBorder={placeholderBorder}
/>
)}
{isTabAndMobile ? (
{isMobile ? (
<div
// className="sm:inline-block md:inline-block lg:hidden "
className="flex items-stretch"
style={{
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
left: props.xPos(props.pos, props.isSignYourself),
top: props.yPos(props.pos, props.isSignYourself),
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
// "auto", //props.posWidth(props.pos, props.isSignYourself),
// height: props.posHeight(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
@@ -793,7 +699,6 @@ function Placeholder(props) {
zIndex: "10"
}}
onTouchEnd={() => handleOnClickPlaceholder()}
onClick={() => handleOnClickPlaceholder()}
>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
@@ -818,7 +723,6 @@ function Placeholder(props) {
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
/>
</div>
) : (
@@ -845,7 +749,6 @@ function Placeholder(props) {
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
/>
</>
)}
@@ -11,34 +11,23 @@ function PlaceholderBorder(props) {
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
const defaultWidth = defaultWidthHeight(props.pos.type).width;
const defaultHeight = defaultWidthHeight(props.pos.type).height;
const width = () => {
const getWidth =
props.placeholderBorder.w || props.pos.Width || defaultWidth;
return (
getWidth * props.scale * props.containerScale + getResizeBorderExtraWidth
);
};
const height = () => {
const getHeight =
props.placeholderBorder.h || props.pos.Height || defaultHeight;
return (
getHeight * props.scale * props.containerScale + getResizeBorderExtraWidth
);
};
const handleMinWidth = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
} else {
return width();
return props.pos.Width
? props.pos.Width + getResizeBorderExtraWidth
: defaultWidth + getResizeBorderExtraWidth;
}
};
const handleMinHeight = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
} else {
return height();
return props.pos.Height
? props.pos.Height + getResizeBorderExtraWidth
: defaultHeight + getResizeBorderExtraWidth;
}
};
return (
@@ -47,7 +36,7 @@ function PlaceholderBorder(props) {
onTouchEnd={() =>
props.pos.type === textWidget && props?.setDraggingEnabled(true)
}
className="absolute inline-block w-[14px] h-[14px] hover:cursor-sw-resize"
className="borderResize"
style={{
borderColor: themeColor,
borderStyle: "dashed",
@@ -1,10 +1,12 @@
import React, { useState } from "react";
import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { randomId, textWidget } from "../../constant/Utils";
function PlaceholderCopy(props) {
const copyType = ["All pages", "All pages but last", "All pages but first"];
const [selectCopyType, setSelectCopyType] = useState("");
//function for get copy placeholder position
const getCopyPlaceholderPosition = (
type,
@@ -193,13 +195,13 @@ function PlaceholderCopy(props) {
props.setIsPageCopy(false);
}}
>
<div className="h-full p-[20px] text-base-content">
<div style={{ height: "100%", padding: 20 }}>
{copyType.map((data, key) => {
return (
<div key={key} className="flex flex-col">
<label className="text-[16px] font-medium">
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
<input
className="mr-[8px] op-radio op-radio-xs"
style={{ accentColor: "red", marginRight: "10px" }}
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
@@ -212,22 +214,33 @@ function PlaceholderCopy(props) {
);
})}
<div className="flex bg-[#9f9f9f] w-full my-[15px]"></div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() => {
handleApplyCopy();
handleUniqueId();
}}
style={{ background: themeColor }}
type="button"
disabled={!selectCopyType}
className="op-btn op-btn-primary"
className="finishBtn"
>
Apply
</button>
<button
type="button"
className="op-btn op-btn-ghost"
onClick={() => handleUniqueId()}
className="finishBtn cancelBtn"
onClick={() => {
handleUniqueId();
}}
>
Cancel
</button>
@@ -2,6 +2,7 @@ import React, { useEffect, useState, forwardRef, useRef } from "react";
import {
getMonth,
getYear,
isMobile,
onChangeHeightOfTextArea,
onChangeInput,
radioButtonWidget,
@@ -13,10 +14,7 @@ import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css";
import RegexParser from "regex-parser";
const textWidgetCls =
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center bg-base-100 whitespace-pre-wrap";
const selectWidgetCls =
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none bg-base-100 text-base-content";
function PlaceholderType(props) {
const type = props?.pos?.type;
const [selectOption, setSelectOption] = useState("");
@@ -139,13 +137,13 @@ function PlaceholderType(props) {
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div
className={`${selectWidgetCls} overflow-hidden`}
style={{ fontSize: calculateFontSize() }}
className="inputPlaceholder"
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
onClick={onClick}
ref={ref}
>
{value}
<i className="fa-light fa-calendar ml-[5px]"></i>
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
</div>
));
ExampleCustomInput.displayName = "ExampleCustomInput";
@@ -306,13 +304,17 @@ function PlaceholderType(props) {
case "signature":
return props.pos.SignUrl ? (
<img
alt="signature"
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
className="w-full h-full "
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
@@ -324,13 +326,17 @@ function PlaceholderType(props) {
case "stamp":
return props.pos.SignUrl ? (
<img
alt="stamp"
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
className="w-full h-full"
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
@@ -346,9 +352,12 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
style={{
// width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
onBlur={handleInputBlur}
disabled={
props.isNeedSign &&
@@ -411,20 +420,19 @@ function PlaceholderType(props) {
false
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: "12px"
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
@@ -433,8 +441,9 @@ function PlaceholderType(props) {
case "dropdown":
return props.data?.signerObjId === props.signerObjId ? (
<select
className={`${selectWidgetCls} text-[12px]`}
className="inputPlaceholder"
id="myDropdown"
style={{ fontSize: "12px" }}
value={selectOption}
onChange={(e) => {
setSelectOption(e.target.value);
@@ -454,7 +463,7 @@ function PlaceholderType(props) {
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values?.map((data, ind) => {
{props.pos?.options?.values.map((data, ind) => {
return (
<option key={ind} value={data}>
{data}
@@ -464,7 +473,7 @@ function PlaceholderType(props) {
</select>
) : (
<div
className={selectWidgetCls}
className="inputPlaceholder"
style={{ fontSize: calculateFontSize() }}
>
{props.pos?.options?.name ? props.pos.options.name : type}
@@ -473,13 +482,17 @@ function PlaceholderType(props) {
case "initials":
return props.pos.SignUrl ? (
<img
alt="initials"
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
className="w-full h-full"
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
@@ -510,20 +523,19 @@ function PlaceholderType(props) {
isDefault
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: "12px"
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
@@ -550,20 +562,19 @@ function PlaceholderType(props) {
false
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: "12px"
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
@@ -590,20 +601,19 @@ function PlaceholderType(props) {
false
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: "12px"
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
@@ -648,7 +658,8 @@ function PlaceholderType(props) {
}
onBlur={handleInputBlur}
closeOnScroll={true}
className={`${selectWidgetCls} outline-[#007bff]`}
className="inputPlaceholder"
style={{ outlineColor: "#007bff" }}
selected={
props?.startDate
? props?.startDate
@@ -671,13 +682,17 @@ function PlaceholderType(props) {
case "image":
return props.pos.SignUrl ? (
<img
alt="image"
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
className="w-full h-full"
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
) : (
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
{props?.handleUserName &&
props?.handleUserName(
props?.data?.Id,
@@ -708,20 +723,19 @@ function PlaceholderType(props) {
false
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }}
cols="50"
/>
) : (
<div
style={{
color: "black",
fontSize: "12px"
fontSize: calculateFontSize()
}}
>
<span>{type}</span>
@@ -734,9 +748,12 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} ${"w-[15px] h-[15px]"} flex justify-center op-radio`}
style={{
width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
type="radio"
disabled={
props.isNeedSign &&
@@ -778,28 +795,37 @@ function PlaceholderType(props) {
false
);
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", overflow: "hidden" }}
cols="50"
/>
);
default:
return props.pos.SignUrl ? (
<div className="pointer-events-none">
<div style={{ pointerEvents: "none" }}>
<img
alt="image"
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
className="w-full h-full"
style={{
width: "99%",
height: "100%",
objectFit: "contain"
}}
/>
</div>
) : (
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
<div
style={{
fontSize: "10px",
color: "black",
justifyContent: "center"
}}
>
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
{props?.handleUserName &&
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
+34 -23
View File
@@ -1,4 +1,5 @@
import React from "react";
import { themeColor } from "../../constant/const";
function PrevNext({ pageNumber, allPages, changePage }) {
//for go to previous page
@@ -11,30 +12,40 @@ function PrevNext({ pageNumber, allPages, changePage }) {
}
return (
<div className="flex items-center">
<button
className="op-btn op-btn-neutral op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<i className="fa-light fa-backward" aria-hidden="true"></i>
<div>
<div className="preBtn1">
<button
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] mr-[5px]"
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<i
className="fa fa-backward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Prev</span>
</button>
<span className="text-xs font-[500]">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<span className="lg:block hidden 2xl:text-[30px]">Prev</span>
</button>
<span className="text-xs text-base-content font-medium mx-2 2xl:text-[30px]">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
className="op-btn op-btn-neutral op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<i className="fa-light fa-forward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden 2xl:text-[30px]">Next</span>
</button>
<button
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] ml-[5px]"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<i
className="fa fa-forward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Next</span>
</button>
</div>
</div>
);
}
+121 -91
View File
@@ -7,14 +7,37 @@ import {
isMobile,
nameColor
} from "../../constant/Utils";
const cursor =
"cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC'),_pointer]";
const RecipientList = (props) => {
const [animationParent] = useAutoAnimate();
const [isHover, setIsHover] = useState();
const [isEdit, setIsEdit] = useState(false);
//function for onhover signer name change background color
const inputRef = useRef(null);
const onHoverStyle = (ind, blockColor) => {
const style = {
background: blockColor ? blockColor : color[ind % color.length],
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
//function for onhover signer name remove background color
const nonHoverStyle = () => {
const style = {
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
const isWidgetExist = (Id) => {
return props.signerPos.some((x) => x.Id === Id);
};
@@ -71,6 +94,7 @@ const RecipientList = (props) => {
props.setRoleName(remainingItems[index]?.Role);
props.setBlockColor(remainingItems[index]?.blockColor);
};
return (
<>
{props.signersdata.length > 0 &&
@@ -89,24 +113,21 @@ const RecipientList = (props) => {
onDrop={(e) =>
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
}
data-tut="recipientArea"
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
className={`${
className={
props.sendInOrder
? props.isMailSend
? "pointer-events-none bg-opacity-80"
: `text-[12px] font-bold ${cursor}`
: props.isMailSend && "pointer-events-none bg-opacity-80"
} flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
style={{
background:
(!isMobile && isHover === ind) || props.isSelectListId === ind
? obj?.blockColor
? obj?.blockColor
: color[ind % color.length]
: "transparent"
}}
? "disabled"
: "dragCursor"
: props.isMailSend && "disabled"
}
style={
(!isMobile && isHover === ind) || props.isSelectListId === ind
? onHoverStyle(ind, obj?.blockColor)
: nonHoverStyle(ind)
}
onClick={(e) => {
e.preventDefault();
props.setSignerObjId(obj?.objectId || "");
@@ -120,18 +141,40 @@ const RecipientList = (props) => {
}
}}
>
<div className="flex flex-row items-center w-full">
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
width: "100%"
}}
>
<div
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
style={{
background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4)
: nameColor[ind % nameColor.length]
: nameColor[ind % nameColor.length],
width: 30,
height: 30,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "12px"
}}
>
<span className="text-white uppercase font-bold text-center text-[12px]">
<span
className={props.sendInOrder && "dragCursor"}
style={{
fontSize: "12px",
textAlign: "center",
fontWeight: "bold",
color: "white",
textTransform: "uppercase"
}}
>
{isWidgetExist(obj.Id) ? (
<i className="fa-light fa-check"></i>
<i className="fa-solid fa-check"></i>
) : (
<>
{obj.Name
@@ -142,74 +185,67 @@ const RecipientList = (props) => {
</span>
</div>
<div
className={`${
obj.Name ? "flex-col" : "flex-row"
} flex items-center`}
style={{
display: "flex",
flexDirection: obj.Name ? "column" : "row",
alignItems: "center"
}}
>
{obj.Name ? (
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj.Name}
</span>
<span className={"userName"}>{obj.Name}</span>
) : (
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
className="bg-transparent p-[3px]"
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => {
setIsEdit({});
props.handleOnBlur(obj.Role, obj.Id);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
inputRef.current.blur();
}
}}
/>
) : (
<span className="text-[13px] 2xl:text-[25px]">
{obj.Role}
</span>
)}
</span>
<>
<span
className="userName"
style={{ cursor: "pointer" }}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
style={{
backgroundColor: "transparent",
width: "inherit",
padding: "3px"
}}
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => {
setIsEdit({});
props.handleOnBlur(obj.Role, obj.Id);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
inputRef.current.blur();
}
}}
/>
) : (
<span style={{ padding: "3px" }}>{obj.Role}</span>
)}
</span>
</>
)}
{obj.Name && (
<span
className={` ${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
<span className={"useEmail"}>
{obj?.Role || obj?.Email}
</span>
)}
</div>
</div>
{isMobile && props.sendInOrder && (
<div className="flex flex-row items-center gap-[5px] mr-2">
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 5
}}
>
<div
onClick={(e) => {
if (ind !== 0) {
@@ -217,7 +253,7 @@ const RecipientList = (props) => {
handleChangeSequence(e, ind, "up");
}
}}
className={ind === 0 ? "text-[gray]" : "text-black"}
style={{ color: ind === 0 ? "gray" : "black" }}
>
</div>
@@ -228,11 +264,10 @@ const RecipientList = (props) => {
handleChangeSequence(e, ind, null, "down");
}
}}
className={
ind === props.signersdata.length - 1
? "text-[gray]"
: "text-black"
}
style={{
color:
ind === props.signersdata.length - 1 ? "gray" : "black"
}}
>
</div>
@@ -244,14 +279,9 @@ const RecipientList = (props) => {
e.stopPropagation();
props.handleDeleteUser(obj.Id);
}}
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} cursor-pointer`}
style={{ cursor: "pointer", marginLeft: "5px" }}
>
<i className="fa-light fa-trash-can 2xl:text-[30px]"></i>
<i className="fa-regular fa-trash-can"></i>
</div>
)}
<hr />
@@ -1,6 +1,7 @@
import React, { useEffect, useRef, useState } from "react";
import React from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf";
import { useSelector } from "react-redux";
import { themeColor } from "../../constant/const";
function RenderAllPdfPage({
signPdfUrl,
@@ -8,104 +9,88 @@ function RenderAllPdfPage({
setAllPages,
setPageNumber,
setSignBtnPosition,
pageNumber,
signerPos,
signerObjectId
pageNumber
}) {
const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState("");
//set all number of pages after load pdf
function onDocumentLoad({ numPages }) {
setAllPages(numPages);
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
if (signerPos) {
const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
setBookmarkColor(checkUser[0]?.blockColor);
let pageNumberArr = [];
if (checkUser?.length > 0) {
checkUser[0]?.placeHolder?.map((data) => {
pageNumberArr.push(data?.pageNumber);
});
setSignPageNumber(pageNumberArr);
}
}
}
const pageContainer = useRef();
const isHeader = useSelector((state) => state.showHeader);
const [pageWidth, setPageWidth] = useState("");
useEffect(() => {
const updateSize = () => {
if (pageContainer.current) {
setPageWidth(pageContainer.current.offsetWidth);
}
};
// Use setTimeout to wait for the transition to complete
const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer);
}, [isHeader, pageContainer]);
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
const addSignatureBookmark = (index) => {
const ispageNumber = signPageNumber.includes(index + 1);
return (
ispageNumber && (
<div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
<i
style={{ color: bookmarkColor || "red" }}
className="fa-light fa-bookmark"
></i>
</div>
)
);
};
return (
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Pages
</div>
<div className="showPages">
<div
className={`flex h-[90%] flex-col items-center m-2
autoSignScroll hide-scrollbar max-h-[100vh] `}
style={{
display: "flex",
flexDirection: "row",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
backgroundColor: "white",
height: "100%"
}}
>
<Document
loading={"Loading Document.."}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
className={`${
pageNumber - 1 === index ? "border-[red]" : "border-[#878787]"
} border-2 m-[10px] flex justify-center items-center relative`}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
<div style={{ width: "140px" }}>
<div
style={{
background: themeColor
// color:"white"
}}
className="signedStyle"
>
Pages
</div>
<div>
<RSC
id="RSC-Example"
style={{
width: "135px",
height: window.innerHeight - 130 + "px"
}}
>
{signerPos && addSignatureBookmark(index)}
<Document
loading={"Loading Document.."}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
style={{
width: "100px",
border:
pageNumber - 1 === index
? "2px solid red"
: "2px solid #878787",
// padding: "5px",
margin: "10px",
<div className="relative z-[1] overflow-hidden">
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={pageWidth - 60}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
</div>
))}
</Document>
display: "flex",
justifyContent: "center",
alignItems: "center",
contain: "content"
}}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
}}
>
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={100}
height={100}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
))}
</Document>
</RSC>
</div>
<hr />
</div>
</div>
</div>
);
+271 -186
View File
@@ -11,6 +11,8 @@ import Alert from "../../primitives/Alert";
function RenderPdf({
pageNumber,
pdfOriginalWidth,
pdfNewWidth,
drop,
signerPos,
successEmail,
@@ -24,13 +26,15 @@ function RenderPdf({
pdfDetails,
xyPostion,
pdfUrl,
numPages,
pageDetails,
pdfRequest,
setCurrentSigner,
signerObjectId,
signedSigners,
pdfLoad,
setPdfLoad,
setPdfLoadFail,
placeholder,
pdfLoadFail,
setSignerPos,
setXyPostion,
index,
@@ -53,105 +57,183 @@ function RenderPdf({
unSignedWidgetId,
setIsCheckbox,
handleNameModal,
handleTextSettingModal,
setTempSignerId,
uniqueId,
pdfOriginalWH,
scale
uniqueId
}) {
const isMobile = window.innerWidth < 767;
const newWidth = containerWH.width;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
// handle signature block width and height according to screen
const posWidth = (pos, signYourself) => {
const getPdfPageWidth = pdfOriginalWH.find(
(data) => data.pageNumber === pageNumber
);
const containerScale = containerWH.width / getPdfPageWidth.width || 1;
const defaultWidth = defaultWidthHeight(pos.type).width;
const posWidth = pos.Width ? pos.Width : defaultWidth;
let width;
if (signYourself) {
return posWidth * scale * containerScale;
width = posWidth;
return width;
} else {
if (pos.isMobile && pos.scale) {
if (pos.IsResize) {
if (scale > 1) {
return posWidth * pos.scale * containerScale * scale;
if (isMobile) {
if (!pos.isMobile) {
if (pos.IsResize) {
width = posWidth ? posWidth : defaultWidth;
return width;
} else {
return posWidth * containerScale;
width = (posWidth || defaultWidth) / scale;
return width;
}
} else {
if (scale > 1) {
return posWidth * pos.scale * containerScale * scale;
} else {
return posWidth * pos.scale * containerScale;
}
width = posWidth;
return width;
}
} else {
return posWidth * scale * containerScale;
if (pos.isMobile) {
if (pos.IsResize) {
width = posWidth ? posWidth : defaultWidth;
return width;
} else {
width = (posWidth || defaultWidth) * pos.scale;
return width;
}
} else {
width = posWidth ? posWidth : defaultWidth;
return width;
}
}
}
};
const posHeight = (pos, signYourself) => {
const getPdfPageWidth = pdfOriginalWH.find(
(data) => data.pageNumber === pageNumber
);
const containerScale = containerWH.width / getPdfPageWidth?.width || 1;
let height;
const posHeight = pos.Height;
const defaultHeight = defaultWidthHeight(pos.type).height;
const posHeight = pos.Height || defaultWidthHeight(pos.type).height;
if (signYourself) {
return posHeight * scale * containerScale;
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
if (pos.isMobile && pos.scale) {
if (pos.IsResize) {
if (scale > 1) {
return posHeight * pos.scale * containerScale * scale;
if (isMobile) {
if (!pos.isMobile) {
if (pos.IsResize) {
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
return posHeight * containerScale;
height = (posHeight || defaultHeight) / scale;
return height;
}
} else {
if (scale > 1) {
return posHeight * pos.scale * containerScale * scale;
} else {
return posHeight * pos.scale * containerScale;
}
height = posHeight ? posHeight : defaultHeight;
return height;
}
} else {
return posHeight * scale * containerScale;
if (pos.isMobile) {
if (pos.IsResize) {
height = posHeight ? posHeight : defaultHeight;
return height;
} else {
height = (posHeight || defaultHeight) * pos.scale;
return height;
}
} else {
height = posHeight ? posHeight : defaultHeight;
return height;
}
}
}
};
const xPos = (pos, signYourself) => {
const resizePos = pos.xPosition;
if (signYourself) {
return resizePos;
} else {
//checking both condition mobile and desktop view
if (isMobile) {
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
if (!pos.isMobile) {
return resizePos / scale;
}
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
else {
return resizePos * (pos.scale / scale);
}
} else {
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
if (pos.isMobile) {
return pos.scale && resizePos * pos.scale;
}
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
else {
return resizePos;
}
}
}
};
const yPos = (pos, signYourself) => {
const resizePos = pos.yPosition;
if (signYourself) {
return resizePos;
} else {
//checking both condition mobile and desktop view
if (isMobile) {
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
if (!pos.isMobile) {
return resizePos / scale;
}
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
else {
return resizePos * (pos.scale / scale);
}
} else {
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
if (pos.isMobile) {
return pos.scale && resizePos * pos.scale;
}
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
else {
return resizePos;
}
}
}
};
//function for render placeholder block over pdf document
const checkSignedSigners = (data) => {
let checkSign = [];
//condition to handle quick send flow and using normal request sign flow
checkSign = signedSigners
? signedSigners?.filter(
(sign) =>
sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
)
: [];
const checkSignedSignes = (data) => {
const checkSign = signedSigners.filter(
(sign) => sign.objectId === data.signerObjId
);
if (data.signerObjId === signerObjectId) {
setCurrentSigner(true);
}
const handleAllUserName = (Id, Role, type) => {
return (
<React.Fragment>
<div className="text-black text-[8px] font-bold">
{
pdfDetails[0].Signers?.find(
(signer) => signer.objectId === data.signerObjId
)?.Name
}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{Role && (
<div className="text-black text-[8px] font-medium">
{`(${Role})`}
</div>
)}
</React.Fragment>
);
return pdfDetails[0].Signers.map((signerData, key) => {
return (
signerData.objectId === data.signerObjId && (
<React.Fragment key={key}>
{signerData?.Name && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{signerData.Name}
</div>
)}
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
{Role && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
)}
</React.Fragment>
)
);
});
};
return (
checkSign.length === 0 &&
data.placeHolder.map((placeData, key) => {
@@ -178,6 +260,8 @@ function RenderPdf({
isShowDropdown={true}
isNeedSign={true}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
handleUserName={handleAllUserName}
@@ -189,11 +273,6 @@ function RenderPdf({
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setCurrWidgetsDetails={setCurrWidgetsDetails}
uniqueId={uniqueId}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
</React.Fragment>
)
@@ -204,7 +283,6 @@ function RenderPdf({
})
);
};
//function for render placeholder block over pdf document
const handleUserName = (Id, Role, type) => {
if (Id) {
@@ -212,12 +290,14 @@ function RenderPdf({
if (checkSign?.Name) {
return (
<>
<div className="text-black text-[8px] font-medium">
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{checkSign?.Name}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
{Role && (
<div className="text-black text-[8px] font-medium">
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
)}
@@ -226,39 +306,47 @@ function RenderPdf({
} else {
return (
<>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
</>
);
}
} else {
return (
<>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
</>
);
}
};
return (
<>
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{isMobile ? (
{isMobile && scale ? (
<div
data-tut="reactourForth"
className={`${
isGuestSigner ? "30px" : ""
} border-[0.1px] border-[#ebe8e8]`}
style={{
border: "0.1px solid #ebe8e8",
marginTop: isGuestSigner && "30px"
}}
ref={drop}
id="container"
>
{containerWH?.width &&
pdfOriginalWH.length > 0 &&
{pdfLoadFail.status &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSigners(data)}
{checkSignedSignes(data)}
</React.Fragment>
);
})
@@ -297,6 +385,8 @@ function RenderPdf({
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
@@ -312,13 +402,6 @@ function RenderPdf({
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
</React.Fragment>
);
@@ -350,10 +433,13 @@ function RenderPdf({
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
pdfOriginalWidth={pdfOriginalWidth}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
@@ -366,12 +452,6 @@ function RenderPdf({
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
)
);
@@ -381,10 +461,16 @@ function RenderPdf({
}))}
{/* this component for render pdf document is in middle of the component */}
<div className="flex items-center justify-center">
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Document
onLoadError={() => {
setPdfLoad(false);
setPdfLoadFail(true);
}}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
@@ -402,16 +488,19 @@ function RenderPdf({
: pdfDetails[0].URL
}
>
<Page
key={index}
pageNumber={pageNumber}
width={containerWH.width}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
{Array.from(new Array(numPages), (el, index) => (
<Page
key={index}
pageNumber={pageNumber}
width={containerWH.width}
height={containerWH.height}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
))}
</Document>
</div>
</div>
@@ -420,31 +509,31 @@ function RenderPdf({
style={{
position: "relative",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
height: window.innerHeight + "px",
zIndex: 0
width:
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
height: window.innerHeight - 110 + "px"
}}
noScrollY={false}
noScrollX={scale === 1 ? true : false}
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
>
<div
style={{
width: containerWH.width * scale
border: "0.1px solid #ebe8e8",
width: pdfOriginalWidth
}}
ref={drop}
id="container"
>
{pdfLoad &&
containerWH?.width &&
pdfOriginalWH.length > 0 &&
(pdfRequest //pdf request sign flow
? signerPos?.map((data, key) => {
{pdfLoadFail.status &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSigners(data)}
{checkSignedSignes(data)}
</React.Fragment>
);
})
: placeholder //placeholder and template flow
: placeholder
? signerPos.map((data, ind) => {
return (
<React.Fragment key={ind}>
@@ -479,6 +568,8 @@ function RenderPdf({
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
@@ -494,13 +585,6 @@ function RenderPdf({
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
</React.Fragment>
);
@@ -512,55 +596,53 @@ function RenderPdf({
);
})
: xyPostion.map((data, ind) => {
// signyourself flow
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={(event, dragElement) =>
handleStop(event, dragElement, pos.type)
}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
</React.Fragment>
pos && (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={(event, dragElement) =>
handleStop(event, dragElement, pos.type)
}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
pdfOriginalWidth={pdfOriginalWidth}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
xPos={xPos}
yPos={yPos}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
/>
</React.Fragment>
)
);
})}
</React.Fragment>
@@ -570,7 +652,11 @@ function RenderPdf({
{/* this component for render pdf document is in middle of the component */}
<Document
onLoadError={() => {
pdfLoad(false);
const load = {
status: false,
type: "failed"
};
setPdfLoadFail(load);
}}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
@@ -588,18 +674,17 @@ function RenderPdf({
: pdfDetails[0].URL
}
>
<Page
key={index}
width={containerWH.width}
scale={scale || 1}
className={"-z-[1]"} // when user zoom-in in tablet widgets move backward that's why pass -z-[1]
pageNumber={pageNumber}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
{Array.from(new Array(numPages), (el, index) => (
<Page
key={index}
pageNumber={pageNumber}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
))}
</Document>
</div>
</RSC>
+183 -83
View File
@@ -1,5 +1,6 @@
import React, { useRef, useState, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas";
import { themeColor } from "../../constant/const";
function SignPad({
isSignPad,
@@ -61,6 +62,7 @@ function SignPad({
} else if (isStamp) {
setImage("");
}
setIsSignImg("");
} else if (isTab === "uploadImage") {
setImage("");
@@ -68,23 +70,29 @@ function SignPad({
// setIsInitial(false);
};
//function for set signature url
const handleSignatureChange = (data) => {
setSignature(data);
setIsSignImg(data);
const handleSignatureChange = () => {
setSignature(canvasRef.current.toDataURL());
setIsSignImg(canvasRef.current.toDataURL());
};
//save button component
const SaveBtn = () => {
return (
<div>
<div style={{ marginTop: "2px" }}>
{(isTab === "draw" || isTab === "uploadImage") && (
<button
style={{
color: "black",
border: "1px solid #ccc"
}}
type="button"
className="op-btn op-btn-ghost mr-1 mt-[2px]"
className="finishBtn saveBtn"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
setCurrWidgetsDetails({});
@@ -101,8 +109,6 @@ function SignPad({
setIsSignImg("");
onSaveSign(null, false, textWidth, textHeight);
} else {
setIsSignImg("");
canvasRef.current.clear();
onSaveSign(signatureType);
}
}
@@ -121,21 +127,18 @@ function SignPad({
setSignValue("");
setIsStamp(false);
}}
type="button"
className={`${
isSignImg || image || isDefaultSign || textWidth
? ""
: "pointer-events-none"
} op-btn op-btn-primary shadow-lg`}
style={{
background: themeColor,
color: "white"
}}
disabled={
(isTab === "draw" && isSignImg) ||
(isTab === "image" && image) ||
(isTab === "mysignature" && isDefaultSign) ||
(isTab === "type" && textWidth)
? false
: image
? false
: true
isSignImg || image || isDefaultSign || textWidth ? false : true
}
type="button"
className={
isSignImg || image || isDefaultSign || textWidth
? "finishBtn saveBtn"
: "disabledFinish saveBtn"
}
>
Save
@@ -203,6 +206,7 @@ function SignPad({
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isTab]);
//function for convert input text value in image
const convertToImg = async (fontStyle, text, color) => {
//get text content to convert in image
@@ -210,8 +214,8 @@ function SignPad({
const fontfamily = fontStyle
? fontStyle
: fontSelect
? fontSelect
: "Fasthand";
? fontSelect
: "Fasthand";
//creating span for getting text content width
const span = document.createElement("span");
@@ -252,7 +256,7 @@ function SignPad({
const PenColorComponent = (props) => {
return (
<div className="flex flex-row items-start">
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
@@ -263,10 +267,10 @@ function SignPad({
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
props?.convertToImg &&
@@ -280,7 +284,7 @@ function SignPad({
}
}}
key={key}
className="fa-light fa-pen-nib"
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
@@ -292,14 +296,33 @@ function SignPad({
return (
<div>
{isSignPad && (
<div className="op-modal op-modal-open">
<div className="op-modal-box px-[13px] pt-2 pb-0">
<div className="flex justify-between text-base-content items-center">
<div className="text-[1.2rem]">
<div className="flex flex-row justify-between mt-[3px]">
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
<div className="modaloverlay">
<div className="modalcontainer">
<div
className="modalheader"
style={{ padding: "0 13px", marginTop: "5px" }}
>
<div className="modaltitle">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
background: "white",
marginTop: "3px"
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "end",
gap: 10
}}
>
{isStamp ? (
<span className="op-link-primary op-link">
<span style={{ color: themeColor }} className="signTab">
{widgetType === "image"
? "Upload image"
: "Upload stamp image"}
@@ -313,18 +336,24 @@ function SignPad({
setIsImageSelect(false);
setIsTab("draw");
setImage();
if (isSignImg) {
setSignature(isSignImg);
}
}}
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
style={{
color: isTab === "draw" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
>
Draw
</span>
<div
style={{
border:
isTab === "draw"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
@@ -334,14 +363,22 @@ function SignPad({
setIsTab("uploadImage");
setSignatureType("");
}}
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
style={{
color:
isTab === "uploadImage" ? themeColor : "#515252"
}}
className="signTab"
>
Upload Image
</span>
<div
style={{
border:
isTab === "uploadImage"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
@@ -352,14 +389,23 @@ function SignPad({
setSignatureType("");
setImage();
}}
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
style={{
color: isTab === "type" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
>
Type
</span>
<div
style={{
border:
isTab === "type"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
{!isInitial && defaultSign ? (
<div>
@@ -371,14 +417,24 @@ function SignPad({
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
>
My Signature
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
) : (
isInitial &&
@@ -392,14 +448,24 @@ function SignPad({
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
>
My Initials
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
)
)}
@@ -409,7 +475,8 @@ function SignPad({
</div>
</div>
<div
className="text-[1.5rem] cursor-pointer"
className="closebtn"
style={{ color: "black" }}
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
@@ -426,10 +493,10 @@ function SignPad({
&times;
</div>
</div>
<div className="p-[20px] h-full">
<div style={{ height: "100%", padding: 20 }}>
{isDefaultSign ? (
<>
<div className="flex justify-center">
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
@@ -447,19 +514,24 @@ function SignPad({
>
<img
alt="stamp img"
className="w-full h-full object-contain bg-white"
style={{
width: "100%",
height: "100%",
background: "rgb(255, 255, 255)",
objectFit: "contain"
}}
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
</div>
<div className="flex justify-end">
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div className="flex justify-center">
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
@@ -484,13 +556,13 @@ function SignPad({
ref={imageRef}
hidden
/>
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
<div className="text-[10px]">Upload</div>
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
<div className="uploadImg">Upload</div>
</div>
</div>
) : (
<>
<div className="flex justify-center">
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{
border: "1.3px solid #007bff",
@@ -509,26 +581,39 @@ function SignPad({
ref={imageRef}
src={image.src}
draggable="false"
className=" object-contain h-full w-full"
style={{
objectFit: "contain",
height: "100%",
width: "100%"
}}
/>
</div>
</div>
<div className="flex justify-end">
<div
style={{ display: "flex", justifyContent: "flex-end" }}
>
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div className="flex justify-between items-center">
<span className="mr-[5px] text-[12px]">
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}}
>
<span className="signatureText">
{isInitial ? "Initials" : "Signature"}:
</span>
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
className="signatureInput"
placeholder="Your signature"
value={signValue}
onChange={(e) => {
@@ -537,7 +622,8 @@ function SignPad({
}}
/>
</div>
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
<div className="fontOptionContainer">
{fontOptions.map((font, ind) => {
return (
<div
@@ -567,14 +653,21 @@ function SignPad({
})}
</div>
<div className="flex flex-row justify-between mt-[10px]">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
) : (
<>
<div className="flex justify-center">
<div style={{ display: "flex", justifyContent: "center" }}>
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
@@ -589,12 +682,19 @@ function SignPad({
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current?.toDataURL())
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
/>
</div>
<div className="flex flex-row justify-between mt-[10px]">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<PenColorComponent />
<SaveBtn />
</div>
+42 -13
View File
@@ -1,4 +1,5 @@
import React from "react";
import { themeColor } from "../../constant/const";
import "../../styles/signature.css";
function Signedby({ pdfDetails }) {
const getFirstLetter = (name) => {
@@ -7,24 +8,52 @@ function Signedby({ pdfDetails }) {
};
return (
<div className="hidden md:block w-full h-full bg-base-100">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<div className="signerComponent">
<div
style={{
background: themeColor,
color: "white"
}}
className="signedStyle"
>
Signed By
</div>
<div className="mt-[2px] bg-base-100">
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
<div className="bg-[#576081] flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1">
<span className="text-[12px] text-center font-bold text-white uppercase">
<div style={{ marginTop: "2px", background: "white" }}>
<div
style={{
display: "flex",
flexDirection: "row",
padding: "10px",
background: "#93a3db"
}}
>
<div
className="signerStyle"
style={{
background: "#abd1d0",
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px"
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold"
}}
>
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
</span>
</div>
<div className="flex flex-col">
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
{pdfDetails.ExtUserPtr.Name}
</span>
<span className="text-[10px] font-medium text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
{pdfDetails.ExtUserPtr.Email}
</span>
<div style={{ display: "flex", flexDirection: "column" }}>
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
</div>
</div>
</div>
@@ -1,53 +0,0 @@
import React from "react";
import { darkenColor, getFirstLetter } from "../../constant/Utils";
function SignerListComponent(props) {
const checkSignerBackColor = (obj) => {
if (obj) {
let data = "";
if (obj?.Id) {
data = props.signerPos.filter((data) => data.Id === obj.Id);
} else {
data = props.signerPos.filter(
(data) => data.signerObjId === obj.objectId
);
}
return data && data.length > 0 && data[0].blockColor;
}
};
const checkUserNameColor = (obj) => {
const getBackColor = checkSignerBackColor(obj);
if (getBackColor) {
const color = darkenColor(getBackColor, 0.4);
return color;
} else {
return "#abd1d0";
}
};
return (
<div
className="rounded-xl mx-1 flex flex-row items-center py-[10px] mt-1"
style={{ background: checkSignerBackColor(props.obj) }}
>
<div
style={{ background: checkUserNameColor(props.obj) }}
className="flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1"
>
<span className="text-[12px] text-center font-bold text-black uppercase">
{getFirstLetter(props?.obj?.Name || props?.obj?.Role)}
</span>
</div>
<div className="flex flex-col">
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
{props.obj?.Name || props?.obj?.Role}
</span>
<span className="text-[10px] font-medium text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
{props.obj?.Email || props.obj?.email}
</span>
</div>
</div>
);
}
export default SignerListComponent;
@@ -1,26 +1,39 @@
import React from "react";
import { themeColor } from "../../constant/const";
import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip";
function SignerListPlace(props) {
return (
<div>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span className="relative">
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">
{props.title ? props.title : "Recipients"}
<span className="absolute text-xs z-[30] mt-1 ml-0.5">
{props?.title === "Roles" && (
<>
<a data-tooltip-id="my-tooltip">
<sup>
<i className="fa-light fa-question rounded-full border-[1px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
<i
className="fa-solid fa-question rounded-full"
style={{
borderColor: "white",
color: "white",
fontSize: 11,
borderWidth: 1.5,
padding: "1px 3px"
}}
></i>
</sup>
</a>
<Tooltip id="my-tooltip" className="z-[100]">
<div className="max-w-[450px] 2xl:max-w-[500px] 2xl:text-[20px] p-[1px]">
<p className="font-bold pb-[1px]">
What are template roles?
</p>
<Tooltip id="my-tooltip">
<div className="max-w-[450px]">
<p className="font-bold">What are template roles?</p>
<p>
Begin by specifying each role needed for the completion of
the document. Think about the parties involved in the
@@ -55,32 +68,36 @@ function SignerListPlace(props) {
</span>
</span>
</div>
<div className="overflow-auto hide-scrollbar max-h-[180px]">
<div className="signerList">
<RecipientList {...props} />
</div>
<div className="mx-1">
{props.handleAddSigner ? (
<div
role="button"
data-tut="reactourAddbtn"
disabled={props?.isMailSend ? true : false}
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
onClick={() => props.handleAddSigner()}
>
<i className="fa-light fa-plus"></i> Add role
</div>
) : (
<div
role="button"
data-tut="addRecipient"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false}
onClick={() => props.setIsAddSigner(true)}
>
<i className="fa-light fa-plus"></i> Add recipients
</div>
)}
</div>
{props.handleAddSigner ? (
<div
data-tut="reactourAddbtn"
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => props.handleAddSigner()}
style={{
opacity: props.isMailSend && "0.5",
pointerEvents: props.isMailSend && "none"
}}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
) : (
<div
data-tut="reactourAddbtn"
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => props.setIsAddSigner(true)}
style={{
opacity: props.isMailSend && "0.5",
pointerEvents: props.isMailSend && "none"
}}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add recipients</span>
</div>
)}
</div>
);
}
@@ -1,75 +0,0 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
function TextFontSetting(props) {
return (
<ModalUi
headerColor={"#dc3545"}
isOpen={props.isTextSetting}
reduceWidth={"max-w-[350px]"}
title={"Text field"}
handleClose={() => {
props.setIsTextSetting(false);
}}
>
<div className="h-full p-[20px]">
<div className="flex items-center">
<span>Font size:</span>
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
props.fontSize || props.currWidgetsDetails?.options?.fontSize
}
onChange={(e) => props.setFontSize(e.target.value)}
>
{fontsizeArr.map((size, ind) => {
return (
<option className="text-[13px]" value={size} key={ind}>
{size}
</option>
);
})}
</select>
<div className="flex flex-row gap-1 items-center ml-4">
<span>color: </span>
<select
value={
props.fontColor || props.currWidgetsDetails?.options?.fontColor
}
onChange={(e) => props.setFontColor(e.target.value)}
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
>
{fontColorArr.map((color, ind) => {
return (
<option value={color} key={ind}>
{color}
</option>
);
})}
</select>
<span
style={{
background:
props.fontColor ||
props.currWidgetsDetails?.options?.fontColor
}}
className="w-5 h-[19px] ml-1"
></span>
</div>
</div>
<div className="h-[1px] bg-[#9f9f9f] w-full mt-[15px] mb-2"></div>
<button
onClick={() => props.handleSaveFontSize()}
type="button"
className="op-btn op-btn-primary mt-2"
>
save
</button>
</div>
</ModalUi>
);
}
export default TextFontSetting;
+33 -15
View File
@@ -1,13 +1,16 @@
import React from "react";
import Loader from "../../primitives/Loader";
import { rejectBtn, submitBtn, themeColor } from "../../constant/const";
function VerifyEmail(props) {
return (
<dialog className="op-modal op-modal-open absolute z-[1999]">
<div className="md:w-[40%] w-[80%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="font-bold text-lg pt-[15px] px-[20px] text-base-content">
<div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded">
<div className="bg-white rounded outline-none md:w-[40%] w-[80%]">
<div
style={{ backgroundColor: themeColor }}
className=" text-white p-[10px] rounded-t"
>
OTP verification
</h3>
</div>
{props.isVerifyModal ? (
<form
onSubmit={(e) => {
@@ -15,24 +18,25 @@ function VerifyEmail(props) {
props.handleVerifyEmail(e);
}}
>
<div className="px-6 py-3 text-base-content">
<div className="px-6 py-3">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter OTP received over email"
value={props.otp}
onChange={(e) => props.setOtp(e.target.value)}
/>
</div>
<hr />
<div className="px-6 my-3">
<button type="submit" className="op-btn op-btn-primary">
<button type="submit" className={submitBtn}>
Verify
</button>
<button
className="op-btn op-btn-secondary ml-2"
className={`${rejectBtn} ml-2`}
onClick={(e) => props.handleResend(e)}
>
Resend
@@ -40,15 +44,29 @@ function VerifyEmail(props) {
</div>
</form>
) : props.otpLoader ? (
<div className="h-[150px] flex items-center justify-center">
<Loader />
<div
style={{
height: "150px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="px-6 py-3 text-base-content">
<p className="mb-2 text-sm">Please verify your email !</p>
<div className="px-6 py-3">
<p className="mb-2">Please verify your email !</p>
<hr />
<div className="px-0 mt-3">
<button
className="op-btn op-btn-primary"
className={submitBtn}
type="submit"
onClick={() => {
props.handleVerifyBtn();
@@ -60,7 +78,7 @@ function VerifyEmail(props) {
</div>
)}
</div>
</dialog>
</div>
);
}
@@ -9,6 +9,7 @@ import {
textWidget,
widgets
} from "../../constant/Utils";
import { themeColor } from "../../constant/const";
function WidgetComponent({
dragSignature,
signRef,
@@ -250,87 +251,122 @@ function WidgetComponent({
{isMobile ? (
!isMailSend && (
<div
data-tut={dataTut}
id="navbar"
className="fixed z-[99] bottom-0 right-0"
className="stickyfooter"
style={{ width: window.innerWidth + "px" }}
>
<div className="mx-1">
{isSigners && (
<div
data-tut="recipientArea"
className="py-[10px] flex justify-center items-center op-card"
{isSigners && (
<div
data-tut={dataTut}
style={{
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0],
padding: "10px 20px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
onClick={() => handleModal()}
>
<span
style={{
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0]
fontSize: "13px",
fontWeight: "700",
textAlign: "center"
}}
onClick={() => handleModal()}
>
<span className="text-[13px] font-bold flex items-center">
{title ? title : "Recipient"}
{signersdata[isSelectListId]?.Role && (
<div>
{signersdata[isSelectListId]?.Name ? (
<>
:{" "}
{signersdata[isSelectListId]?.Name?.length > 12
? `${signersdata[isSelectListId].Name.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Name}
</>
) : (
<>
:{" "}
{signersdata[isSelectListId]?.Role?.length > 12
? `${signersdata[isSelectListId].Role.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Role}
</>
)}
</div>
)}
<div className="ml-[6px] text-[16px]">
<i className="fa-light fa-angle-down"></i>
{title ? title : "Recipient"}
</span>
<span
style={{
fontSize: "13px",
fontWeight: "700",
display: "flex",
alignItems: "center"
}}
>
{signersdata[isSelectListId]?.Role && (
<div>
{signersdata[isSelectListId]?.Name ? (
<>
:{" "}
{signersdata[isSelectListId]?.Name?.length > 12
? `${signersdata[isSelectListId].Name.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Name}
</>
) : (
<>
:{" "}
{signersdata[isSelectListId]?.Role?.length > 12
? `${signersdata[isSelectListId].Role.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Role}
</>
)}
</div>
</span>
</div>
)}
</div>
<div className="mx-1">
{handleAddSigner ? (
)}
<div style={{ marginLeft: 6, fontSize: 16 }}>
<i className="fa-solid fa-angle-down"></i>
</div>
</span>
</div>
)}
{handleAddSigner ? (
<div
data-tut="reactourAddbtn"
style={{
margin: "5px 0 5px 0",
backgroundColor: themeColor,
color: "white"
}}
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
) : (
setIsAddSigner && (
<div
data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => handleAddSigner()}
style={{
margin: "5px 0 5px 0",
backgroundColor: themeColor,
color: "white"
}}
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => setIsAddSigner(true)}
>
<i className="fa-light fa-plus"></i>
<span>Add role</span>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add recipients</span>
</div>
) : (
setIsAddSigner && (
<div
data-tut="addRecipient"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => setIsAddSigner(true)}
>
<i className="fa-light fa-plus"></i>
<span>Add recipients</span>
</div>
)
)}
</div>
)
)}
<div
data-tut="addWidgets"
ref={scrollContainerRef}
className="bg-base-100 border-[2px] border-t-primary"
style={{
background: "white",
borderTop: `2px solid ${themeColor}`
}}
>
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
<div
style={{
display: "flex",
overflowX: "scroll",
whiteSpace: "nowrap",
padding: "10px 5px 10px 1px"
}}
>
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
@@ -347,18 +383,20 @@ function WidgetComponent({
) : (
<div
data-tut={dataTut}
className={`${
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
} hidden md:block h-full bg-base-100`}
className={
isMailSend ? "disabled signerComponent " : "signerComponent"
}
>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>Fields</span>
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">Fields</span>
</div>
<div
className="p-[15px] flex flex-col pt-4 2xl:m-5"
data-tut="addWidgets"
>
<div className="signLayoutContainer1">
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
@@ -375,7 +413,7 @@ function WidgetComponent({
handleClose={handleModal}
>
{signersdata.length > 0 ? (
<div className="max-h-[600px] overflow-auto pb-1">
<div style={{ maxHeight: "600px", overflow: "auto" }}>
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
@@ -395,7 +433,14 @@ function WidgetComponent({
/>
</div>
) : (
<div className=" p-[20px] text-[15px] font-medium text-center">
<div
style={{
padding: 20,
fontSize: 15,
fontWeight: "500",
textAlign: "center"
}}
>
Please add a {title ? title : "recipients"}
</div>
)}
@@ -4,9 +4,9 @@ import { getWidgetType } from "../../constant/Utils";
function WidgetList(props) {
return props.updateWidgets.map((item, ind) => {
return (
<div className="2xl:p-3 mb-[5px]" key={ind}>
<div key={ind} style={{ marginBottom: "5px" }}>
<div
className="select-none mx-[2px] md:mx-0 cursor-all-scroll"
className="widgets"
onClick={() => {
props.addPositionOfSignature &&
props.addPositionOfSignature("onclick", item);
@@ -22,10 +22,11 @@ function WidgetList(props) {
}
}}
onMouseMove={props?.handleDivClick}
onMouseDown={() => props?.handleMouseLeave()}
onTouchStart={props?.handleDivClick}
onMouseDown={() => {
props?.handleMouseLeave();
}}
>
{item.ref && getWidgetType(item)}
{item.ref && getWidgetType(item, props?.marginLeft)}
</div>
</div>
);
@@ -6,7 +6,6 @@ import { textInputWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip";
const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({
@@ -122,15 +121,15 @@ const WidgetNameModal = (props) => {
props.widgetName === textInputWidget
? "pt-0"
: ""
} p-[20px] text-base-content`}
} p-[20px]`}
>
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Name
<span className="text-[red]"> *</span>
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
@@ -140,41 +139,39 @@ const WidgetNameModal = (props) => {
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) && (
<>
<div className="mb-[0.75rem]">
<div className="flex items-center gap-1">
<label
htmlFor="textvalidate"
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} text-[13px]`}
>
Validation
</label>
<Tooltip
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
/>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
</div>
<div
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} flex flex-row gap-[10px] mb-[0.5rem]`}
<div className="form-section">
<label
htmlFor="textvalidate"
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{ fontSize: 13 }}
>
<div className="w-full relative group">
Validation
</label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
<div
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
>
<div style={{ width: "100%", position: "relative" }}>
<input
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
name="textvalidate"
placeholder="Enter custom regular expression"
placeholder="Enter custom expression"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
// onBlur={() => handleBlurRegex()}
/>
<select
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
className="validationlist addUserInput"
name="textvalidate"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
@@ -182,13 +179,17 @@ const WidgetNameModal = (props) => {
>
<option
disabled={formdata?.textvalidate}
className="text-[13px]"
style={{ fontSize: "13px" }}
>
Select...
</option>
{inputOpt.map((data, ind) => {
return (
<option className="text-[13px]" key={ind} value={data}>
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
{data}
</option>
);
@@ -198,12 +199,12 @@ const WidgetNameModal = (props) => {
</div>
</div>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Default value
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
name="defaultValue"
value={formdata.defaultValue}
onChange={(e) => handledefaultChange(e)}
@@ -221,22 +222,37 @@ const WidgetNameModal = (props) => {
style={{ fontSize: 12 }}
>
<i
className="fa-light fa-exclamation-circle text-[15px]"
style={{ color: "#fab005" }}
></i>
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
invalid default value
</div>
)}
</div>
</>
)}
<div className="mb-[0.75rem]">
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
<div className="form-section">
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
>
{statusArr.map((data, ind) => {
return (
<div key={ind} className="flex flex-row gap-[5px] items-center">
<div
key={ind}
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
>
<input
className="mr-[2px] op-radio op-radio-xs"
style={{ accentColor: "red", marginRight: "10px" }}
type="radio"
name="status"
onChange={() =>
@@ -246,7 +262,9 @@ const WidgetNameModal = (props) => {
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div className="text-[13px] font-medium">{data}</div>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
</div>
);
})}
@@ -254,20 +272,35 @@ const WidgetNameModal = (props) => {
</div>
{(props.defaultdata?.type === textInputWidget ||
props?.widgetName === textInputWidget) && (
<div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]">
<div className="form-section">
<label htmlFor="hint" style={{ fontSize: 13 }}>
Hint
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="addUserInput"
name="hint"
value={formdata.hint}
onChange={(e) => handleChange(e)}
/>
</div>
)}
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary">
<div
style={{
height: 1,
backgroundColor: "#b7b3b3",
width: "100%",
marginBottom: "16px"
}}
></div>
<button
style={{
color: "white",
padding: "5px 20px",
backgroundColor: "#32a3ac"
}}
type="submit"
className="finishBtn"
>
Save
</button>
</form>
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../../primitives/Alert";
import Loader from "../../../primitives/Loader";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
@@ -26,13 +25,9 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
if (parentFolderId) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.equalTo("CreatedBy", Parse.User.current());
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.equalTo("CreatedBy", Parse.User.current());
}
const res = await FolderQuery.find();
@@ -54,7 +49,6 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const exsitQuery = new Parse.Query(folderCls);
exsitQuery.equalTo("Name", name);
exsitQuery.equalTo("Type", "Folder");
exsitQuery.notEqualTo("IsArchive", true);
if (parentFolderId) {
exsitQuery.equalTo("Folder", folderPtr);
}
@@ -116,17 +110,20 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder" className="relative">
{isLoader && (
<div className="absolute h-full w-full flex justify-center items-center">
<Loader />
<div className="absolute h-full w-full flex justify-center items-center ">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
<h1 className="text-base font-semibold mt-[0.4rem]">Create Folder</h1>
<h1 className="text-base font-semibold">Create Folder</h1>
<div className="text-xs mt-2">
<label className="block">
Name<span className="text-red-500 text-[13px]">*</span>
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
@@ -137,7 +134,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
<select
value={selectedParent}
onChange={handleOptions}
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
>
<option>select</option>
{folderList.length > 0 &&
@@ -152,9 +149,9 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
<button
onClick={handleCreateFolder}
disabled={isLoader}
className="op-btn op-btn-primary op-btn-sm mt-3"
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
>
<i className="fa-light fa-plus"></i>
<i className="fa-solid fa-plus mr-1"></i>
<span>Create</span>
</button>
</div>
@@ -24,14 +24,10 @@ const FolderModal = (props) => {
const FolderQuery = new Parse.Query(props.folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.descending("Type");
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.equalTo("CreatedBy", Parse.User.current());
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.descending("Type");
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.equalTo("CreatedBy", Parse.User.current());
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
@@ -145,14 +141,14 @@ const FolderModal = (props) => {
handleCreate();
};
return (
<div className="text-xs mt-2">
<div className="text-xs mt-2 ">
<ModalUi
title={"Select Folder"}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="pt-1 text-[#ac4848] text-[14px] font-[500]">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
@@ -173,50 +169,27 @@ const FolderModal = (props) => {
{" / "}
</React.Fragment>
))}
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
<hr />
</div>
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd && folderList.length > 0
? folderList.map((folder) => (
<div
key={folder.objectId}
className={`${
folder.Type === "Folder"
? "cursor-pointer"
: "cursor-default"
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
onClick={() =>
folder.Type === "Folder" && handleSelect(folder)
}
>
<div className="flex items-center gap-2">
{folder.Type === "Folder" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
>
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
>
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
</svg>
)}
<span className="font-semibold">{folder.Name}</span>
</div>
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
))
: !isLoader && (
<div className="text-base-content text-center my-2">
No data found
</div>
)}
</div>
))}
</div>
{isAdd && (
<CreateFolder
@@ -226,8 +199,8 @@ const FolderModal = (props) => {
/>
)}
{isLoader && (
<div className="flex justify-center my-4">
<i className="fa-light fa-spinner fa-spin-pulse text-[30px]"></i>
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
@@ -235,28 +208,22 @@ const FolderModal = (props) => {
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="op-btn op-btn-seconday op-btn-sm"
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<>
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
<span className="text-xs">Back</span>
</>
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<>
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
<span className="">Add folder</span>
</>
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="op-btn op-btn-primary op-btn-sm"
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
@@ -44,14 +44,10 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
const FolderQuery = new Parse.Query(folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.descending("Type");
FolderQuery.equalTo("CreatedBy", Parse.User.current());
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.notEqualTo("IsArchive", true);
FolderQuery.descending("Type");
FolderQuery.equalTo("CreatedBy", Parse.User.current());
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
@@ -173,39 +169,32 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
{required && <span className="text-red-500 text-[13px]">*</span>}
</label>
</div>
<div className="relative max-w-sm">
<div
onClick={() => SetIsOpen(true)}
className="cursor-pointer rounded px-[20px] py-[20px] bg-base-100 border-[1px] border-base-200 shadow flex max-w-sm gap-8 items-center"
>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="w-[40px] h-[40px] fill-current op-text-secondary"
>
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
</p>
<div className="text-black text-sm">
<i
className="fa-light fa-pencil cursor-pointer"
title="Select Folder"
aria-hidden="true"
></i>
</div>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
<div>
<i
className="far fa-folder-open text-[40px] text-[#33bbff]"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
</p>
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
<i
className="fa fa-pencil"
title="Select Folder"
aria-hidden="true"
></i>
</div>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p>
</div>
<div className="absolute top-2 right-1 cursor-pointer">
<Tooltip
@@ -221,7 +210,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="pt-1 text-[#ac4848] text-[14px] font-[500]">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
@@ -242,50 +231,27 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
{" / "}
</React.Fragment>
))}
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
<hr />
</div>
<div className="mb-2">
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd && folderList.length > 0
? folderList.map((folder) => (
<div
key={folder.objectId}
className={`${
folder.Type === "Folder"
? "cursor-pointer"
: "cursor-default"
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
onClick={() =>
folder.Type === "Folder" && handleSelect(folder)
}
>
<div className="flex items-center gap-2">
{folder.Type === "Folder" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
>
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
>
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
</svg>
)}
<span className="font-semibold">{folder.Name}</span>
</div>
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
))
: !isLoader && (
<div className="text-base-content text-center my-2">
No data found
</div>
)}
</div>
))}
</div>
{isAdd && (
<CreateFolder
@@ -295,8 +261,8 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
/>
)}
{isLoader && (
<div className="flex justify-center my-4">
<i className="fa-light fa-spinner fa-spin-pulse text-[30px]"></i>
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
@@ -304,28 +270,22 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="op-btn op-btn-seconday op-btn-sm"
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<>
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
<span className="text-xs">Back</span>
</>
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<>
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
<span className="">Add folder</span>
</>
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="op-btn op-btn-primary op-btn-sm"
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
@@ -1,7 +1,18 @@
import React, { useState } from "react";
import Parse from "parse";
import "../../../styles/AddUser.css";
import AsyncSelect from "react-select/async";
const customStyles = {
control: (provided) => ({
...provided,
fontSize: "13px" // Font size for the control
}),
option: (provided) => ({
...provided,
fontSize: "13px" // Font size for the options
})
};
const SelectSigners = (props) => {
const [userList, setUserList] = useState([]);
const [selected, setSelected] = useState();
@@ -68,44 +79,32 @@ const SelectSigners = (props) => {
};
return (
<div className="h-full px-[20px] py-[10px] text-base-content">
<div className="w-full mx-auto p-[8px]">
<div className="mb-0">
<label className="text-[14px] font-bold">Choose from contacts</label>
<div className="addusercontainer">
<div className="form-wrapper">
<div className="form-section" style={{ marginBottom: 0 }}>
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
<AsyncSelect
cacheOptions
defaultOptions
value={selected}
loadingMessage={() => "Loading..."}
noOptionsMessage={() => "Contact not found"}
noOptionsMessage={() => "User not Found"}
loadOptions={loadOptions}
onChange={handleOptions}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
styles={customStyles}
/>
</div>
<p
className={`${
isError ? "text-[red]" : "text-transparent"
} text-[11px] ml-[6px] my-[2px]`}
>
Please select signer
</p>
{isError ? (
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
Please select signer
</p>
) : (
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
.
</p>
)}
<div>
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
<button className="submitbutton" onClick={() => handleAdd()}>
Submit
</button>
</div>
@@ -1,30 +1,15 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AddSigner from "../../AddSigner";
import Modal from "react-modal";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
import { createPortal } from "react-dom";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
const AddSignerModal = ({ isOpen, children }) => {
if (!isOpen) {
return null;
}
return createPortal(
<div className="op-modal op-modal-open">
<div className="op-modal-box p-0 min-w-[90%] md:min-w-[500px] max-h-90 overflow-y-auto hide-scrollbar text-sm">
{children}
</div>
</div>,
document.body
);
};
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
@@ -32,6 +17,7 @@ const AddSignerModal = ({ isOpen, children }) => {
*/
const SignersInput = (props) => {
Modal.setAppElement("body");
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = useState([]);
@@ -122,17 +108,15 @@ const SignersInput = (props) => {
<label className="block relative">
Signers
{props.required && <span className="text-red-500 text-[13px]">*</span>}
<span className="absolute ml-1 text-xs z-30">
<span className="absolute ml-1 text-xs z-50">
<Tooltip
id={"signer-tooltip"}
message={
"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."
}
message={"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."}
/>
</span>
</label>
<div className="flex gap-x-[5px]">
<div className="w-full">
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<Select
onSortEnd={onSortEnd}
distance={4}
@@ -142,22 +126,6 @@ const SignersInput = (props) => {
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
noOptionsMessage={() => "Contact not found"}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
</div>
<div
@@ -165,29 +133,66 @@ const SignersInput = (props) => {
setIsModel(true);
openModal();
}}
className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[38px] min-w-[48px] flex justify-center items-center"
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<i className="fa-light fa-plus"></i>
<i className="fas fa-plus"></i>
</div>
<AddSignerModal isOpen={modalIsOpen}>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
Add Signer
</h3>
<button
onClick={handleModalCloseClick}
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
>
</button>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</AddSignerModal>
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
id="modal1"
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
border: "1px transparent",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
style={{ background: "#32a3ac" }}
>
<div className="text-[1.2rem] font-normal">Add Signer</div>
<button
onClick={handleModalCloseClick}
className="text-[1.5rem] cursor-pointer focus:outline-none"
>
&times;
</button>
</div>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
</div>
</div>
);
@@ -1,92 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import { findContact } from "../../../constant/Utils";
const SuggestionInput = (props) => {
const [inputValue, setInputValue] = useState(props?.value || "");
const [suggestions, setSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const ref = useRef(null);
useEffect(() => {
document.addEventListener("mousedown", Clickout);
return () => {
document.removeEventListener("mousedown", Clickout);
};
}, []);
const Clickout = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setShowSuggestions(false);
}
};
// create debounce to avoid unnecessay api calls
useEffect(() => {
let timer;
if (inputValue) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
(async () => {
const res = await findContact(inputValue);
if (res?.length > 0) {
setSuggestions(res);
setShowSuggestions(true);
} else {
setSuggestions(res);
setShowSuggestions(false);
}
})();
}, 1000);
}
return () => clearTimeout(timer);
}, [inputValue]);
const handleInputChange = async (e) => {
const value = e.target.value;
setInputValue(value?.toLowerCase());
if (props.onChange) {
props.onChange(value);
}
if (value.trim() === "") {
setSuggestions([]);
setShowSuggestions(false);
return;
}
};
const handleSuggestionClick = (suggestion) => {
setInputValue(suggestion.Email);
setSuggestions([]);
setShowSuggestions(false);
if (props.onChange) {
props.onChange(suggestion);
}
};
return (
<div className="flex flex-col items-center relative text-base-content">
<input
type={props?.type || "text"}
value={inputValue}
onChange={handleInputChange}
placeholder="Enter Email..."
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
required={props.required}
/>
{showSuggestions && (
<ul
ref={ref}
className="absolute z-50 left-0 top-[2.55rem] w-full max-h-[100px] overflow-auto bg-base-200 border border-gray-300 rounded shadow-md"
>
{suggestions.map((suggestion, index) => (
<li
key={index}
className="py-2 px-2 w-full text-sm cursor-pointer hover:bg-base-300"
onClick={() => handleSuggestionClick(suggestion)}
>
{suggestion.Email}
</li>
))}
</ul>
)}
</div>
);
};
export default SuggestionInput;
+2 -6
View File
@@ -3,18 +3,14 @@ import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none" className="my-0.5">
<li key={item.title} role="none">
<NavLink
to={
item.pageType
? `/${item.pageType}/${item.objectId}`
: `/${item.objectId}`
}
className={({ isActive }) =>
`${
isActive ? " bg-base-300 text-base-content" : ""
} flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer focus:text-[#0056b3] focus:bg-[#eef1f5]"
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
+18 -55
View File
@@ -1,18 +1,17 @@
import React, { useState, useEffect } from "react";
import Menu from "./Menu";
import Submenu from "./SubMenu";
import SocialMedia from "./SocialMedia";
import Parse from "parse";
import dp from "../../assets/images/dp.png";
import sidebarList from "../../json/menuJson";
const Sidebar = ({ isOpen, closeSidebar }) => {
const [menuList, setmenuList] = useState([]);
const [submenuOpen, setSubmenuOpen] = useState(false);
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("Extand_Class")
? JSON.parse(localStorage.getItem("Extand_Class"))?.[0]?.Company
: "";
const tenantname = localStorage.getItem("TenantName");
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
@@ -22,49 +21,22 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
const menuItem = async () => {
try {
if (localStorage.getItem("defaultmenuid")) {
const menuId = localStorage.getItem("defaultmenuid") !== "VPh91h0ZHk";
if (menuId) {
setmenuList(sidebarList);
} else {
const addUserForm = {
icon: "fa-light fa-user",
title: "Add User",
target: "_self",
pageType: "form",
description: "",
objectId: "lM0xRnM3iE"
};
const newSidebarList = sidebarList.map((item) => {
if (item.title === "Settings") {
// Make a shallow copy of the item
const newItem = { ...item };
// Insert addUserForm at the second position
newItem.children.splice(1, 0, addUserForm);
return newItem;
}
return item;
});
setmenuList(newSidebarList);
}
}
var sideMenu = Parse.Object.extend("w_menu");
var query = new Parse.Query(sideMenu);
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
const results = await query.first();
const resultjson = results.toJSON();
let result = resultjson;
setmenuList(result.menuItems);
} catch (e) {
console.error("Problem", e);
}
};
const toggleSubmenu = (title) => {
setSubmenuOpen({ [title]: !submenuOpen[title] });
};
const handleMenuItem = () => {
closeSidebar();
setSubmenuOpen({});
};
return (
<aside
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[999] shadow-lg hide-scrollbar
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
className={`bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 md:translate-x-0
${isOpen ? "translate-x-0 block" : "-translate-x-full hidden"}`}
>
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
@@ -75,20 +47,13 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
/>
</div>
<div>
<p className="text-[14px] font-bold text-base-content">{username}</p>
<p
className={`text-[12px] text-base-content ${
tenantname ? "mt-2" : ""
}`}
>
<p className="text-[14px] font-bold">{username}</p>
<p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
{tenantname}
</p>
</div>
</div>
<nav
className="op-menu op-menu-sm"
aria-label="OpenSign Sidebar Navigation"
>
<nav aria-label="OpenSign Sidebar Navigation">
<ul
className="text-sm"
role="menubar"
@@ -100,21 +65,19 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
key={item.title}
item={item}
isOpen={isOpen}
closeSidebar={handleMenuItem}
closeSidebar={closeSidebar}
/>
) : (
<Submenu
key={item.title}
item={item}
closeSidebar={closeSidebar}
toggleSubmenu={toggleSubmenu}
submenuOpen={submenuOpen}
/>
)
)}
</ul>
</nav>
<footer className="mt-4 flex justify-center items-center text-[25px] text-base-content gap-3">
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<SocialMedia />
</footer>
</aside>
+15 -15
View File
@@ -1,13 +1,19 @@
import React from "react";
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const Submenu = ({ item, closeSidebar }) => {
const [submenuOpen, setSubmenuOpen] = useState(false);
const { title, icon, children } = item;
const toggleSubmenu = () => {
setSubmenuOpen(!submenuOpen);
};
return (
<li role="none" className="my-0.5">
<li role="none">
<button
onClick={() => toggleSubmenu(item.title)}
className="flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none "
onClick={toggleSubmenu}
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:outline-none focus:text-[#0056b3] focus:bg-[#eef1f5]"
aria-expanded={submenuOpen}
aria-haspopup="true"
aria-controls={`submenu-${title}`}
@@ -17,29 +23,23 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
<span className="ml-3 lg:ml-4">{title}</span>
<i
className={`${
submenuOpen[item.title]
? "fa-light fa-angle-down"
: "fa-light fa-angle-right"
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
}`}
aria-hidden="true"
></i>
</div>
</button>
{submenuOpen[item.title] && (
{submenuOpen && (
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
{children.map((childItem) => (
<li key={childItem.title} role="none" className="my-0.5">
<li key={childItem.title} role="none">
<NavLink
to={
childItem.pageType
? `/${childItem.pageType}/${childItem.objectId}`
: `/${childItem.objectId}`
}
className={({ isActive }) =>
`${
isActive ? "bg-base-300 text-base-content" : ""
} flex items-center text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white focus:text-[#0056b3] focus:bg-[#eef1f5] cursor-pointer"
onClick={closeSidebar}
role="menuitem"
tabIndex={submenuOpen ? 0 : -1}
File diff suppressed because it is too large Load Diff
+6
View File
@@ -1,6 +1,12 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
export const rejectBtn =
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
export const submitBtn =
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
export const modalSubmitBtnColor = "#17a2b8";
export const modalCancelBtnColor = "white";
export const themeColor = "#47a3ad";
export const iconColor = "#686968";
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
+8 -63
View File
@@ -2,72 +2,17 @@
@tailwind components;
@tailwind utilities;
body {
background-color: white;
body{
background-color: white;
}
/* Custom CSS to hide scrollbar */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
/* below css class replace bootstrap background color with daisy */
.op-bg-primary {
@apply bg-primary;
}
.op-bg-secondary {
@apply bg-secondary;
}
.op-bg-info {
@apply bg-info;
}
.op-bg-success{
@apply bg-success;
}
.op-bg-warning {
@apply bg-warning;
}
/* below css class replace bootstrap text color with daisy */
.op-text-primary {
@apply text-primary;
}
.op-text-secondary {
@apply text-secondary;
}
.op-text-info {
@apply text-info;
}
.op-text-success{
@apply text-success;
}
.op-text-warning {
@apply text-warning;
}
.op-border-primary{
@apply border-primary
}
/* CSS for scrollbar customization */
* {
scrollbar-width: thin; /* For Firefox */
scrollbar-color: gray; /* For Firefox */
}
/* For webkit-based browsers like Chrome, Safari */
*::-webkit-scrollbar {
width: 5px;
}
*::-webkit-scrollbar-track {
background-color: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: gray;
border-radius: 10px;
}
-1
View File
@@ -16,7 +16,6 @@ import {
import DragElement from "./components/pdf/DragElement";
import TagManager from "react-gtm-module";
import Parse from "parse";
import './polyfills'
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
+68 -78
View File
@@ -3,7 +3,7 @@ export default function reportJson(id) {
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
switch (id) {
// draft documents report
case "ByHuevtCFY":
@@ -14,16 +14,18 @@ export default function reportJson(id) {
{
btnId: "1231",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-pen",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2142",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -41,8 +43,9 @@ export default function reportJson(id) {
btnId: "4536",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
@@ -59,40 +62,34 @@ export default function reportJson(id) {
{
btnId: "8901",
hoverLabel: "Share",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-share",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1588",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1630",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "resend"
},
{
btnId: "1688",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-light fa-file-circle-xmark",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
@@ -100,7 +97,7 @@ export default function reportJson(id) {
btnId: "1488",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa-light fa-trash",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -119,16 +116,18 @@ export default function reportJson(id) {
{
btnId: "1378",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-eye",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1278",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -145,16 +144,18 @@ export default function reportJson(id) {
{
btnId: "1458",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-eye",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1358",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -171,16 +172,18 @@ export default function reportJson(id) {
{
btnId: "1898",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-eye",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1998",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -197,40 +200,34 @@ export default function reportJson(id) {
{
btnId: "1999",
hoverLabel: "Share",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-share",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1999",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1631",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "resend"
},
{
btnId: "6788",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-light fa-file-circle-xmark",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
@@ -238,7 +235,7 @@ export default function reportJson(id) {
btnId: "2000",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa-light fa-trash",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -256,8 +253,9 @@ export default function reportJson(id) {
btnId: "2001",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
@@ -272,16 +270,18 @@ export default function reportJson(id) {
{
btnId: "2003",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-pen",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2004",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#ff4848",
textColor: "white",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
@@ -296,8 +296,9 @@ export default function reportJson(id) {
{
btnId: "2204",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-trash",
btnColor: "#f55a42",
textColor: "white",
btnIcon: "fa-solid fa-trash",
action: "delete"
}
],
@@ -309,38 +310,27 @@ export default function reportJson(id) {
case "6TeaPr321t":
return {
reportName: "Templates",
heading: templateReport,
heading: head,
actions: [
{
btnId: "2234",
btnLabel: "Use",
hoverLabel: "Use",
btnColor: "op-btn-primary",
btnIcon: "fa-light fa-plus",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
},
{
btnId: "1631",
btnLabel: "Quick send",
hoverLabel: "Quick send",
btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
btnColor: "transparent",
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
@@ -350,7 +340,7 @@ export default function reportJson(id) {
btnId: "2434",
btnLabel: "Edit",
hoverLabel: "Edit",
btnIcon: "fa-light fa-pen",
btnIcon: "fa-solid fa-pen",
redirectUrl: "template",
action: "redirect"
},
@@ -358,7 +348,7 @@ export default function reportJson(id) {
btnId: "1834",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa-light fa-trash",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
-90
View File
@@ -1,90 +0,0 @@
const dashboardJson = [
{
id: "35KBoSgoAK",
columns: [
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fa-light fa-signature",
bgColor: "op-bg-primary",
label: "Need your Signature",
description: null,
data: {
id: "e04ee9dc-932d-6b80-0202-703fd154eb74",
queryType: "",
class: "contracts_Document",
query:
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "4Hhwbp482K",
tourSection: "tourcard1",
tourMessage:
"Clicking on this card will take you to the list of documents awaiting your review."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fa-light fa-sign-out-alt",
bgColor: "op-bg-secondary",
label: "Out for signatures",
description: null,
data: {
id: "c1935cc0-28d4-3d73-b72a-8c1e3d18a17f",
queryType: "",
class: "contracts_Document",
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*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',
key: "count",
Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF",
tourSection: "tourcard2",
tourMessage:
"Clicking on this card will take you to a list of documents awaiting signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "5Go51Q7T8r",
data: {
tourSection: "tourreport1",
tourMessage:
"This is a list of documents that are waiting for your signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "d9k3UfYHBc",
data: {
tourSection: "tourreport2",
tourMessage:
"This is a list of documents you've sent to other parties for signature."
}
}
},
{
colsize: "col-span-12 md:col-span-12 lg:col-span-12",
widget: {
type: "report",
reportId: "kC5mfynCi4",
data: {
tourSection: "tourreport3",
tourMessage:
"This are documents you have started but have not finalized for sending."
}
}
}
]
}
];
export default dashboardJson;
-161
View File
@@ -1,161 +0,0 @@
const sidebarList = [
{
icon: "fa-light fa-tachometer-alt",
title: "Dashboard",
target: "",
pageType: "dashboard",
description: "",
objectId: "35KBoSgoAK"
},
{
icon: "fa-light fa-pen-nib",
title: "Sign yourself",
target: "_self",
pageType: "form",
description: "",
objectId: "sHAnZphf69"
},
{
icon: "fa-light fa-paper-plane",
title: "Request signatures",
target: "_self",
pageType: "form",
description: "",
objectId: "8mZzFxbG1z"
},
{
icon: "fa-light fa-newspaper",
title: "Templates",
target: "_self",
pageType: null,
description: null,
objectId: null,
children: [
{
icon: "fa-light fa-file-signature",
title: "Create template",
target: "_self",
pageType: "form",
description: "",
objectId: "template"
},
{
icon: "fa-light fa-file-contract",
title: "Manage templates",
target: "_self",
pageType: "report",
description: "",
objectId: "6TeaPr321t"
}
]
},
{
icon: "fa-light fa-folder",
title: "OpenSign™ Drive",
target: "_self",
pageType: "",
description: "",
objectId: "opensigndrive"
},
{
icon: "fa-light fa-address-card",
title: "Reports",
target: "_self",
pageType: null,
description: "",
objectId: null,
children: [
{
icon: "fa-light fa-signature",
title: "Need your sign",
target: "_self",
pageType: "report",
description: "",
objectId: "4Hhwbp482K"
},
{
icon: "fa-light fa-tasks",
title: "In Progress",
target: "_self",
pageType: "report",
description: "",
objectId: "1MwEuxLEkF"
},
{
icon: "fa-light fa-check-circle",
title: "Completed",
target: "_self",
pageType: "report",
description: "",
objectId: "kQUoW4hUXz"
},
{
icon: "fa-light fa-edit",
title: "Drafts",
target: "_self",
pageType: "report",
description: "",
objectId: "ByHuevtCFY"
},
{
icon: "fa-light fa-times-circle",
title: "Declined",
target: "_self",
pageType: "report",
description: "",
objectId: "UPr2Fm5WY3"
},
{
icon: "fa-light fa-hourglass-end",
title: "Expired",
target: "_self",
pageType: "report",
description: "",
objectId: "zNqBHXHsYH"
},
{
icon: "fa-light fa-address-book",
title: "Contactbook",
target: "_self",
pageType: "report",
description: "",
objectId: "5KhaPr482K"
}
]
},
{
icon: "fa-light fa-cog",
title: "Settings",
target: "_self",
pageType: null,
description: "",
objectId: null,
children: [
{
icon: "fa-light fa-pen-fancy",
title: "My Signature",
target: "_self",
pageType: "",
description: "",
objectId: "managesign"
},
{
icon: "fa-light fa-key",
title: "API Token",
target: "_self",
pageType: "generatetoken",
description: "",
objectId: ""
},
{
icon: "fa-light fa-globe",
title: "Webhook",
target: "_self",
pageType: "webhook",
description: "",
objectId: ""
}
]
}
];
export default sidebarList;
+1 -2
View File
@@ -52,8 +52,7 @@ const plans = [
"API Access",
"100 API signatures included",
"DocumentId removal from signed docs",
"Custom email templates",
"Auto reminders"
"Custom email templates"
]
},
{
+43 -36
View File
@@ -5,20 +5,17 @@ import Sidebar from "../components/sidebar/Sidebar";
import { useWindowSize } from "../hook/useWindowSize";
import Tour from "reactour";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate, useLocation, Outlet } from "react-router-dom";
import { isEnableSubscription } from "../constant/const";
import { useCookies } from "react-cookie";
import { fetchSubscription } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { showHeader } from "../redux/reducers/showHeader";
const HomeLayout = () => {
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
const { width } = useWindowSize();
const [isOpen, setIsOpen] = useState(true);
const arr = useSelector((state) => state.TourSteps);
@@ -31,34 +28,29 @@ const HomeLayout = () => {
const [tourConfigs, setTourConfigs] = useState([]);
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
const tenantId = localStorage.getItem("TenantId");
useEffect(() => {
if (!tenantId) {
setIsUserValid(false);
} else {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
checkIsSubscribed();
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
checkIsSubscribed();
} else {
setIsUserValid(false);
}
})();
saveCookies();
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
saveCookies();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tenantId]);
}, []);
//function to use save data in cookies storage
const saveCookies = () => {
const main_Domain = window.location.origin;
@@ -100,7 +92,6 @@ const HomeLayout = () => {
}
const showSidebar = () => {
setIsOpen((value) => !value);
dispatch(showHeader(!isOpen));
};
useEffect(() => {
if (width && width <= 768) {
@@ -234,7 +225,7 @@ const HomeLayout = () => {
}
const closeSidebar = () => {
if (width <= 1023) {
if (width <= 768) {
setIsOpen(false);
}
};
@@ -251,14 +242,27 @@ const HomeLayout = () => {
};
return (
<div>
<div className="sticky top-0 z-[101]">
<div className="sticky top-0 z-50">
{!isLoader && <Header showSidebar={showSidebar} />}
</div>
{isUserValid ? (
<>
{isLoader ? (
<div className="flex h-[100vh] justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
@@ -268,7 +272,7 @@ const HomeLayout = () => {
id="renderList"
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
>
<div className="bg-base-200 p-3">{<Outlet />}</div>
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
<div className="z-30">
<Footer />
</div>
@@ -289,10 +293,13 @@ const HomeLayout = () => {
)}
</>
) : (
<ModalUi showHeader={false} isOpen={true} showClose={false}>
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-medium">Your session has expired.</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
<p className="text-xl font-normal">Your session has expired.</p>
<button
onClick={handleLoginBtn}
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
>
Login
</button>
</div>
+7 -7
View File
@@ -49,13 +49,13 @@ function ChangePassword() {
return <Navigate to="/" />;
}
return (
<div className="w-full bg-base-100 text-base-content shadow rounded-box p-2">
<Title title="Change Password" />
<div className="w-full bg-white shadow rounded p-2">
<Title title={"Change Password"} />
<div className="text-xl font-bold border-b-[1px] border-gray-300">
Change Password
</div>
<div className="m-2">
<form onSubmit={handleSubmit} className=" flex flex-col gap-y-2">
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="currentpassword" className="block text-xs ml-1">
Current Password
@@ -65,7 +65,7 @@ function ChangePassword() {
name="currentpassword"
value={currentpassword}
onChange={(e) => setCurrentPassword(e.target.value)}
className="op-input op-input-bordered op-input-sm text-xs w-full"
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Current Password"
required
/>
@@ -79,7 +79,7 @@ function ChangePassword() {
name="newpassword"
value={newpassword}
onChange={(e) => setnewpassword(e.target.value)}
className="op-input op-input-bordered op-input-sm text-xs w-full"
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="New Password"
required
/>
@@ -91,7 +91,7 @@ function ChangePassword() {
<input
type="password"
name="confirmpassword"
className="op-input op-input-bordered op-input-sm text-xs w-full"
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={confirmpassword}
onChange={(e) => setconfirmpassword(e.target.value)}
placeholder="Confirm Password"
@@ -100,7 +100,7 @@ function ChangePassword() {
</div>
<button
type="submit"
className="op-btn op-btn-primary shadow-md mt-2"
className="bg-[#188ae2] m-2 px-4 py-3 rounded-sm shadow-md text-white text-xs font-semibold uppercase"
>
Change Password
</button>
+84 -16
View File
@@ -1,18 +1,20 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import "../styles/loader.css";
import GetDashboard from "../components/dashboard/GetDashboard";
import { useNavigate, useParams } from "react-router-dom";
import Title from "../components/Title";
import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson";
import Loader from "../primitives/Loader";
const Dashboard = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { id } = useParams();
const [dashboard, setdashboard] = useState({});
const [loading, setloading] = useState(true);
const [dashboard, setdashboard] = useState([]);
const [classnameArray, setclassnameArray] = useState([]);
const [loading, setloading] = useState(false);
const [defaultQuery, setDefaultQuery] = useState("");
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
@@ -22,16 +24,59 @@ const Dashboard = () => {
getDashboard(localStorage.getItem("PageLanding"));
}
} else {
navigate("/", { replace: true, state: { from: "" } });
navigate("/", { replace: true, state: { from: location } });
}
// eslint-disable-next-line
}, [id]);
const getDashboard = async (id) => {
setloading(true);
try {
const dashboard = dashboardJson.find((x) => x.id === id);
setdashboard(dashboard);
const dashboardTour = dashboard.columns
var forms = Parse.Object.extend("w_dashboard");
var query = new Parse.Query(forms);
query.equalTo("objectId", id);
const results = await query.first();
const resultjson = results.toJSON();
let classArray = [];
let DefaultQuery = "";
resultjson.rows.forEach((x) => {
let col_lg = "",
col_md = "",
col_xs = "",
col_sm = "",
col_ = "",
obj = {};
let subItem = [];
let item = "";
x.columns.forEach((y) => {
if (y.widget.type === "customeHtml") {
DefaultQuery = y.widget.data[0].default;
}
if (Number(y["collg"]) > 0) {
col_lg = "col-lg-" + y["collg"];
}
if (Number(y["colmd"]) > 0) {
col_md = "col-md-" + y["colmd"];
}
if (Number(y["colxs"]) > 0) {
col_xs = "col-xs-" + y["colxs"];
}
if (Number(y["colsm"]) > 0) {
col_sm = "col-sm-" + y["colsm"];
}
if (Number(y["col"]) > 0) {
col_ = "col-" + y["col"];
}
item =
col_lg + " " + col_md + " " + col_xs + " " + col_sm + " " + col_;
subItem.push(item);
});
obj = subItem;
classArray.push(obj);
});
setdashboard(resultjson.rows);
// console.log("resultjson.rows ", resultjson.rows);
const arr = resultjson.rows[0].columns
.filter((col) => {
if (col.widget.data && col.widget.data.tourSection) {
return col;
@@ -45,7 +90,14 @@ const Dashboard = () => {
// style: { backgroundColor: "#abd4d2" },
};
});
dispatch(saveTourSteps(dashboardTour));
dispatch(saveTourSteps(arr));
// console.log("arr ", arr);
setclassnameArray(classArray);
if (localStorage.getItem("DashboardDefaultFilter")) {
setDefaultQuery(localStorage.getItem("DashboardDefaultFilter"));
} else {
setDefaultQuery(DefaultQuery);
}
setloading(false);
} catch (e) {
console.error("Problem", e);
@@ -53,16 +105,32 @@ const Dashboard = () => {
}
};
let _dash = (
<GetDashboard
dashboard={dashboard}
classnameArray={classnameArray}
DefaultQuery={defaultQuery}
/>
);
if (loading) {
_dash = (
<div style={{ height: "300px", backgroundColor: "white" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
}
return (
<React.Fragment>
<Title title="Dashboard" />
{loading ? (
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
<Loader />
</div>
) : (
<GetDashboard dashboard={dashboard} />
)}
{_dash}
</React.Fragment>
);
};
+109 -31
View File
@@ -1,5 +1,7 @@
import React, { useEffect, useState } from "react";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import "../styles/signature.css";
import "../styles/AddUser.css";
import Title from "../components/Title";
import RenderDebugPdf from "../components/RenderDebugPdf";
import { pdfjs } from "react-pdf";
@@ -56,7 +58,7 @@ const DebugPdf = () => {
const fetchPdfMetadata = async () => {
try {
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
// console.log("pdfDataURL ", pdfDataURL);
console.log("pdfDataURL ", pdfDataURL);
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
const pdfType = await inferPdfType(pdfInfo);
setPdfDetails((prevDetails) => ({
@@ -74,7 +76,7 @@ const DebugPdf = () => {
const inferPdfType = async (pdf) => {
try {
const firstPage = await pdf.getPage(2);
const firstPage = await pdf.getPage(1);
const scale = 1;
const { width, height } = firstPage.getViewport({ scale });
setPdfDimension({ width: width, height: height });
@@ -215,18 +217,14 @@ const DebugPdf = () => {
};
return (
<div>
{copied && (
<Alert type="success">
<span className="ml-3">Copied</span>
</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} />
) : (
<>
{!isModal && (
<div className="flex flex-row justify-between">
<div className="signatureContainer">
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdf}
@@ -255,68 +253,126 @@ const DebugPdf = () => {
/>
</div>
</div>
<div className="w-[220px] bg-base-100">
<div className="text-[18px] font-medium py-[10px] px-[12px] border-b-[1px] border-[gray]">
<div style={{ backgroundColor: "white", width: 220 }}>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
PDF details
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Name: {pdfDetails?.name}
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Pdf type: {pdfDetails?.pdftype}
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Total Pages: {pdfDetails?.totalPages}
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Current Page: {pdfDetails?.currentPage}
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Base64 : {pdfDetails?.base64.slice(0, 10)}...
<span
className="op-btn op-btn-outline op-btn-primary op-btn-xs rounded-md w-[25px] h-[25px] text-[12px] m-[2px] "
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => copytoclipboard(pdfDetails?.base64)}
>
<i className="fa-light fa-copy"></i>
<i className="fa-solid fa-copy"></i>
</span>
</div>
<div className="text-[18px] font-medium py-[10px] px-[12px] border-b-[1px] border-[gray]">
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
Last click
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
x co-ordinate: {pdfDetails?.x}
</div>
<div className="text-[14px] py-[5px] px-[12px]">
<div style={{ fontSize: 14, padding: "5px 12px" }}>
y co-ordinate: {pdfDetails?.y}
</div>
<div className="text-[18px] font-medium py-[10px] px-[12px] border-y-[1px] border-[gray]">
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey",
borderTop: "1px solid grey"
}}
>
Annotation
</div>
<ul className=" list-none p-[10px] h-[500px] overflow-y-auto">
<ul
style={{
listStyle: "none",
padding: 10,
height: 500,
overflowY: "auto",
scrollbarWidth: 5
}}
>
{annotations.map((coord, index) => (
<li key={index}>
<span className="text-[13px] font-medium">{`Box ${
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
index + 1
}:`}</span>
<code className="text-[12px] text-base-content select-none">
<code
style={{
fontSize: 12,
color: "black",
cursor: "pointer"
}}
>
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
</code>
<div>
<span
className="op-btn op-btn-outline op-btn-primary op-btn-xs rounded-md w-[23px] h-[20px] text-[12px] m-[2px] "
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() =>
copytoclipboard(
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
)
}
>
<i className="fa-light fa-copy"></i>
<i className="fa-solid fa-copy"></i>
</span>
<span
className="op-btn op-btn-outline op-btn-error op-btn-xs rounded-md w-[23px] h-[20px] text-[12px] m-[2px] "
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => handleDelete(coord.key)}
>
<i className="fa-light fa-trash-can"></i>
<i className="fa-solid fa-trash-can"></i>
</span>
</div>
</li>
@@ -326,16 +382,38 @@ const DebugPdf = () => {
</div>
)}
<ModalUi title={"Select PDF"} isOpen={isModal}>
<form onSubmit={handleSubmit} className="m-[10px]">
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
<input
type="file"
onChange={(e) => handleFileChange(e.target.files)}
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full h-[40px] text-xs"
style={{
width: "100%",
border: "1px solid grey",
borderRadius: "4px",
padding: "5px",
fontSize: 12
}}
accept=".pdf"
required
/>
<div
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
></div>
<button
className="op-btn op-btn-primary w-full mt-3 mb-1"
style={{
background: "#32a3ac",
borderRadius: "2px",
boxShadow:
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
border: "none",
textTransform: "uppercase",
fontSize: "13px",
fontWeight: "600",
padding: "0.375rem 0.75rem",
textAlign: "center",
color: "#ffffff",
outline: "none"
}}
type="submit"
>
Submit
+24 -39
View File
@@ -1,26 +1,21 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import { useNavigate } from "react-router-dom";
import { NavLink } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import Parse from "parse";
import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo } from "../constant/Utils";
function ForgotPassword() {
const dispatch = useDispatch();
const navigate = useNavigate();
const [state, setState] = useState({
email: "",
password: "",
hideNav: ""
});
const [sentStatus, setSentStatus] = useState("");
const [image, setImage] = useState();
const handleChange = (event) => {
const { name, value } = event.target;
setState({ ...state, [name]: value });
@@ -56,26 +51,15 @@ function ForgotPassword() {
useEffect(() => {
dispatch(fetchAppInfo());
saveLogo();
resize();
window.addEventListener("resize", resize);
return () => window.removeEventListener("resize", resize);
// eslint-disable-next-line
}, []);
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
} else {
setImage(appInfo?.applogo || undefined);
}
} else {
setImage(appInfo?.applogo || undefined);
}
};
let image = appInfo.applogo;
return (
<div>
<div className="bg-white">
<Title title="Forgot password page" />
{sentStatus === "success" && (
<Alert type="success">
@@ -85,31 +69,29 @@ function ForgotPassword() {
{sentStatus === "failed" && (
<Alert type={"danger"}>Please setup email adapter </Alert>
)}
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{image && (
<img
src={image}
className="object-contain h-full"
alt="applogo"
/>
<div>
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block">
{state.hideNav ? (
<img src={image} width="100%" alt="" />
) : (
<img src={image} width="100%" alt="" />
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<div className="">
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
<span className="text-[12px] text-[#878787]">
Reset Your Password
</span>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="shadow-md rounded my-4">
<div className="px-6 py-4">
<label className="block text-xs">Email</label>
<input
type="email"
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
name="email"
className="op-input op-input-bordered op-input-sm w-full"
value={state.email}
onChange={handleChange}
required
@@ -117,16 +99,19 @@ function ForgotPassword() {
<hr className="my-2 border-none" />
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold">
<button type="submit" className="op-btn op-btn-primary">
<div className="flex justify-between items-center px-4">
<button
type="submit"
className="rounded-sm bg-[#3ac9d6] text-white px-4 py-3 text-xs font-semibold shadow uppercase"
>
Submit
</button>
<button
onClick={() => navigate("/", { replace: true })}
className="op-btn op-btn-secondary"
<NavLink
to="/"
className="rounded-sm bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-3 text-xs font-semibold shadow uppercase"
>
Login
</button>
</NavLink>
</div>
</form>
</div>
+86 -212
View File
@@ -11,15 +11,11 @@ import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import { SaveFileSize } from "../constant/saveFileSize";
import { checkIsSubscribed, getFileName, toDataUrl } from "../constant/Utils";
import { getFileName, toDataUrl } from "../constant/Utils";
import { PDFDocument } from "pdf-lib";
import axios from "axios";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, submitBtn } from "../constant/const";
import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip";
import Upgrade from "../primitives/Upgrade";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Loader from "../primitives/Loader";
// `Form` render all type of Form on this basis of their provided in path
function Form() {
@@ -51,9 +47,7 @@ const Forms = (props) => {
TimeToCompleteDays: 15,
SendinOrder: "false",
password: "",
file: "",
remindOnceInEvery: 5,
autoreminder: false
file: ""
});
const [fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
@@ -65,7 +59,6 @@ const Forms = (props) => {
const [isPassword, setIsPassword] = useState(false);
const [isDecrypting, setIsDecrypting] = useState(false);
const [isCorrectPass, setIsCorrectPass] = useState(true);
const [isSubscribe, setIsSubscribe] = useState(false);
const handleStrInput = (e) => {
setIsCorrectPass(true);
setFormData({ ...formData, [e.target.name]: e.target.value });
@@ -76,17 +69,6 @@ const Forms = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.title]);
useEffect(() => {
fetchSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
};
function getFileAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
@@ -400,8 +382,6 @@ const Forms = (props) => {
if (props.title !== "Sign Yourself") {
const isChecked = formData.SendinOrder === "false" ? false : true;
object.set("SendinOrder", isChecked);
object.set("AutomaticReminders", formData.autoreminder);
object.set("RemindOnceInEvery", parseInt(formData.remindOnceInEvery));
}
object.set("URL", fileupload);
object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
@@ -421,6 +401,7 @@ const Forms = (props) => {
className: "contracts_Users",
objectId: ExtCls[0].objectId
});
const res = await object.save();
if (res) {
setSigners([]);
@@ -445,7 +426,7 @@ const Forms = (props) => {
setIsSubmit(false);
}
} else {
alert("Please wait while the document is being uploaded.");
alert("Please wait until the PDF has been uploading.");
}
};
@@ -475,19 +456,12 @@ const Forms = (props) => {
? "Note to myself"
: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "true",
password: "",
file: "",
remindOnceInEvery: 5,
autoreminder: false
SendinOrder: "true"
});
setFileUpload("");
setpercentage(0);
setTimeout(() => setIsReset(false), 50);
};
const handleCancel = () => {
navigate("/dashboard/35KBoSgoAK");
};
const handlePasswordSubmit = async (e) => {
e.preventDefault();
setIsPassword(false);
@@ -561,11 +535,8 @@ const Forms = (props) => {
inputFileRef.current.value = ""; // Set file input value to empty string
}
};
const handleAutoReminder = () => {
setFormData((prev) => ({ ...prev, autoreminder: !formData.autoreminder }));
};
return (
<div className="shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content">
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title={props?.title} />
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
@@ -575,8 +546,16 @@ const Forms = (props) => {
</Alert>
)}
{isSubmit ? (
<div className="h-[300px] flex justify-center items-center">
<Loader />
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
@@ -587,30 +566,29 @@ const Forms = (props) => {
>
<form onSubmit={handlePasswordSubmit}>
<div className="px-6 pt-3 pb-2">
<label className="mb-2 text-xs text-base-content">
Password
</label>
<label className="mb-2 text-xs">Password</label>
<input
type="text"
name="password"
value={formData.password}
onChange={(e) => handleStrInput(e)}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter pdf password"
required
/>
<p
className={`${
!isCorrectPass
? "text-red-600"
: "text-transparent pointer-events-none"
} ml-2 text-[11px] `}
>
Please provide correct password
</p>
{!isCorrectPass ? (
<p className="ml-2 text-[11px] text-red-600">
Please provide correct password
</p>
) : (
<p className="ml-2 text-[11px] text-transparent text-red-600">
.
</p>
)}
</div>
<div className="px-6 mb-3">
<button type="submit" className="op-btn op-btn-primary">
<hr />
<div className="px-6 my-3">
<button type="submit" className={submitBtn}>
Submit
</button>
</div>
@@ -626,12 +604,12 @@ const Forms = (props) => {
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-base-content text-sm">{percentage}%</span>
<span className="text-black text-sm">{percentage}%</span>
</div>
)}
{isDecrypting && (
<div className="flex items-center gap-x-2">
<span className="text-base-content text-sm">
<span className="text-black text-sm">
Decrypting pdf please wait...
</span>
</div>
@@ -645,15 +623,15 @@ const Forms = (props) => {
</label>
{fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-x text-[13px]">
<div className="break-all ">
file selected: {getFileName(fileupload)}
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {getFileName(fileupload)}
</div>
<div
onClick={() => setFileUpload("")}
className="cursor-pointer px-[10px] text-[20px] font-bold text-red-500"
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-light fa-xmark"></i>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
@@ -664,10 +642,10 @@ const Forms = (props) => {
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center ">
<div className="flex gap-2 justify-center items-center">
<input
type="file"
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
onChange={(e) => handleFileInput(e)}
ref={inputFileRef}
accept={
@@ -695,23 +673,21 @@ const Forms = (props) => {
</label>
<input
name="Name"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
{props.title === "New Template" && (
<div className="text-xs mt-2">
<label className="block">Description</label>
<input
name="Description"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
/>
</div>
)}
<div className="text-xs mt-2">
<label className="block">Description</label>
<input
name="Description"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
/>
</div>
{props.signers && (
<SignersInput
onChange={handleSigners}
@@ -725,19 +701,18 @@ const Forms = (props) => {
</label>
<input
name="Note"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
{props.title !== "New Template" && (
<SelectFolder
onSuccess={handleFolder}
folderCls={props.Cls}
isReset={isReset}
/>
)}
<SelectFolder
onSuccess={handleFolder}
folderCls={props.Cls}
isReset={isReset}
/>
{props.title === "Request Signatures" && (
<div className="text-xs mt-2">
<label className="block">
@@ -747,7 +722,7 @@ const Forms = (props) => {
<input
type="number"
name="TimeToCompleteDays"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.TimeToCompleteDays}
onChange={(e) => handleStrInput(e)}
required
@@ -755,147 +730,46 @@ const Forms = (props) => {
</div>
)}
{props.title !== "Sign Yourself" && (
<>
<div className="text-xs mt-2">
<label className="block">
Send In Order
<a data-tooltip-id="sendInOrder-tooltip" className="ml-1">
<sup>
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
</sup>
</a>
<Tooltip id="sendInOrder-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">Send in Order</p>
<p>
Choose how you want the signing requests to be sent to
the document signers:
</p>
<p className="p-[5px]">
<ol className="list-disc">
<li>
<span className="font-bold">Yes:</span>
<span>
Selecting this option will send the signing
request to the first signer initially. Once the
first signer completes their part, the next
signer in the sequence will receive the request.
This process continues until all signers have
signed the document. This method ensures that
the document is signed in a specific order.
</span>
</li>
<li>
<span className="font-bold">No: </span>
<span>
Selecting this option will send the signing
links to all signers simultaneously. Every
signer can sign the document at their
convenience, regardless of whether other signers
have completed their signatures. This method is
faster but does not enforce any signing order
among the participants.
</span>
</li>
</ol>
</p>
<p>
Select the option that best suits the needs of your
document processing.
</p>
</div>
</Tooltip>
</label>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div className="text-center">Yes</div>
</div>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"false"}
name="SendinOrder"
className="op-radio op-radio-xs"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-center">No</div>
</div>
<div className="text-xs mt-2">
<label className="block">Send In Order</label>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"true"}
name="SendinOrder"
checked={formData.SendinOrder === "true"}
className=""
onChange={handleStrInput}
/>
<div className="text-center">Yes</div>
</div>
<div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span
className={
isSubscribe || !isEnableSubscription
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
className={`${
isSubscribe || !isEnableSubscription
? "cursor-pointer "
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
checked={formData.autoreminder}
onChange={handleAutoReminder}
/>
</label>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
type="radio"
value={"false"}
name="SendinOrder"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-center">No</div>
</div>
{formData?.autoreminder === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
value={formData.remindOnceInEvery}
name="remindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
required
/>
</div>
)}
</>
</div>
)}
<div className="flex items-center mt-3 gap-2">
<div className="flex items-center mt-3 gap-2 text-white">
<button
className={`${
isSubmit ? "cursor-progress" : ""
} op-btn op-btn-primary`}
isSubmit && "cursor-progress"
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
type="submit"
disabled={isSubmit}
>
Next
</button>
<div
className="op-btn op-btn-ghost"
onClick={() => handleCancel()}
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
onClick={() => handleReset()}
>
Cancel
Reset
</div>
</div>
</form>
+159 -141
View File
@@ -1,31 +1,26 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
const tourSteps = [
{
selector: '[data-tut="apisubscribe"]',
content: "Upgrade now to generate API token"
}
];
function GenerateToken() {
const navigation = useNavigate();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [apiToken, SetApiToken] = useState("");
const [isLoader, setIsLoader] = useState(true);
const [copied, setCopied] = useState(false);
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
useEffect(() => {
fetchToken();
// eslint-disable-next-line
@@ -37,6 +32,7 @@ function GenerateToken() {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
"Content-Type": "application/json",
@@ -47,6 +43,7 @@ function GenerateToken() {
if (res) {
SetApiToken(res.data.result.result);
}
setIsLoader(false);
} catch (err) {
SetApiToken();
@@ -56,43 +53,49 @@ function GenerateToken() {
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!isSubscribe && isEnableSubscription) {
setIsTour(true);
} else {
setIsLoader(true);
setIsModal(false);
try {
const url = parseBaseUrl + "functions/generateapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
const res = await axios.post(url, {}, { headers: headers });
setIsLoader(true);
setIsModal(false);
try {
const url = parseBaseUrl + "functions/generateapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, {}, { headers: headers }).then((res) => {
if (res) {
SetApiToken(res.data.result.token);
setIsAlert({ type: "success", msg: "Token generated successfully." });
// localStorage.setItem("apiToken", res.data.result.token);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating Token");
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
} catch (error) {
setIsAlert({ type: "danger", msg: "Something went wrong." });
console.log("while generating Token", error);
} finally {
setIsLoader(false);
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
console.log("err", error);
}
};
const copytoclipboard = (text) => {
copytoData(text);
setIsAlert({ type: "success", msg: "Copied" });
setCopied(true);
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleModal = () => setIsModal(!isModal);
@@ -100,124 +103,139 @@ function GenerateToken() {
return (
<React.Fragment>
<Title title={"API Token"} />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
{isGenerate && (
<Alert type="success">Token generated successfully!</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div className="flex justify-center items-center h-screen">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign API{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
}
isSubscribe={true}
/>
</h1>
<ul
className={"w-full flex flex-col p-2 text-sm"}
// isSubscribe || !isEnableSubscription
// ? "w-full flex flex-col p-2 text-sm "
// : "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
<div className="bg-white flex flex-col justify-center shadow rounded">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign API{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
}
isSubscribe={true}
/>
</h1>
<ul
className={
isSubscribe || !isEnableSubscription
? "w-full flex flex-col p-2 text-sm "
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
}
>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5">
<span className="ml-1">API Token:</span>{" "}
<span
id="token"
className={`${
isSubscribe || !isEnableSubscription
? ""
: "bg-white/20 pointer-events-none select-none"
} md:text-end py-2 md:py-0`}
>
<span
className="cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken ? apiToken : "_____"}
</span>
<button
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
<i className="fa-light fa-copy"></i>
</button>
</span>
</div>
<button
onClick={apiToken ? handleModal : handleSubmit}
className="op-btn op-btn-primary"
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
<span className="">Api Token:</span>{" "}
<span
id="token"
className=" md:text-end cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
</li>
</ul>
<div className="flex items-center justify-center">
{apiToken ? apiToken : "_____"}
</span>
</div>
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="op-btn op-btn-secondary mt-2 mb-3 px-8"
onClick={apiToken ? handleModal : handleSubmit}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
View Docs
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
</div>
<ModalUi
isOpen={isModal}
title={apiToken ? "Regenerate Token" : "Generate Token"}
handleClose={handleModal}
</li>
</ul>
<div className="flex items-center justify-center ">
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
<div className="m-[20px]">
<div className="text-lg font-normal text-base-content">
Are you sure you want to regenerate token it will expire old
token?
</div>
<hr className="bg-[#ccc] mt-4" />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
</button>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
View Docs
</button>
</div>
{!isSubscribe && isEnableSubscription && (
<div data-tut="apisubscribe">
<SubscribeCard />
</div>
<>
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
Upgrade to PRO Plan
</h1>
<ul className={"w-full flex flex-col p-2 text-sm "}>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
<span className="">$29.99/month:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
Sign 100 docs using API. Only 0.15/docs after that.
</span>
</div>
{!isSubscribe && isEnableSubscription && (
<button
type="button"
onClick={() => navigation("/subscription")}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
Upgrade Now
</button>
)}
</li>
</ul>
</>
)}
</>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to regenerate token it will expire old
token?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
No
</button>
</div>
</div>
</ModalUi>
</div>
)}
<Tour
onRequestClose={() => setIsTour(false)}
steps={tourSteps}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
scrollOffset={-100}
rounded={5}
/>
</React.Fragment>
);
}
+171 -190
View File
@@ -1,16 +1,16 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import "../styles/loginPage.css";
import loader from "../assets/images/loader2.gif";
import axios from "axios";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, themeColor } from "../constant/const";
import { contractUsers, getAppLogo } from "../constant/Utils";
import logo from "../assets/images/logo.png";
import { appInfo } from "../constant/appinfo";
import Parse from "parse";
import Loader from "../primitives/Loader";
function GuestLogin() {
const { id, userMail, contactBookId, base64url } = useParams();
const navigate = useNavigate();
let navigate = useNavigate();
const [email, setEmail] = useState(userMail);
const [OTP, setOTP] = useState("");
const [EnterOTP, setEnterOtp] = useState(false);
@@ -19,10 +19,9 @@ function GuestLogin() {
const [appLogo, setAppLogo] = useState("");
const [documentId, setDocumentId] = useState(id);
const [contactId, setContactId] = useState(contactBookId);
const [sendmail, setSendmail] = useState();
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
useEffect(() => {
handleServerUrl();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -53,49 +52,51 @@ function GuestLogin() {
//split url in array from '/'
const checkSplit = decodebase64.split("/");
setDocumentId(checkSplit[0]);
setContact((prev) => ({ ...prev, email: checkSplit[1] }));
setEmail(checkSplit[1]);
const contactId = checkSplit?.[2];
setSendmail(checkSplit[3]);
if (!contactId) {
const params = { email: checkSplit[1], docId: checkSplit[0] };
try {
const linkContactRes = await Parse.Cloud.run(
"linkcontacttodoc",
params
);
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes?.contactId);
} catch (err) {
console.log("Err in link ext contact", err);
}
} else {
setContactId(checkSplit[2]);
}
setContactId(checkSplit[2]);
}
setIsLoading(false);
};
//send email OTP function
const SendOtp = async () => {
setLoading(true);
setEmail(email);
try {
const params = { email: email.toString(), docId: documentId };
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
if (Otp) {
setLoading(false);
setEnterOtp(true);
}
} catch (error) {
alert("something went wrong!");
}
const handleChange = (event) => {
const { value } = event.target;
setOTP(value);
};
const handleSendOTPBtn = async (e) => {
e.preventDefault();
await SendOtp();
//send email OTP function
const SendOtp = async (e) => {
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (serverUrl && localStorage) {
setLoading(true);
e.preventDefault();
setEmail(email);
try {
let url = `${serverUrl}functions/SendOTPMailV1/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId
};
let body = {
email: email.toString(),
docId: documentId
};
let Otp = await axios.post(url, body, { headers: headers });
if (Otp) {
setLoading(false);
setEnterOtp(true);
}
} catch (error) {
alert("something went wrong!");
}
} else {
alert("something went wrong!");
}
};
//verify OTP send on via email
@@ -133,8 +134,7 @@ function GuestLogin() {
`Parse/${parseId}/currentUser`,
JSON.stringify(_user)
);
// console.log("contractUserDetails ", contractUserDetails);
if (contractUserDetails && contractUserDetails.length > 0) {
if (contractUserDetails) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
@@ -146,13 +146,7 @@ function GuestLogin() {
//save isGuestSigner true in local to handle login flow header in mobile view
localStorage.setItem("isGuestSigner", true);
setLoading(false);
if (sendmail === "false") {
navigate(
`/load/recipientSignPdf/${documentId}/${contactId}?sendmail=${sendmail}`
);
} else {
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
}
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
}
} catch (error) {
console.log("err ", error);
@@ -161,169 +155,156 @@ function GuestLogin() {
alert("Please Enter OTP!");
}
};
const handleUserData = async (e) => {
e.preventDefault();
const params = { ...contact, docId: documentId };
try {
setLoading(true);
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes.contactId);
setEnterOtp(true);
await SendOtp();
} catch (err) {
setLoading(false);
alert("something went wron, please try agian later.");
console.log("Err in link ext contact", err);
}
};
const handleInputChange = (e) => {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
return (
<div className="p-[2rem]">
<div style={{ padding: "2rem", background: "white" }}>
{isLoading ? (
<div className="flex flex-col justify-center items-center h-[100vh]">
<Loader />
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
flexDirection: "column"
}}
>
<img
alt="no img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
) : (
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
{appLogo && (
<img src={appLogo} className="object-contain" alt="logo" />
)}
<div
style={{
margin: "10px",
border: "0.5px solid #c5c7c9",
padding: "30px",
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"
}}
>
<div className="main_head">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{appLogo && (
<img
src={appLogo}
className="object-contain h-full"
alt="logo"
/>
)}
</div>
</div>
{contactId ? (
<>
{!EnterOTP ? (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
Verification code is sent to your email
</legend>
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
{!EnterOTP ? (
<div className="row">
<div className="col-sm-6 KLO">
<span className="welcomeText">Welcome Back !</span>
<br />
<span className="KNLO">
Verification code is sent to your email
</span>
<div className="card card-box" style={{ borderRadius: "0px" }}>
<div className="card-body">
<input
type="email"
name="mobile"
value={email}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
disabled
className="loginInput"
/>
</div>
<div className="mt-3">
<button
className="op-btn op-btn-primary"
onClick={(e) => handleSendOTPBtn(e)}
disabled={loading}
>
{loading ? "Loading..." : "Get verification code"}
</button>
<br />
</div>
</div>
) : (
<form
className="w-full md:w-[50%] text-base-content"
onSubmit={VerifyOTP}
>
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2">
You will get a verification code via Email
</legend>
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
<p className="text-sm">Enter Verification Code</p>
<div className="btnContainer">
{loading ? (
<button
type="button"
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
disabled
>
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
</button>
) : (
<button
className="verifyBtn"
style={{
background: themeColor,
color: "white",
marginLeft: "0px !important"
}}
onClick={(e) => SendOtp(e)}
>
Send OTP
</button>
)}
</div>
</div>
</div>
) : (
<div className="row">
<div className="col-sm-6 KLO">
<span className="welcomeText">Welcome Back !</span>
<br />
<span className="KNLO">You will get a OTP via Email</span>
<div className="card card-box">
<div className="card-body">
<label>Enter Verification Code</label>
<input
type="number"
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="loginInput"
name="OTP"
value={OTP}
onChange={(e) => setOTP(e.target.value)}
onChange={handleChange}
/>
<br />
</div>
<div className="mt-2.5">
</div>
<div>
{loading ? (
<button
className="op-btn op-btn-primary"
type="submit"
disabled={loading}
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
type="button"
disabled
>
{loading ? "Loading..." : "Verify"}
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
</button>
</div>
</form>
)}
</>
) : (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
<legend className="text-[12px] text-[#878787] mt-2">
Provide your details
</legend>
<form
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"
onSubmit={handleUserData}
>
<div className="mb-2">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={contact.name}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
required
/>
) : (
<button
type="button"
onClick={(e) => VerifyOTP(e)}
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
>
Verify
</button>
)}
</div>
<div className="mb-2">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={contact.email}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
required
disabled
/>
</div>
<div className="mt-2.5">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
</label>
<input
type="text"
name="phone"
value={contact.phone}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
/>
</div>
<div className="mt-2 flex justify-start">
<button
type="submit"
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Next"}
</button>
</div>
</form>
</div>
</div>
)}
</div>
+194 -156
View File
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import "../styles/loader.css";
import { useDispatch } from "react-redux";
import axios from "axios";
import Title from "../components/Title";
@@ -9,13 +10,16 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const";
import {
isEnableSubscription,
modalCancelBtnColor,
modalSubmitBtnColor
} from "../constant/const";
import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchSubscription, getAppLogo, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { fetchSubscription, getAppLogo } from "../constant/Utils";
function Login() {
const navigate = useNavigate();
const location = useLocation();
@@ -41,7 +45,6 @@ function Login() {
});
const [isModal, setIsModal] = useState(false);
const [image, setImage] = useState();
const [isLoginSSO, setIsLoginSSO] = useState(false);
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
@@ -260,7 +263,7 @@ function Login() {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
phone: results[0].get("Phone"),
company: results[0].get("Company")
};
localStorage.setItem(
@@ -314,7 +317,7 @@ function Login() {
const LocalUserDetails = {
name: _user.name,
email: email,
phone: _user?.phone || ""
phone: _user.phone
// company: results.get("Company"),
};
localStorage.setItem(
@@ -865,7 +868,7 @@ function Login() {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
phone: results[0].get("Phone"),
company: results[0].get("Company")
};
localStorage.setItem(
@@ -902,7 +905,7 @@ function Login() {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || ""
phone: results[0].get("Phone")
// company: results.get("Company"),
};
localStorage.setItem(
@@ -979,7 +982,7 @@ function Login() {
userDetails: {
name: userInformation.name,
email: userInformation.email,
phone: userInformation?.phone || "",
phone: userInformation.phone,
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
@@ -991,7 +994,7 @@ function Login() {
const LocalUserDetails = {
name: userInformation.name,
email: userInformation.email,
phone: userInformation?.phone || "",
phone: userInformation.phone,
company: userDetails.Company,
jobTitle: userDetails.JobTitle
};
@@ -1013,9 +1016,8 @@ function Login() {
const handleCloseModal = () => {
setIsModal(false);
if (Parse?.User?.current()) {
Parse.User.logOut();
}
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
@@ -1038,174 +1040,201 @@ function Login() {
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
};
// `handleSignInWithSSO` is trigger when user click sign in with sso and open sso authorize endpoint
const handleSignInWithSSO = () => {
if (isLoginSSO === false) {
if (state?.email) {
setIsLoginSSO(true);
const encodedEmail = encodeURIComponent(state.email);
const clientUrl = window.location.origin;
const domain = state.email.split("@")?.pop();
const ssoApiUrl =
process.env.SSO_API_URL || "https://sso.opensignlabs.com/api";
openInNewTab(
`${ssoApiUrl}/oauth/authorize?response_type=code&provider=saml&tenant=${domain}&product=OpenSign&redirect_uri=${clientUrl}/sso&state=${encodedEmail}`,
"_self"
);
} else {
alert("Please provide email.");
}
}
};
return (
<div>
<div className="bg-white">
<Title title={"Login Page"} />
{state.loading && (
<div
aria-live="assertive"
className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50"
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<Loader />
<div
role="status"
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)"
}}
className="loader-37"
>
<span className="sr-only">Loading...</span>
</div>
</div>
)}
{appInfo && appInfo.appId ? (
<>
<div
aria-labelledby="loginHeading"
role="region"
className="md:p-10 lg:p-16"
>
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div aria-labelledby="loginHeading" role="region">
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-4 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{image && (
<img
src={image}
className="object-contain h-full"
alt="applogo"
alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly."
/>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit} aria-label="Login Form">
<h1 className="text-[30px] mt-6">Welcome Back!</h1>
<fieldset>
<legend className="text-[12px] text-[#878787]">
Login to your account
</legend>
<div className="w-full px-6 py-3 my-1 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<label className="block text-xs" htmlFor="email">
Email
</label>
<input
id="email"
type="email"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="email"
autoComplete="username"
value={state.email}
onChange={handleChange}
required
/>
<hr className="my-1 border-none" />
{!isLoginSSO && (
<>
<label className="block text-xs" htmlFor="password">
Password
</label>
<div className="relative">
<input
id="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"
name="password"
value={state.password}
autoComplete="current-password"
onChange={handleChange}
required
/>
<span
className="absolute cursor-pointer top-[50%] right-[10px] -translate-y-[50%] text-base-content"
onClick={togglePasswordVisibility}
>
{state.passwordVisible ? (
<i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon
) : (
<i className="fa fa-eye text-xs pb-1 " /> // Open eye icon
)}
</span>
</div>
</>
)}
<div className="relative mt-1">
<div>
<form onSubmit={handleSubmit} aria-label="Login Form">
<h1 className="text-[30px] mt-6">Welcome Back!</h1>
<fieldset>
<legend className="text-[12px] text-[#878787]">
Login to your account
</legend>
<div className="px-6 py-4 outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
<label className="block text-xs" htmlFor="email">
Username
</label>
<input
id="email"
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
name="email"
value={state.email}
onChange={handleChange}
required
/>
<hr className="my-2 border-none" />
<label className="block text-xs" htmlFor="password">
Password
</label>
<div className="relative">
<input
id="password"
type={state.passwordVisible ? "text" : "password"}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
name="password"
value={state.password}
onChange={handleChange}
required
/>
<span
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
state.passwordVisible
? "text-[#007bff]"
: "text-black"
}`}
onClick={togglePasswordVisibility}
>
{state.passwordVisible ? (
<i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon
) : (
<i className="fa fa-eye text-xs pb-1 " /> // Open eye icon
)}
</span>
</div>
</div>
</fieldset>
<div className="flex flex-row justify-between items-center ml-4 py-2">
<div>
<label
className="form-check-label inline-block cursor-pointer"
htmlFor="rememberpassword"
>
<input
type="checkbox"
className="form-check-input mr-1"
value=""
id="rememberpassword"
/>
<span className="text-[13px]">
Remember Password
</span>
</label>
</div>
<div>
<NavLink
to="/forgetpassword"
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
className="text-[13px] underline focus:outline-none focus:ring-2 focus:ring-blue-600"
>
Forgot Password?
Forgot Password ?
</NavLink>
</div>
</div>
</fieldset>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
<button
type="submit"
className="op-btn op-btn-primary"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Login"}
</button>
<button
type="button"
className="op-btn op-btn-accent"
disabled={state.loading}
onClick={() =>
navigate(
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
<button
type="submit"
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Login"}
</button>
<NavLink
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
to={
location.search
? "/signup" + location.search
: "/signup"
)
}
>
Create Account
</button>
</div>
</form>
{(appInfo.googleClietId || isEnableSubscription) && (
<div className="op-divider my-4 text-sm">OR</div>
)}
<div className="flex flex-col justify-center items-center gap-y-3">
{/* {appInfo?.fbAppId && (
}
style={width < 768 ? { textAlign: "center" } : {}}
>
Create Account
</NavLink>
</div>
</form>
<br />
{(appInfo.fbAppId || appInfo.googleClietId) && (
<div className="text-sm flex justify-center items-center">
<hr className="border-[1px] border-gray-300 w-full" />
<span className="px-2 text-gray-500 cursor-default">
OR
</span>
<hr className="border-[1px] border-gray-300 w-full" />
</div>
)}
<br />
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{/* {appInfo.fbAppId && appInfo.fbAppId !== "" ? (
<LoginFacebook
FBCred={appInfo.fbAppId}
thirdpartyLoginfn={thirdpartyLoginfn}
thirdpartyLoader={state.thirdpartyLoader}
setThirdpartyLoader={setThirdpartyLoader}
/>
)} */}
{appInfo?.googleClietId && (
<GoogleSignInBtn
GoogleCred={appInfo.googleClietId}
thirdpartyLoginfn={thirdpartyLoginfn}
thirdpartyLoader={state.thirdpartyLoader}
setThirdpartyLoader={setThirdpartyLoader}
/>
)}
{isEnableSubscription && (
<div
className="cursor-pointer border-[1px] border-gray-300 rounded px-[40px] py-2 font-semibold text-sm hover:border-[#d2e3fc] hover:bg-[#ecf3feb7]"
onClick={() => handleSignInWithSSO()}
>
Sign in with SSO
</div>
)}
) : null} */}
</div>
<div style={{ margin: "10px 0" }}></div>
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{appInfo.googleClietId && appInfo.googleClietId !== "" ? (
<GoogleSignInBtn
GoogleCred={appInfo.googleClietId}
thirdpartyLoginfn={thirdpartyLoginfn}
thirdpartyLoader={state.thirdpartyLoader}
setThirdpartyLoader={setThirdpartyLoader}
/>
) : null}
</div>
</div>
</div>
{width >= 768 && (
<div className="place-self-center">
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
<img
src={login_img}
@@ -1220,18 +1249,18 @@ function Login() {
<Alert type={state.alertType}>{state.alertMsg}</Alert>
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<form className="px-4 py-3 text-base-content">
<form className="px-4 py-3">
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span className="text-[red] text-[13px]">*</span>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
@@ -1250,11 +1279,11 @@ function Login() {
className="block text-xs text-gray-700 font-semibold"
>
Job Title
<span className="text-[red] text-[13px]">*</span>
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
@@ -1266,18 +1295,23 @@ function Login() {
required
/>
</div>
<div className="mt-4 gap-2 flex flex-row">
<div className="mt-4">
<button
type="button"
className="op-btn op-btn-primary"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
onClick={(e) => handleSubmitbtn(e)}
style={{
marginRight: 10,
backgroundColor: modalSubmitBtnColor
}}
>
Login
</button>
<button
type="button"
className="op-btn op-btn-ghost"
className="py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 75, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
@@ -1287,11 +1321,15 @@ function Login() {
</>
) : (
<div
aria-live="assertive"
className="fixed w-full h-full flex justify-center items-center z-50"
>
<Loader />
</div>
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
)}
</div>
);
+227 -141
View File
@@ -6,8 +6,6 @@ import { toDataUrl } from "../constant/Utils";
import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { SaveFileSize } from "../constant/saveFileSize";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
const ManageSign = () => {
const appName = appInfo.appname;
const [penColor, setPenColor] = useState("blue");
@@ -19,7 +17,7 @@ const ManageSign = () => {
const [isvalue, setIsValue] = useState(false);
const allColor = ["blue", "red", "black"];
const [isLoader, setIsLoader] = useState(true);
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
const [isSuccess, setIsSuccess] = useState(false);
const [Initials, setInitials] = useState("");
const [isInitials, setIsInitials] = useState(false);
const [id, setId] = useState("");
@@ -187,20 +185,12 @@ const ManageSign = () => {
updateSign.set("UserId", userId);
const res = await updateSign.save();
setIsLoader(false);
setIsAlert({
type: "success",
message: "Signature saved successfully."
});
setTimeout(() => setIsAlert({}), 2000);
setIsSuccess(true);
return res;
} catch (err) {
console.log(err);
setIsLoader(false);
setIsAlert({
type: "danger",
message: `${err.message}`
});
setTimeout(() => setIsAlert({}), 2000);
alert(`${err.message}`);
}
} else {
try {
@@ -211,24 +201,21 @@ const ManageSign = () => {
updateSign.set("UserId", userId);
const res = await updateSign.save();
setIsLoader(false);
setIsAlert({
type: "success",
message: "Signature saved successfully."
});
setTimeout(() => setIsAlert({}), 2000);
setIsSuccess(true);
return res;
} catch (err) {
console.log(err);
setIsLoader(false);
setIsAlert({
type: "success",
message: `${err.message}`
});
setTimeout(() => setIsAlert({}), 2000);
alert(`${err.message}`);
}
}
};
const handleSignatureBtn = () => {
if (imageRef.current) {
imageRef.current.value = "";
}
setImage("");
};
const handleUploadBtn = () => {
imageRef.current.click();
};
@@ -237,130 +224,213 @@ const ManageSign = () => {
setIsValue(true);
};
return (
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
<div className="relative h-full bg-white flex shadow rounded overflow-auto">
{isLoader && (
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
<Loader />
<div className="absolute bg-black bg-opacity-75 z-50 w-full h-full flex justify-center items-center">
<div
style={{ color: "#3dd3e0" }}
className="loader-37 text-[45px]"
></div>
</div>
)}
{isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
<div className="relative w-full">
<div className="ml-[5px] my-[20px] md:m-[20px]">
<div className="text-[20px] font-semibold m-[10px] md:m-0 mb-2">
{isSuccess && (
<div
className="alert alert-success successBox"
role="alert"
onAnimationEnd={() => setIsSuccess(false)}
>
Signature saved successfully!
</div>
)}
<div
className="mainDiv"
style={{
width: "100%",
paddingRight: "10px"
}}
>
<div style={{ margin: 20 }}>
<div
style={{
fontWeight: "700",
fontSize: 15,
color: "#000",
paddingBottom: 8
}}
>
My Signature
</div>
<div className="flex flex-col md:flex-row gap-0 md:gap-[12px]">
<div className="relative">
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
Signature
</span>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/*"
ref={imageRef}
hidden
/>
<div className="relative">
<div>
{image ? (
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box">
<img
alt="signature"
src={image}
className="w-full h-full object-contain"
/>
</div>
) : (
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
width: "456px",
height: "180px",
className: "signatureCanvas rounded-box"
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
<div className="signBlock mt-4">
<div>
<div style={{ position: "relative" }}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "50%",
paddingLeft: 10
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
gap: 10,
marginBottom: 10
}}
>
<>
<span
onClick={() => handleSignatureBtn()}
className="signature"
>
Signature
</span>
</>
<span
onClick={() => handleUploadBtn()}
className="imgupload"
>
Upload Image
</span>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/*"
ref={imageRef}
hidden
/>
)}
<div className="penContainerDefault flex flex-row justify-between">
<div>
{!image && (
<div className="flex flex-row">
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
</div>
</div>
<div style={{ position: "relative" }}>
<div>
{image ? (
<div
style={{
position: "relative",
border: "2px solid #888"
}}
className="signatureCanvas"
>
<img
alt="preview image"
src={image}
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
/>
</div>
) : (
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
width: "456px",
height: "180px",
className: "signatureCanvas"
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
/>
)}
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between"
}}
className="penContainerDefault"
>
<div>
{!image && (
<div
style={{ display: "flex", flexDirection: "row" }}
>
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa-light fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
)}
</div>
<div className="flex flex-row gap-2 text-sm md:text-base mr-1">
<div
type="button"
className="op-link"
onClick={() => handleUploadBtn()}
>
Upload image
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
)}
</div>
<div
type="button"
className="op-link"
onClick={() => handleClear()}
>
Clear
<div>
<div
type="button"
className="clearbtn cursor-pointer"
onClick={() => handleClear()}
>
Clear
</div>
</div>
</div>
</div>
{warning && (
<div
className="warning signWarning"
style={{ fontSize: 12 }}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please upload signature/Image
</div>
)}
</div>
{warning && (
<span className="customwarning signWarning text-[12px] w-[220px] md:w-[300px]">
<i className="fa-light fa-exclamation-circle text-[#fab005] text-[15px] mr-[4px]"></i>
Please upload signature/Image
</span>
)}
</div>
</div>
<div className="relative">
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
Initials
</span>
<div style={{ position: "relative" }}>
<div style={{ margin: "6px 5px 18px" }}>
<span className="signature">Initials</span>
</div>
<div>
{isInitials ? (
<div className="intialSignature relative border-[1px] border-[#888] rounded-box overflow-hidden">
<div
style={{
position: "relative",
border: "2px solid #888"
}}
className="intialSignature"
>
<img
alt="inititals"
alt="preview image"
src={Initials}
style={{
width: "100%",
@@ -374,7 +444,7 @@ const ManageSign = () => {
ref={initailsRef}
penColor={initialPen}
canvasProps={{
className: "intialSignature rounded-box"
className: "intialSignature"
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
@@ -383,10 +453,17 @@ const ManageSign = () => {
dotSize={1}
/>
)}
<div className="flex flex-row justify-between w-[183px]">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between"
}}
className="penContainerInitial"
>
<div>
{!isInitials && (
<div className="flex flex-row">
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
@@ -397,10 +474,10 @@ const ManageSign = () => {
key === 0 && initialPen === "blue"
? "2px solid blue"
: key === 1 && initialPen === "red"
? "2px solid red"
: key === 2 && initialPen === "black"
? "2px solid black"
: "2px solid white"
? "2px solid red"
: key === 2 && initialPen === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
@@ -412,7 +489,7 @@ const ManageSign = () => {
}
}}
key={key}
className="fa-light fa-pen-nib"
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
@@ -424,7 +501,7 @@ const ManageSign = () => {
<div>
<div
type="button"
className="op-link text-sm md:text-base mr-1"
className="clearbtn cursor-pointer"
onClick={() => handleClearInitials()}
>
Clear
@@ -432,11 +509,20 @@ const ManageSign = () => {
</div>
</div>
</div>
{/* {!warning && (
<div className="warning signWarning" style={{ fontSize: 12 }}>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please upload signature/Image
</div>
)} */}
</div>
</div>
<div className="pt-3 ml-2 md:ml-0">
<div style={{ paddingTop: 10 }}>
<button
className="op-btn op-btn-primary"
className="customBtn successBtn"
onClick={(e) => handleSubmit(e)}
>
save
+412 -274
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useState, useRef } from "react";
import "../styles/opensigndrive.css";
import { iconColor } from "../constant/const";
import loader from "../assets/images/loader2.gif";
import { themeColor, iconColor } from "../constant/const";
import { getDrive } from "../constant/Utils";
import { useNavigate } from "react-router-dom";
import Title from "../components/Title";
@@ -9,15 +10,27 @@ import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Tour from "reactour";
import axios from "axios";
import Loader from "../primitives/Loader";
const DriveBody = React.lazy(
() => import("../components/opensigndrive/DriveBody")
);
const AppLoader = () => {
const Loader = () => {
return (
<div className="h-[100vh] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
};
@@ -266,8 +279,7 @@ function Opensigndrive() {
setNewFolderName(value);
};
//function for create folder
const handleAddFolder = async (e) => {
e.preventDefault();
const handleAddFolder = async () => {
if (newFolderName) {
setIsFolderLoader(true);
const getParentObjId = folderName[folderName.length - 1];
@@ -288,7 +300,6 @@ function Opensigndrive() {
const exsitQuery = new Parse.Query(foldercls);
exsitQuery.equalTo("Name", newFolderName);
exsitQuery.equalTo("Type", "Folder");
exsitQuery.notEqualTo("IsArchive", true);
if (parentId) {
exsitQuery.equalTo("Folder", folderPtr);
}
@@ -438,10 +449,21 @@ function Opensigndrive() {
<React.Fragment key={id}>
<span
onClick={() => handleRoute(id, folderData)}
className="text-[#a64b4e] font-normal cursor-pointer"
style={{
color: "#a64b4e",
fontWeight: "400",
cursor: "pointer"
}}
>
{data.name}
<span className="text-[#a64b4e] font-extralight cursor-pointer mx-[4px]">
<span
style={{
color: "#a64b4e",
fontWeight: "200",
cursor: "pointer",
margin: "0 4px"
}}
>
&gt;
</span>
</span>
@@ -522,295 +544,411 @@ function Opensigndrive() {
}
}
return (
<div className="bg-base-100 text-base-content rounded-box w-full shadow-md">
<div style={{ backgroundColor: "white" }} className="folderComponent">
<Title title={"OpenSign™ Drive"} drive={true} />
<ModalUi
isOpen={isAlert.isShow}
title={"Alert"}
handleClose={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
>
<div className="h-full p-[20px] pb-[15px]">
<p>{isAlert.alertMessage}</p>
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
<button
onClick={() =>
setIsAlert({
isShow: false,
alertMessage: ""
})
}
type="button"
className="op-btn op-btn-neutral op-btn-sm"
>
Close
</button>
</div>
</ModalUi>
<ModalUi
isOpen={isFolder}
title={"Add New Folder"}
handleClose={oncloseFolder}
>
<div className="h-full p-[20px] pt-[10px] pb-[15px]">
{folderLoader ? (
<div className="h-[200px] flex justify-center items-center">
<Loader />
</div>
) : (
<form
onSubmit={handleAddFolder}
className="flex flex-col text-base-content"
>
<label className="py-[8px] text-[15px] font-[400] mb-0">
Name
<span className="text-[red]">*</span>
</label>
<input
required
className="op-input op-input-bordered op-input-sm"
type="text"
value={newFolderName}
onChange={(e) => handleFolderName(e)}
/>
<span className="text-[red] text-[12px] mt-[6px]">{error}</span>
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
<div className="flex flex-row">
<button type="submit" className="op-btn op-btn-primary">
Add
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-1"
onClick={oncloseFolder}
>
Close
</button>
</div>
</form>
)}
</div>
</ModalUi>
{isLoading.isLoad ? (
<div className="flex flex-col justify-center items-center h-[100vh] w-full">
<Loader />
<span className="text-[13px] text-base-content">
{isLoading.message}
</span>
</div>
) : handleError ? (
<div className="flex justify-center items-center h-[100vh] w-full">
<span className="text-[20px] text-base-content">{handleError}</span>
</div>
) : (
<>
<div className="flex flex-row justify-between items-center px-[25px] pt-[20px]">
{tourData && (
<Tour
onRequestClose={closeTour}
steps={tourData}
isOpen={isTour}
closeWithMask={false}
scrollOffset={-100}
rounded={5}
/>
)}
<div>
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlert.isShow}
title={"Alert"}
handleClose={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
>
<div style={{ height: "100%", padding: 20 }}>
<p>{isAlert.alertMessage}</p>
<div
data-tut="reactourFirst"
onMouseEnter={(e) => handleMouseEnter(e)}
ref={scrollRef}
className="w-full whitespace-nowrap cursor-pointer select-none overflow-x-auto"
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() =>
setIsAlert({
isShow: false,
alertMessage: ""
})
}
type="button"
className="finishBtn cancelBtn"
>
{handleFolderTab(folderName)}
</div>
<div className="flex flex-row items-center">
Close
</button>
</div>
</ModalUi>
<ModalUi
isOpen={isFolder}
title={"Add New Folder"}
handleClose={oncloseFolder}
>
<div style={{ height: "100%", padding: 20 }}>
{folderLoader ? (
<div
id="folder-menu"
className={
isNewFol ? "dropdown show dropDownStyle" : "dropdown"
}
onClick={() => setIsNewFol(!isNewFol)}
style={{
height: "200px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}
>
<div className="sort" data-tut="reactourSecond">
<i
className="fa-light fa-plus-square"
aria-hidden="true"
style={{ fontSize: "25px", color: `${iconColor}` }}
></i>
</div>
<div
className={isNewFol ? "dropdown-menu show" : "dropdown-menu"}
aria-labelledby="dropdownMenuButton"
aria-expanded={isNewFol ? "true" : "false"}
>
<div className="flex flex-col">
<span
className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => setIsFolder(true)}
>
<i
className="fa-light fa-plus mr-[5px]"
aria-hidden="true"
></i>
Create folder
</span>
<span
className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/sHAnZphf69")}
>
<i className="fa-light fa-pen-nib mr-[5px]"></i>
Sign Yourself
</span>
<span
className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/8mZzFxbG1z")}
>
<i className="fa-light fa-file-signature mr-[5px]"></i>
Request Signatures
</span>
</div>
</div>
<img
alt="loader img"
src={loader}
style={{ width: "50px", height: "50px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
Loading...
</span>
</div>
<div
id="menu-container"
className={isShowSort ? "dropdown show" : "dropdown"}
onClick={() => setIsShowSort(!isShowSort)}
) : (
<form
onSubmit={(e) => {
e.preventDefault();
handleAddFolder();
}}
style={{ display: "flex", flexDirection: "column" }}
>
<div
data-tut="reactourThird"
className="sort "
data-toggle="dropdown"
<label
style={{
margin: "10px 0px 10px 0px",
fontSize: "15px",
fontWeight: "400"
}}
>
<i
className="fa-light fa-sort-amount-asc mr-[5px] text-[14px]"
aria-hidden="true"
style={{ color: `${iconColor}` }}
></i>
<span style={{ fontSize: "15px", color: `${iconColor}` }}>
{selectedSort}
</span>
</div>
Name
<span style={{ color: "red" }}>*</span>
</label>
<input
required
className="form-control inputStyle"
type="text"
value={newFolderName}
onChange={(e) => handleFolderName(e)}
// className="addFolderInput"
/>
<span
style={{ color: "red", fontSize: "12px", marginTop: "6px" }}
>
{error}
</span>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div style={{ display: "flex", flexDirection: "row" }}>
<button
style={{ background: themeColor }}
type="submit"
className="finishBtn"
>
Add
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={oncloseFolder}
>
Close
</button>
</div>
</form>
)}
</div>
</ModalUi>
{isLoading.isLoad ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%",
flexDirection: "column"
}}
>
<img
alt="loader img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
) : handleError ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%"
}}
>
<span style={{ fontSize: "20px", color: "gray" }}>
{handleError}
</span>
</div>
) : (
<>
<div className="folderContainer">
{tourData && (
<Tour
onRequestClose={closeTour}
steps={tourData}
isOpen={isTour}
closeWithMask={false}
scrollOffset={-100}
rounded={5}
/>
)}
<div
data-tut="reactourFirst"
onMouseEnter={(e) => handleMouseEnter(e)}
ref={scrollRef}
style={{
width: "100%"
}}
className="folderPath"
>
{handleFolderTab(folderName)}
</div>
<div className="dropMenuBD">
<div
id="folder-menu"
className={
isShowSort ? "dropdown-menu show" : "dropdown-menu"
isNewFol ? "dropdown show dropDownStyle" : "dropdown"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowSort ? "true" : "false"}
onClick={() => setIsNewFol(!isNewFol)}
>
{sortingValue.map((value, ind) => {
return (
<span
key={ind}
onClick={() => {
setSelectedSort(value);
sortingData(value, null, pdfData);
}}
className="dropdown-item text-[10px] md:text-[13px]"
style={{
paddingLeft: selectedSort !== value && "31px"
}}
>
{selectedSort === value && (
<i
className="fa-light fa-check"
aria-hidden="true"
></i>
)}
<span className="ml-[5px]">{value}</span>
</span>
);
})}
<hr className="hrStyle" />
{sortOrder.map((order, ind) => {
return (
<span
key={ind}
onClick={() => {
setSortingOrder(order);
sortingData(null, order, pdfData);
}}
className="dropdown-item text-[10px] md:text-[13px]"
style={{
paddingLeft: sortingOrder !== order && "31px"
}}
>
{sortingOrder === order && (
<i
className="fa-light fa-check"
aria-hidden="true"
></i>
)}
<span className="ml-[5px]">{order}</span>
</span>
);
})}
</div>
</div>
<div>
{isList ? (
<div className="sort" onClick={() => setIsList(!isList)}>
<div className="sort" data-tut="reactourSecond">
<i
onClick={() => setIsList(!isList)}
className="fa-light fa-th-large"
style={{ fontSize: "24px", color: `${iconColor}` }}
className="fa fa-plus-square"
aria-hidden="true"
style={{ fontSize: "25px", color: `${iconColor}` }}
></i>
</div>
) : (
<div
data-tut="reactourForth"
className="sort"
onClick={() => setIsList(!isList)}
className={
isNewFol ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isNewFol ? "true" : "false"}
>
{" "}
<div
style={{
display: "flex",
flexDirection: "column"
}}
>
<span
className="dropdown-item itemColor"
onClick={() => setIsFolder(true)}
>
<i
style={{ marginRight: "5px" }}
className="fa fa-plus"
aria-hidden="true"
></i>
Create folder
</span>
<span
className="dropdown-item itemColor"
onClick={() => navigate("/form/sHAnZphf69")}
>
<i
style={{ marginRight: "5px" }}
className="fas fa-pen-nib"
></i>
Sign Yourself
</span>
<span
className="dropdown-item itemColor"
onClick={() => navigate("/form/8mZzFxbG1z")}
>
{" "}
<i
style={{ marginRight: "5px" }}
className="fa fa-file-signature"
></i>
Request Signatures{" "}
</span>
</div>
</div>
</div>
<div
id="menu-container"
className={isShowSort ? "dropdown show" : "dropdown"}
onClick={() => setIsShowSort(!isShowSort)}
>
<div
data-tut="reactourThird"
className=" sort "
data-toggle="dropdown"
>
<i
className="fa-light fa-list"
className="fa fa-sort-amount-asc"
aria-hidden="true"
style={{ fontSize: "23px", color: `${iconColor}` }}
style={{
marginRight: "5px",
fontSize: "14px",
color: `${iconColor}`
}}
></i>
<span
style={{
fontSize: "15px",
color: `${iconColor}`
}}
>
{selectedSort}
</span>
</div>
)}
<div
className={
isShowSort ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowSort ? "true" : "false"}
>
{sortingValue.map((value, ind) => {
return (
<span
key={ind}
onClick={() => {
setSelectedSort(value);
sortingData(value, null, pdfData);
}}
className="dropdown-item itemColor"
style={{
paddingLeft: selectedSort !== value && "33px"
}}
>
{selectedSort === value && (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
{value}
</span>
);
})}
<hr className="hrStyle" />
{sortOrder.map((order, ind) => {
return (
<span
key={ind}
onClick={() => {
setSortingOrder(order);
sortingData(null, order, pdfData);
}}
className="dropdown-item itemColor"
style={{
paddingLeft: sortingOrder !== order && "33px"
}}
>
{sortingOrder === order && (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
{order}
</span>
);
})}
</div>
</div>
<div>
{isList ? (
<div className="sort" onClick={() => setIsList(!isList)}>
<i
onClick={() => setIsList(!isList)}
className="fa fa-th-large"
style={{ fontSize: "24px", color: `${iconColor}` }}
aria-hidden="true"
></i>
</div>
) : (
<div
data-tut="reactourForth"
className="sort"
onClick={() => setIsList(!isList)}
>
<i
className="fa fa-list"
aria-hidden="true"
style={{ fontSize: "23px", color: `${iconColor}` }}
></i>
</div>
)}
</div>
</div>
</div>
</div>
{pdfData && pdfData.length === 0 ? (
<div className="flex justify-center items-center w-full h-[50vh]">
<span className="text-base-content font-bold">
No Data Found!
</span>
</div>
) : (
<div data-tut="reactourFifth">
<React.Suspense fallback={<AppLoader />}>
<DriveBody
dataTutSixth="reactourSixth"
dataTutSeventh="reactourSeventh"
pdfData={pdfData}
setFolderName={setFolderName}
setIsLoading={setIsLoading}
setDocId={setDocId}
getPdfDocumentList={getPdfDocumentList}
isDocId={docId}
setPdfData={setPdfData}
isList={isList}
setIsAlert={setIsAlert}
setSkip={setSkip}
sortingData={sortingData}
/>
{loading && (
<div className="text-center pb-[20px]">Loading...</div>
)}
</React.Suspense>
</div>
)}
</>
)}
{pdfData && pdfData.length === 0 ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "50vh",
width: "100%"
}}
>
<span style={{ fontWeight: "bold" }}>No Data Found!</span>
</div>
) : (
<div data-tut="reactourFifth">
<React.Suspense fallback={<Loader />}>
<DriveBody
dataTutSixth="reactourSixth"
dataTutSeventh="reactourSeventh"
pdfData={pdfData}
setFolderName={setFolderName}
setIsLoading={setIsLoading}
setDocId={setDocId}
getPdfDocumentList={getPdfDocumentList}
isDocId={docId}
setPdfData={setPdfData}
isList={isList}
setIsAlert={setIsAlert}
setSkip={setSkip}
sortingData={sortingData}
/>
{loading && (
<div style={{ textAlign: "center" }}>Loading...</div>
)}
</React.Suspense>
</div>
)}
</>
)}
</div>
</div>
);
}
+5 -3
View File
@@ -4,11 +4,13 @@ import Title from "../components/Title";
const PageNotFound = ({ prefix }) => {
return (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<Title title={"Page Not Found"} />
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1>
<p className="text-[30px] lg:text-[50px]">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">
{prefix ? prefix : "Page"} Not Found
</p>
</div>
File diff suppressed because it is too large Load Diff
+97 -92
View File
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import "../styles/pgsignup.css";
import Parse from "parse";
import axios from "axios";
import { useDispatch } from "react-redux";
@@ -7,7 +8,6 @@ import { useNavigate, useLocation } from "react-router-dom";
import { appInfo } from "../constant/appinfo";
import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import Loader from "../primitives/Loader";
const PgSignUp = () => {
const navigate = useNavigate();
@@ -127,9 +127,7 @@ const PgSignUp = () => {
user.set("username", zohoRes.data.result.email);
user.set("password", obj.password);
user.set("email", zohoRes.data.result.email);
if (zohoRes?.data?.result?.phone) {
user.set("phone", zohoRes.data.result.phone);
}
user.set("phone", zohoRes.data.result.phone);
user.set("name", zohoRes.data.result.name);
const res = await user.signUp();
@@ -142,7 +140,7 @@ const PgSignUp = () => {
company: zohoRes.data.result.company,
name: zohoRes.data.result.name,
email: zohoRes.data.result.email,
phone: zohoRes?.data?.result?.phone || "",
phone: zohoRes.data.result.phone,
role: obj.role
},
subscription: zohoRes.data.result.subscription
@@ -408,101 +406,108 @@ const PgSignUp = () => {
}
};
return (
<div className="w-full h-screen">
<div className="bg-white">
<Title title={"Pgsignup page"} />
{isLoader ? (
<div className="flex justify-center items-center h-[100vh]">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="py-10 lg:p-16 flex justify-center">
<form
id="signup"
className="w-[90%] md:w-[55%] bg-base-100 text-base-content op-card shadow-md"
onSubmit={handleSubmit}
>
<div className="p-[16px]">
<h1 className="text-2xl md:text-3xl font-bold">
Choose Password
</h1>
<hr className="border-[1px] border-[#cecece] mb-[5px]" />
<label htmlFor="password">
<b className="text-[13px]">Password</b>
</label>
<input
type="password"
placeholder="Enter Password"
name="password"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.password}
onChange={handlePassowordChange}
required
/>
<label htmlFor="confirmPassword">
<b className="text-[13px]">Confirm Password</b>
</label>
<input
type="password"
placeholder="Confirm Password"
name="confirmPassword"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
style={{
border: validationMessage
? validationMessage === "Passwords match!"
? "1px solid green"
: "1px solid red"
: ""
}}
value={formData.confirmPassword}
onChange={handleConFirmPassowordChange}
required
/>
<div
className={`${
validationMessage === "Passwords match!"
? "text-[green]"
: "text-[red]"
} ml-[13px] text-[11px]`}
>
{validationMessage}
<form id="signup" className="pgsignup-content" onSubmit={handleSubmit}>
<div className="pgsignup-container">
<h1 className="text-2xl md:text-3xl font-bold">Choose Password</h1>
<hr className="hrt" />
<label htmlFor="password">
<b className="text-[13px]">Password</b>
</label>
<input
type="password"
placeholder="Enter Password"
name="password"
className="inputt"
value={formData.password}
onChange={handlePassowordChange}
required
/>
<label htmlFor="confirmPassword">
<b className="text-[13px]">Confirm Password</b>
</label>
<input
type="password"
placeholder="Confirm Password"
name="confirmPassword"
className="confirmInputt"
style={{
border: validationMessage
? validationMessage === "Passwords match!"
? "2px solid green"
: "2px solid red"
: "2px solid transparent",
outline: "none"
}}
value={formData.confirmPassword}
onChange={handleConFirmPassowordChange}
required
/>
<div
className="icon"
style={{
margin: validationMessage ? "0 2px 0 2px" : "0 2px 22px 2px",
color:
validationMessage === "Passwords match!" ? "green" : "red",
fontSize: 11
}}
>
{validationMessage}
</div>
{formData.password.length > 0 && (
<div className="mt-1 text-[11px]">
<p
className={`${
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8 characters long
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain uppercase
letter, lowercase letter, digit
</p>
<p
className={`${
specialCharValid ? "text-green-600" : "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should contain special
character
</p>
</div>
{formData.password.length > 0 && (
<div className="mt-1 text-[11px]">
<p
className={`${
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8 characters
long
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain
uppercase letter, lowercase letter, digit
</p>
<p
className={`${
specialCharValid ? "text-green-600" : "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should contain
special character
</p>
</div>
)}
<button
type="submit"
className="op-btn op-btn-primary w-full mt-[8px]"
>
)}
<div className="clearfix">
<button type="submit" className="signupbtn">
Submit
</button>
</div>
</form>
</div>
</div>
</form>
)}
</div>
);
File diff suppressed because it is too large Load Diff
+53 -41
View File
@@ -4,8 +4,6 @@ import checkmark from "../assets/images/checkmark.png";
import plansArr from "../json/plansArr";
import Title from "../components/Title";
import Parse from "parse";
import { openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
const listItemStyle = {
paddingLeft: "20px", // Add padding to create space for the image
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
@@ -18,17 +16,8 @@ const PlanSubscriptions = () => {
const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(false);
const [isLoader, setIsLoader] = useState(true);
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const user = {
name: extUser?.Name,
email: extUser?.Email,
company: extUser?.Company,
phone: extUser?.Phone
};
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
// console.log("userDetails ", userDetails);
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
const name =
userDetails && userDetails.name
? "first_name=" + encodeURIComponent(userDetails.name)
@@ -86,33 +75,55 @@ const PlanSubscriptions = () => {
<>
<Title title={"Subscriptions"} />
{isLoader ? (
<div className="flex items-center justify-center h-screen">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="overflow-y-auto max-h-full">
<div
style={{
backgroundColor: "white",
overflowY: "auto",
maxHeight: "100%"
}}
>
<div className="block my-2">
<div className="flex flex-col justify-center items-center w-full">
<div
role="tablist"
className="op-tabs op-tabs-boxed bg-base-100 my-2 shadow-lg transition-all"
>
<a
<div className="mb-6 mt-2 flex flex-row border-[1px] p-2 border-gray-300 rounded text-sm">
<span
onClick={() => setYearlyVisible(false)}
role="tab"
className={`${!yearlyVisible ? "op-tab-active" : ""} op-tab`}
className={`${
!yearlyVisible
? "bg-[#002862] text-white"
: "bg-white text-black"
} px-4 py-1 rounded cursor-pointer`}
>
Monthly
</a>
<a
</span>
<span
onClick={() => setYearlyVisible(true)}
role="tab"
className={`${yearlyVisible ? "op-tab-active" : ""} op-tab`}
className={`${
yearlyVisible
? "bg-[#002862] text-white"
: "bg-white text-black"
} px-4 py-1 rounded cursor-pointer`}
>
Yearly (10% off)
</a>
</span>
</div>
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
<ul className="flex flex-col md:flex-row h-full bg-white justify-center">
{plansArr.map((item) => (
<li
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
@@ -187,7 +198,9 @@ const PlanSubscriptions = () => {
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
{item.benefits.map((subitem, index) => (
<li style={listItemStyle} key={index} className="m-1">
<span className="relative">{subitem}</span>
<span style={{ position: "relative" }}>
{subitem}
</span>
</li>
))}
</ul>
@@ -195,23 +208,22 @@ const PlanSubscriptions = () => {
))}
</ul>
</div>
<div className="text-sm flex justify-center items-center mt-3 mb-2">
<hr className="border-[2px] border-gray-350 w-[20%]" />
<span className="px-2 text-base-content cursor-default">or</span>
<hr className="border-[2px] border-gray-350 w-[20%]" />
<div className="text-sm flex justify-center items-center">
<hr className="border-[1px] border-gray-300 w-[20%]" />
<span className="px-2 text-gray-500 cursor-default">or</span>
<hr className="border-[1px] border-gray-300 w-[20%]" />
</div>
<div className="flex flex-col justify-center items-center">
<div className="flex flex-col justify-center w-full items-center">
<h3 className="text-[#002862] mt-1 mb-2">
Host it yourself for free
</h3>
<div
className="op-btn op-btn-primary w-[200px]"
onClick={() =>
openInNewTab("https://github.com/OpenSignLabs/OpenSign")
}
<NavLink
to={"https://github.com/OpenSignLabs/OpenSign"}
className="bg-[#002862] w-[200px] text-center text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
target={"_blank"}
>
Visit Github
</div>
</NavLink>
</div>
</div>
</div>
+15 -3
View File
@@ -7,7 +7,6 @@ import { useParams } from "react-router-dom";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Loader from "../primitives/Loader";
const Report = () => {
const { id } = useParams();
@@ -174,8 +173,21 @@ const Report = () => {
<>
<Title title={reportName} />
{isLoader ? (
<div className="h-[100vh] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
-384
View File
@@ -1,384 +0,0 @@
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Parse from "parse";
import axios from "axios";
import { appInfo } from "../constant/appinfo";
import { isEnableSubscription } from "../constant/const";
import { fetchSubscription } from "../constant/Utils";
import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
const SSOVerify = () => {
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
const [isModal, setIsModal] = useState(false);
const [message, setMessage] = useState("Verifying SSO...");
const [isLoader, setIsLoader] = useState(false);
const [userDetails, setUserDetails] = useState({
name: "",
email: "",
phone: "",
Destination: "",
Company: ""
});
useEffect(() => {
linkUserWithSSO();
// eslint-disable-next-line
}, []);
// `linkUserWithSSO` is used to sign in or sign up a user using an SSO code and check if the user is present in the extended class
const linkUserWithSSO = async () => {
const param = new URLSearchParams(location.search);
const code = param?.get("code");
const state = param?.get("state");
try {
// The `ssosign` cloud function is used to sign in or sign up a user
const ssosign = await Parse.Cloud.run("ssosign", {
code: code,
email: state
});
localStorage.setItem("accesstoken", ssosign.sessiontoken);
// `checkExtUser` checks if the user is present in the extended class `contracts_Users` and if not, initiates the new user flow
await checkExtUser(ssosign);
} catch (err) {
setMessage("Error: " + err.message);
console.log("err", err.message);
}
};
const checkExtUser = async (ssosign) => {
const params = { email: ssosign?.email };
try {
// `isextenduser` checks if the current user is present in the extended class
const extRes = await Parse.Cloud.run("isextenduser", params);
if (extRes?.isUserExist) {
// `getUserDetails` retrieves the current user's details from the extended class
const extRes = await Parse.Cloud.run("getUserDetails", params);
if (ssosign && ssosign.sessiontoken) {
const LocalUserDetails = {
name: extRes.Name,
email: extRes.email,
phone: extRes?.get("Phone") || "",
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(ssosign.sessiontoken);
}
} else {
setIsModal(true);
setUserDetails((prev) => ({
...prev,
name: ssosign.name,
email: ssosign.email,
phone: ssosign?.phone || ""
}));
}
} catch (err) {
console.log("Err in isextenduser or getuserdetails cloud function", err);
setMessage("Error: " + err.message);
}
};
// `handleSubmitbtn` is used to create a user in the extended class
const handleSubmitbtn = async (e) => {
e.preventDefault();
setIsLoader(true);
let phone;
if (userDetails?.phone) {
phone = validateInput(userDetails?.phone);
} else {
phone = true;
}
if (userDetails.Destination && userDetails.Company && phone) {
const payload = { sessionToken: localStorage.getItem("accesstoken") };
if (payload && payload.sessionToken) {
const params = {
userDetails: {
name: userDetails.name,
email: userDetails.email,
phone: userDetails?.phone || "",
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
};
try {
const userSignUp = await Parse.Cloud.run("usersignup", params);
if (userSignUp && userSignUp.sessionToken) {
const LocalUserDetails = params.userDetails;
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
await thirdpartyLoginfn(userSignUp.sessionToken);
setIsLoader(false);
} else {
alert(userSignUp.message);
setIsLoader(false);
}
} catch (err) {
console.log("error in usersignup", err);
localStorage.removeItem("accesstoken");
alert("Something went wrong.");
setIsLoader(false);
}
} else {
localStorage.removeItem("accesstoken");
alert("Internal server error.");
setIsLoader(false);
}
} else {
alert("Please fill required details correctly.");
setIsLoader(false);
}
};
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
const thirdpartyLoginfn = async (sessionToken) => {
const baseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
try {
const validUser = await Parse.User.become(sessionToken);
if (validUser) {
localStorage.setItem("accesstoken", sessionToken);
const _user = JSON.parse(JSON.stringify(validUser));
localStorage.setItem("UserInformation", JSON.stringify(_user));
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
let userRoles = [];
const userSettings = appInfo.settings;
//Get Current user roles
const url = `${baseUrl}functions/UserGroups`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: _user.sessionToken
};
const body = { appname: appInfo.appname };
const UserGroupsRes = await axios.post(url, JSON.stringify(body), {
headers: headers
});
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || [];
if (userRoles) {
let _currentRole = "";
const excludeRoles = ["contracts_Guest", `contracts_appeditor`];
const filteredRole = userRoles.filter(
(x) => !excludeRoles.includes(x)
);
if (filteredRole?.length > 1) {
console.log("user has two roles");
// _currentRole = filteredRole.filter((x) => x === "contracts_User");
} else {
_currentRole = filteredRole?.[0] || "";
}
if (_currentRole) {
const roleSetting = userSettings?.find(
(setting) => setting.role === _currentRole
);
const redirectUrl =
location?.state?.from ||
`/${roleSetting.pageType}/${roleSetting.pageId}`;
const _role = _currentRole.replace(`${appInfo.appname}_`, "");
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem(
"extended_class",
roleSetting.extended_class
);
try {
const extUser = await Parse.Cloud.run("getUserDetails", {
email: _user.email
});
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", _extUser.Email);
localStorage.setItem("username", _extUser.Name);
localStorage.setItem("scriptId", true);
let tenentInfo = [];
const results = [extUser];
if (results) {
let extendedInfo_stringify = JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
);
if (_extUser.TenantId) {
const obj = {
tenentId: _extUser.TenantId.objectId,
tenentName: _extUser.TenantId.TenantName || ""
};
localStorage.setItem("TenantId", obj.tenentId);
tenentInfo.push(obj);
dispatch(showTenant(obj.tenentName || ""));
localStorage.setItem("TenantName", obj.tenentName || "");
}
localStorage.setItem("PageLanding", roleSetting.pageId);
localStorage.setItem("defaultmenuid", roleSetting.menuId);
localStorage.setItem("pageType", roleSetting.pageType);
if (isEnableSubscription) {
const res = await fetchSubscription();
const plan = res.plan;
const billingDate = res.billingDate;
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
localStorage.removeItem("userDetails");
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
navigate(redirectUrl);
}
}
}
} catch (err) {
alert("user not exist.");
setMessage("Error: User not exist.");
console.log("err in get extUser", err);
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} catch (err) {
console.log("err in usergroups", err);
}
}
} catch (err) {
console.log("Err in become method", err);
}
};
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
const handleCloseModal = () => {
setIsModal(false);
if (Parse?.User?.current()) {
Parse.User.logOut();
}
};
// `validateInput` is used to verify the phone pattern
function validateInput(input) {
if (input) {
const pattern = /^(?!.*\+.*\+)[\d+-]*$/;
return pattern.test(input);
} else {
return true;
}
}
return (
<div>
<div className="w-full h-screen flex flex-col justify-center items-center text-sm md:text-xl ">
{message === "Verifying SSO..." && <Loader />}
<div className="text-base-content">{message}</div>
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<div className="relative">
{isLoader && (
<div className="absolute w-full h-full bg-black bg-opacity-25 flex justify-center items-center">
<Loader />
</div>
)}
<form
className="px-4 py-3 text-base-content"
onSubmit={handleSubmitbtn}
>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="Phone"
value={userDetails.phone}
onChange={(e) =>
setUserDetails({
...userDetails,
phone: e.target.value
})
}
disabled={isLoader}
required
/>
<p className="text-[10px] text-[red] ml-2">
{!validateInput(userDetails.phone) && "Invalid phone value"}
</p>
</div>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title
<span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mt-4">
<button type="submit" className="op-btn op-btn-primary">
Login
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-2"
onClick={handleCloseModal}
>
Cancel
</button>
</div>
</form>
</div>
</ModalUi>
</div>
);
};
export default SSOVerify;
+71 -69
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import Title from "../components/Title";
import { useNavigate, useLocation } from "react-router-dom";
import { useNavigate, NavLink, useLocation } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize";
import Alert from "../primitives/Alert";
@@ -11,8 +11,7 @@ import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { getAppLogo } from "../constant/Utils";
const Signup = () => {
const { width } = useWindowSize();
const navigate = useNavigate();
@@ -34,7 +33,7 @@ const Signup = () => {
const [lengthValid, setLengthValid] = useState(false);
const [caseDigitValid, setCaseDigitValid] = useState(false);
const [specialCharValid, setSpecialCharValid] = useState(false);
const [isAuthorize, setIsAuthorize] = useState(false);
const togglePasswordVisibility = () => setShowPassword(!showPassword);
const clearStorage = async () => {
@@ -502,58 +501,80 @@ const Signup = () => {
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
};
return (
<div className="">
<div className="bg-white">
{state.loading && (
<div className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50">
<Loader />
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
</div>
)}
<Title title={"Signup page"} />
{appInfo && appInfo.applogo ? (
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div>
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{image && (
<img src={image} className="object-contain h-full" alt="logo" />
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<div className="">
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Create Account !</h2>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="outline outline-1 outline-slate-300/50 shadow-md rounded my-4">
<div className="px-6 py-4 text-xs">
<label className="block ">
Name <span className="text-[red] text-[13px]">*</span>
Name{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Email <span className="text-[red] text-[13px]">*</span>
Email{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
id="email"
type="email"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={email}
onChange={(e) =>
setEmail(e.target.value?.toLowerCase())
}
onChange={(e) => setEmail(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Phone <span className="text-[red] text-[13px]">*</span>
Phone{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
@@ -561,11 +582,11 @@ const Signup = () => {
<hr className="my-2 border-none" />
<label>
Company{" "}
<span className="text-[red] text-[13px]">*</span>
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={company}
onChange={(e) => setCompany(e.target.value)}
required
@@ -573,11 +594,11 @@ const Signup = () => {
<hr className="my-2 border-none" />
<label>
Job Title{" "}
<span className="text-[red] text-[13px]">*</span>
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={jobTitle}
onChange={(e) => setJobTitle(e.target.value)}
required
@@ -585,19 +606,21 @@ const Signup = () => {
<hr className="my-2 border-none" />
<label>
Password{" "}
<span className="text-[red] text-[13px]">*</span>
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
name="password"
value={password}
onChange={(e) => handlePasswordChange(e)}
required
/>
<span
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
showPassword ? "text-[#007bff]" : "text-black"
}`}
onClick={togglePasswordVisibility}
>
{showPassword ? (
@@ -635,62 +658,34 @@ const Signup = () => {
{specialCharValid ? "✓" : "✗"} Password should
contain special character
</p>
{/* </>
)} */}
</div>
)}
<div className="mt-2.5 ml-1 flex flex-row items-center">
<input
type="checkbox"
className="op-checkbox op-checkbox-sm"
id="termsandcondition"
checked={isAuthorize}
onChange={(e) => setIsAuthorize(e.target.checked)}
required
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
I agree to the
</label>
<span
className="underline cursor-pointer ml-1"
onClick={() =>
openInNewTab(
"https://www.opensignlabs.com/terms-and-conditions"
)
}
>
Terms of Service
</span>
<span>.</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
<button
type="submit"
className="op-btn op-btn-primary"
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow uppercase"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Register"}
</button>
<button
type="button"
className="op-btn op-btn-secondary"
disabled={state.loading}
onClick={() =>
navigate(location.search ? "/" + location.search : "/")
}
<NavLink
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
to={location.search ? "/" + location.search : "/"}
style={width < 768 ? { textAlign: "center" } : {}}
>
Login
</button>
</NavLink>
</div>
</form>
</div>
{width >= 768 && (
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
<img src={login_img} alt="loader" width="100%" />
<img src={login_img} alt="bisec" width="100%" />
</div>
</div>
)}
@@ -699,9 +694,16 @@ const Signup = () => {
<Alert type={state.alertType}>{state.alertMsg}</Alert>
</div>
) : (
<div className="fixed w-full h-full flex justify-center items-center z-50">
<Loader />
</div>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
)}
</div>
);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+132 -181
View File
@@ -1,8 +1,4 @@
import React, {
useState,
useEffect
// useRef
} from "react";
import React, { useState, useEffect } from "react";
import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -12,11 +8,10 @@ import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
import Upgrade from "../primitives/Upgrade";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
function UserProfile() {
const navigate = useNavigate();
@@ -32,10 +27,6 @@ function UserProfile() {
const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
// const [publicUserName, setPublicUserName] = useState(
// extendUser && extendUser?.[0]?.UserName
// );
// const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company
);
@@ -46,7 +37,6 @@ function UserProfile() {
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
// const [userNameError, setUserNameError] = useState("");
useEffect(() => {
getUserDetail();
}, []);
@@ -84,72 +74,46 @@ function UserProfile() {
}
}
};
//function to check public username already exist
// const handleCheckPublicUserName = async () => {
// try {
// const res = await Parse.Cloud.run("getpublicusername", {
// username: publicUserName
// });
// if (res) {
// setIsLoader(false);
// setUserNameError("user name already exist");
// setTimeout(() => {
// setUserNameError("");
// }, 3000);
// return res;
// }
// } catch (e) {
// console.log("error in getpublicusername cloud function");
// }
// };
const handleSubmit = async (e) => {
e.preventDefault();
let phn = Phone,
res = "";
//condition to call cloud function when user change publicUserName
// if (previousPublicUserName.current !== publicUserName) {
// res = await handleCheckPublicUserName();
// }
if (!res) {
setIsLoader(true);
try {
const userQuery = Parse.Object.extend("_User");
const query = new Parse.Query(userQuery);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn || "");
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res?.phone || "");
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({
Name: res.name,
Phone: res?.phone || ""
});
alert("Profile updated successfully.");
setEditMode(false);
setIsLoader(false);
//navigate("/dashboard/35KBoSgoAK");
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
setIsLoader(true);
let phn = Phone;
try {
const tour = Parse.Object.extend("_User");
const query = new Parse.Query(tour);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn);
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res.phone);
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({ Name: res.name, Phone: res.phone });
alert("Profile updated successfully.");
setEditMode(false);
navigate("/dashboard/35KBoSgoAK");
}
);
});
} catch (error) {
console.log("err", error);
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
}
);
});
} catch (error) {
console.log("err", error);
}
};
@@ -158,15 +122,14 @@ function UserProfile() {
const extClass = localStorage.getItem("extended_class");
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId;
const body = {
Phone: obj?.Phone || "",
Phone: obj.Phone,
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company
// UserName: publicUserName || ""
};
await axios.put(
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
body,
@@ -184,6 +147,7 @@ function UserProfile() {
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
// console.log("updateRes ", updateRes);
};
@@ -277,45 +241,29 @@ function UserProfile() {
setOtpLoader(false);
alert("OTP sent on you email");
};
//function to handle onchange username and restrict 6-characters username for free users
// const handleOnchangeUserName = (e) => {
// const value = e.target.value;
// if (value.length > 6 && !isSubscribe) {
// setUserNameError("Please upgrade to allow more than 6 characters.");
// setTimeout(() => {
// setUserNameError("");
// }, 2000);
// } else {
// setPublicUserName(e.target.value);
// }
// };
const handleCancel = () => {
setEditMode(false);
SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg"));
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
};
return (
<React.Fragment>
<Title title={"Profile"} />
{isLoader ? (
<div className="h-[100vh] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="flex justify-center items-center w-full relative">
{/* {userNameError && (
<div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
>
{userNameError}
</div>
)} */}
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box w-[450px]">
<div className="flex justify-center items-center w-full">
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
<img
@@ -327,7 +275,7 @@ function UserProfile() {
{editmode && (
<input
type="file"
className="op-file-input op-file-input-bordered op-file-input-sm max-w-[270px] mt-4 text-sm"
className="max-w-[270px] text-sm py-1 px-2 mt-4 border-[1px] border-[#15b4e9] text-black rounded"
accept="image/png, image/gif, image/jpeg"
onChange={(e) => {
let files = e.target.files;
@@ -353,7 +301,7 @@ function UserProfile() {
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
editmode ? "py-1" : "py-2"
}`}
>
<span className="font-semibold">Name:</span>{" "}
@@ -361,7 +309,7 @@ function UserProfile() {
<input
type="text"
value={name}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetName(e.target.value)}
/>
) : (
@@ -370,14 +318,14 @@ function UserProfile() {
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
editmode ? "py-1" : "py-2"
}`}
>
<span className="font-semibold">Phone:</span>{" "}
{editmode ? (
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetPhone(e.target.value)}
value={Phone}
/>
@@ -391,7 +339,7 @@ function UserProfile() {
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
editmode ? "py-1" : "py-2"
}`}
>
<span className="font-semibold">Company:</span>{" "}
@@ -399,7 +347,7 @@ function UserProfile() {
<input
type="text"
value={company}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => setCompany(e.target.value)}
/>
) : (
@@ -408,7 +356,7 @@ function UserProfile() {
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
editmode ? "py-1" : "py-2"
}`}
>
<span className="font-semibold">Job title:</span>{" "}
@@ -416,7 +364,7 @@ function UserProfile() {
<input
type="text"
value={jobTitle}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => setJobTitle(e.target.value)}
/>
) : (
@@ -442,37 +390,6 @@ function UserProfile() {
)}
</span>
</li>
{/* {isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">
Public profile :{" "}
<Tooltip
message={`this is your public URL. Copy or share it
with the signer, and you will be able to see
all your publicly set templates.`}
/>
</span>
<div className="flex text-xs items-center">
<span>opensign-me.vercel.app/</span>
{editmode ? (
<input
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="op-input op-input-bordered op-input-xs"
/>
)}
</div>
</li>
)} */}
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span
@@ -487,23 +404,28 @@ function UserProfile() {
url={
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
}
/>
isSubscribe={isSubscribe}
/>{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
</span>{" "}
<label
className={`${
className={
isSubscribe || !isEnableSubscription
? `${editmode ? "cursor-pointer" : ""}`
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
? `${
editmode ? "cursor-pointer" : ""
} relative inline-flex items-center mb-0`
: "relative inline-flex items-center mb-0 pointer-events-none opacity-50"
}
>
<input
disabled={editmode ? false : true}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
checked={isDisableDocId}
onChange={handleDisableDocId}
type="checkbox"
value=""
className="sr-only peer"
/>
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600"></div>
</label>
</div>
{!isEnableSubscription && (
@@ -515,29 +437,44 @@ function UserProfile() {
)}
</li>
</ul>
<div className="flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4">
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
{editmode ? (
<button
type="button"
onClick={handleSubmit}
className="rounded shadow focus:outline-none border-[2px] border-[#15b4e9] bg-white text-[#15b4e9] px-4 py-2 mr-4"
>
Save
</button>
) : (
<button
type="button"
onClick={() => {
setEditMode(true);
}}
className="rounded shadow focus:outline-none text-white bg-[#e7505a] px-4 py-2 mr-4"
>
Edit
</button>
)}
<button
type="button"
onClick={(e) =>
editmode ? handleSubmit(e) : setEditMode(true)
}
className="op-btn op-btn-primary"
>
{editmode ? "Save" : "Edit"}
</button>
<button
type="button"
onClick={() =>
editmode ? handleCancel() : navigate("/changepassword")
}
className={`op-btn ${
editmode ? "op-btn-ghost" : "op-btn-secondary"
onClick={() => {
if (editmode) {
setEditMode(false);
} else {
navigate("/changepassword");
}
}}
className={`rounded shadow focus:outline-none text-white bg-[#3598dc] ${
editmode ? "px-4 py-2 " : "p-2"
}`}
>
{editmode ? "Cancel" : "Change Password"}
</button>
</div>
</div>
{isVerifyModal && (
<ModalUi
isOpen
@@ -545,29 +482,43 @@ function UserProfile() {
handleClose={handleCloseVerifyModal}
>
{otpLoader ? (
<div className="h-[150px] flex justify-center items-center">
<Loader />
<div
style={{
height: "150px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<form onSubmit={(e) => handleVerifyEmail(e)}>
<div className="px-6 py-3 text-base-content">
<div className="px-6 py-3">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter OTP received over email"
value={otp}
onChange={(e) => setOtp(e.target.value)}
/>
</div>
<div className="px-6 mb-3">
<button type="submit" className="op-btn op-btn-primary">
<hr />
<div className="px-6 my-3">
<button type="submit" className={submitBtn}>
Verify
</button>
<button
className="op-btn op-btn-secondary ml-2"
className={`${rejectBtn} ml-2`}
onClick={(e) => handleResend(e)}
>
Resend
+148 -115
View File
@@ -1,32 +1,27 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
import Parse from "parse";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
const tourSteps = [
{
selector: '[data-tut="webhooksubscribe"]',
content: "Upgrade now to set webhook"
}
];
function Webhook() {
const navigation = useNavigate();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [webhook, setWebhook] = useState();
const [isLoader, setIsLoader] = useState(true);
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [error, setError] = useState("");
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
useEffect(() => {
fetchWebhook();
// eslint-disable-next-line
@@ -64,22 +59,31 @@ function Webhook() {
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
const res = await axios.post(url, params, { headers: headers });
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsAlert({ type: "success", msg: "Webhook added successfully." });
} else {
console.error("Error while generating webhook");
setIsAlert({ type: "danger", msg: "Something went wrong." });
}
await axios.post(url, params, { headers: headers }).then((res) => {
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating webhook");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsAlert({ type: "danger", msg: "Something went wrong." });
console.log("err while generating webhook", error);
} finally {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
setIsErr(false);
}, 1500);
console.log("err", error);
}
} else {
setError("The webhook url should always start with https://");
@@ -89,112 +93,141 @@ function Webhook() {
}
};
const handleModal = () => {
if (!isSubscribe && isEnableSubscription) {
setIsTour(true);
} else {
setIsModal(!isModal);
}
};
const handleModal = () => setIsModal(!isModal);
return (
<React.Fragment>
<Title title={"Webhook"} />
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{isGenerate && <Alert type="success">Webhook added successfully!</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div className="h-[100vh] flex justify-center items-center">
<Loader />
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign Webhook{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
isSubscribe={true}
/>
</h1>
<ul className={"w-full flex flex-col p-2 text-sm"}>
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5">
<span className="">Webhook:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
{webhook ? webhook : "_____"}
</span>
</div>
<button
type="button"
onClick={handleModal}
className="op-btn op-btn-primary"
>
{webhook ? "Update Webhook" : "Add Webhook"}
</button>
</li>
</ul>
<div className="flex items-center justify-center">
<div className="bg-white flex flex-col justify-center shadow rounded">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign Webhook{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
isSubscribe={true}
/>
</h1>
<ul
className={
isSubscribe || !isEnableSubscription
? "w-full flex flex-col p-2 text-sm "
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
}
>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
<span className="">Webhook:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
{webhook ? webhook : "_____"}
</span>
</div>
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
)
}
className="op-btn op-btn-secondary mt-2 mb-3 px-7"
onClick={handleModal}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
View Docs
{webhook ? "Update Webhook" : "Add Webhook"}
</button>
</div>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
</li>
</ul>
<div className="flex items-center justify-center ">
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
{error && <Alert type="danger">{error}</Alert>}
<div className="m-[20px]">
<div className="text-lg font-normal text-base-content">
<label className="text-sm ml-2">Webhook</label>
<input
value={webhook}
onChange={(e) => setWebhook(e.target.value)}
placeholder="Enter webhook url"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<hr className="bg-[#ccc] mt-3" />
<div className="flex items-center mt-3 gap-2">
<button
onClick={handleSubmit}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
</button>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
View Docs
</button>
</div>
{!isSubscribe && isEnableSubscription && (
<div data-tut="webhooksubscribe">
<SubscribeCard />
</div>
<>
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
Upgrade to PRO Plan
</h1>
<ul className={"w-full flex flex-col p-2 text-sm "}>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
<span className="">$29.99/month:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
First 100 documents included, then just $0.15 per
document.
</span>
</div>
{!isSubscribe && isEnableSubscription && (
<button
type="button"
onClick={() => navigation("/subscription")}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
Upgrade Now
</button>
)}
</li>
</ul>
</>
)}
</>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
{error && <Alert type="danger">{error}</Alert>}
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
<label className="text-sm ml-2">Webhook</label>
<input
value={webhook}
onChange={(e) => setWebhook(e.target.value)}
placeholder="Enter webhook url"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
No
</button>
</div>
</div>
</ModalUi>
</div>
)}
<Tour
onRequestClose={() => setIsTour(false)}
steps={tourSteps}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
scrollOffset={-100}
rounded={5}
/>
</React.Fragment>
);
}
-16
View File
@@ -1,16 +0,0 @@
// below code is used to handle undefined Promise.withResolvers for safari
if (typeof Promise.withResolvers === "undefined") {
Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
// Usage:
// const { promise, resolve, reject } = Promise.withResolvers()
// console.log(promise, resolve, reject) // Promise { <pending> } [Function (anonymous)] [Function (anonymous)]
// ... Do something async and then call resolve or reject!

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