Compare commits

...
57 Commits
Author SHA1 Message Date
AmolandGitHub b4315b5e0a Merge pull request #163 from OpenSignLabs/staging
v1.0.4
2023-11-10 02:52:04 +05:30
AmolandGitHub c326849b8d Merge pull request #162 from rishabjasrotia/issue/146 2023-11-09 21:18:53 +05:30
prafull-opensignlabsandGitHub a92188d8e2 change variable name 2023-11-09 21:18:36 +05:30
prafull-opensignlabsandGitHub 4055574785 sanitize File Name in upload file 2023-11-09 20:48:53 +05:30
AmolandGitHub 9b295f784a Merge pull request #158 from raktima-opensignlabs/placeholder-mobile-view2 2023-11-09 20:37:49 +05:30
prafull-opensignlabsandGitHub bfae7f236c Merge branch 'staging' into placeholder-mobile-view2 2023-11-09 20:36:33 +05:30
AmolandGitHub fe907116e2 Merge pull request #156 from raktima-opensignlabs/login-header3 2023-11-09 20:27:04 +05:30
rishabjasrotia ade39c2f46 Doc Updated 2023-11-09 18:36:18 +05:30
AndrewandGitHub 31baefcce3 Merge pull request #161 from rishabjasrotia/issue/32 2023-11-09 18:32:23 +05:30
RishabandGitHub 59eb4eb9c7 #32 - Change the title of popup that appears after sending out emails from "Request signatures" flow
#32 - Change the title of popup that appears after sending out emails from "Request signatures" flow
2023-11-09 18:30:37 +05:30
rishabjasrotia 3b93f96479 Fix makefile 2023-11-09 18:05:34 +05:30
rishabjasrotia dbff436bc2 #146 - Fix issue of build & Add local File upload support 2023-11-09 17:53:34 +05:30
AmolandGitHub c3cf9c891e Merge pull request #160 from maoo/patch-1 2023-11-09 17:47:54 +05:30
Maurizio PillituandGitHub 4e7c8c63bb CORS configuration instructions
See https://github.com/OpenSignLabs/OpenSign/issues/159
2023-11-09 12:57:58 +01:00
RaktimaNXG 450a971ca9 remove isSigner exist checking from local storage 2023-11-09 14:30:17 +05:30
RaktimaNXG 5ba1f79f3b replce isSigner variable name to isGuestSigner 2023-11-09 14:23:56 +05:30
AndrewandGitHub d255d5bfb2 Merge pull request #151 from prafull-opensignlabs/mf_header 2023-11-09 12:01:41 +05:30
AndrewandGitHub f5e835cbc4 Merge pull request #148 from prafull-opensignlabs/fix_signup 2023-11-09 12:01:20 +05:30
AmolandGitHub b40eb7119d Merge branch 'issue/119' into staging 2023-11-08 22:36:01 +05:30
RaktimaNXG f56faf3afd handle placeholder block position and size on mobile view 2023-11-08 21:01:09 +05:30
RaktimaNXG 5fa60d8135 solved overflow header on logiin page 2023-11-08 20:42:35 +05:30
AmolandGitHub 695345d911 Merge pull request #144 from raktima-opensignlabs/main
fix: blocking popup on view expired and declined document screens
2023-11-08 20:11:16 +05:30
prafull-opensignlabs 3f60509254 remove header from report microapp 2023-11-08 16:55:30 +05:30
RaktimaandGitHub 6bd9ac0b8f Merge pull request #12 from OpenSignLabs/staging
Update
2023-11-08 16:55:09 +05:30
AmolandGitHub ccecec5e59 Merge pull request #149 from OpenSignLabs/staging
v1.0.3
2023-11-08 14:28:32 +05:30
prafull-opensignlabs 2022df000e fix: user sign up issue for user existing in contact list 2023-11-08 13:56:28 +05:30
prafull-opensignlabsandGitHub ec6e037422 Merge pull request #10 from OpenSignLabs/staging
sync fork
2023-11-07 18:35:15 +05:30
AndrewandGitHub d4be4aec2c Merge pull request #143 from rishabjasrotia/issue/81 2023-11-07 18:09:08 +05:30
prafull-opensignlabsandGitHub 4b11fe77e0 Merge pull request #9 from OpenSignLabs/staging
sync fork
2023-11-07 17:40:34 +05:30
RishabandGitHub 4caf7b8c1a Update header.js 2023-11-07 17:31:50 +05:30
AmolandGitHub c736bbce55 Merge pull request #145 from prafull-opensignlabs/staging
remove hardcoded pfx passphrase
2023-11-07 16:30:35 +05:30
prafull-opensignlabsandGitHub 4bbf0d05d5 Merge pull request #147 from lsprr/accessibility/login
Improve accessibility of the login form
2023-11-07 16:14:40 +05:30
RaktimaNXG d42e11c5c1 fix: blocking screen by warning for expired and decline document 2023-11-07 11:32:25 +05:30
prafull-opensignlabs f227b79316 change prod and dev command 2023-11-07 11:29:08 +05:30
lsprr d4b4e02b62 Improve accessibility of the login form
- Add ARIA labels to input fields for better screen reader support
- Implement keyboard navigation for the login form elements
- Fix semantic HTML issues for assistive technologies
2023-11-06 15:57:49 -05:00
prafull-opensignlabs 1e76cf5253 fix: login url in mail change to current host 2023-11-06 20:48:01 +05:30
prafull-opensignlabs 4b9088b252 docs: update pass_phrase variable in env files 2023-11-06 20:00:10 +05:30
prafull-opensignlabs 820d6378f9 fix: add loadmf route to load login flow of microapp, add pagenotfound microapp 2023-11-06 19:44:06 +05:30
prafull-opensignlabs dfc816bdac Merge branch 'staging' of https://github.com/prafull-opensignlabs/OpenSign into staging 2023-11-06 16:51:17 +05:30
prafull-opensignlabs 0f62419d99 remove confidential passphrase param 2023-11-06 16:48:06 +05:30
prafull-opensignlabsandGitHub 37af995aaa Merge pull request #8 from OpenSignLabs/staging
sync fork
2023-11-06 16:10:54 +05:30
RishabandGitHub 4c69971ba1 Update emailComponent.js 2023-11-06 14:52:23 +05:30
RaktimaNXG fe37f3e021 fix: expired and decline document block whole screeen 2023-11-06 14:51:33 +05:30
RishabandGitHub 9047aa7dc8 Fix Printing for mobile 2023-11-06 14:49:44 +05:30
RishabandGitHub d33df9b934 Fix Print for mobile 2023-11-06 14:49:25 +05:30
RaktimaandGitHub 95558d1407 Merge pull request #11 from OpenSignLabs/staging
update
2023-11-06 14:48:05 +05:30
AndrewandGitHub 3164ee5824 Merge pull request #142 from OpenSignLabs/all-contributors/add-lsprr
docs: add lsprr as a contributor for a11y
2023-11-06 13:56:42 +05:30
allcontributors[bot]andGitHub 6a98adebd2 docs: update .all-contributorsrc 2023-11-06 08:26:13 +00:00
allcontributors[bot]andGitHub 015b752f5b docs: update README.md 2023-11-06 08:26:12 +00:00
AndrewandGitHub 11164a716f Merge pull request #140 from lsprr/lsprr/accessibility/sidebar
Refactor Menu and Social Media Components for Improved Accessibility
2023-11-06 13:55:19 +05:30
AndrewandGitHub 64638acd43 Merge pull request #141 from prafull-opensignlabs/staging 2023-11-06 13:24:20 +05:30
prafull-opensignlabsandGitHub 133769d5fd Merge pull request #7 from OpenSignLabs/staging
Merge pull request #137 from prafull-opensignlabs/staging
2023-11-06 13:19:11 +05:30
prafull-opensignlabs e330285e24 add pagination in reports, fix navigation in signup 2023-11-06 13:17:01 +05:30
lsprr 2acf851852 Refactor: Split Sidebar into Menu and SocialMedia components for modularity
- Created separate Menu and SocialMedia components from the Sidebar.
- Enhanced accessibility features in Menu.js, Sidebar.js, SubMenu.js, and SocialMedia.js.
- Adjusted NavLink components to improve keyboard navigation accessibility.
- Cleaned up and isolated component responsibilities for clarity and maintenance.
2023-11-05 15:55:38 -05:00
rishabjasrotia 73978ebce4 Fix issue 2023-11-05 19:20:34 +05:30
rishabjasrotia 96ae95a97c Fix the generation of remoteentry.js for docker development 2023-11-05 19:13:54 +05:30
rishabjasrotia 356d2e81c5 Update Document & Self Signed Cert added 2023-11-05 19:09:15 +05:30
45 changed files with 2267 additions and 1447 deletions
+9
View File
@@ -169,6 +169,15 @@
"contributions": [
"bug"
]
},
{
"login": "lsprr",
"name": "Luis Parra",
"avatar_url": "https://avatars.githubusercontent.com/u/16653744?v=4",
"profile": "https://luisparra.dev",
"contributions": [
"a11y"
]
}
]
}
+3
View File
@@ -47,3 +47,6 @@ MAILGUN_SENDER=postmaster@mail.yourdomain.com
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
PFX_BASE64=
# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=
+59 -2
View File
@@ -42,7 +42,7 @@ DO_SECRET_ACCESS_KEY=
# Digital ocean spaces region or AWS s3 region
DO_REGION=us-west
# local storage
USE_LOCAL=TRUE
USE_LOCAL=FALSE
# Email mailgun config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
MAILGUN_API_KEY=XXXXX
@@ -50,4 +50,61 @@ MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
PFX_BASE64=
PFX_BASE64='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
c6yryFgECIld0S5kDk7JAgIIAA=='
# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=
+3
View File
@@ -22,3 +22,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
apps/OpenSign/public/mfbuild/*
microfrontends/SignDocuments/build/*
apps/OpenSignServer/files/files/*
+60 -2
View File
@@ -88,5 +88,63 @@ You can install all 3 components on digital ocean using the button below -
## Localhost(Docker)
Below are the steps to follow -
- Execute `make build`
For local Setup we need to need following prerequisite:
Environment Varaibles:
| Environment Varibale | Value | Description |
| ------------- | ------------- | ------------- |
| CI | false | Set CI to false while running the app locally |
| PUBLIC_URL | http://localhost:3000 | Set it to the URL form where the app home page will be accessed |
| GENERATE_SOURCEMAP | false | Set it to true if you want to generate the Sourcemap for debugging |
| REACT_APP_SERVERURL | http://localhost:8080/app | 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_APPID | http://localhost:3000 | Set it to the URL form where the app home page will be accessed |
| PUBLIC_URL | opensignstgn | 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. |
| APP_ID | opensignstgn | A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App. |
| appName | open_sign_server | Name of the app. It will be visible in the verification emails sent out. |
| MASTER_KEY | XnAadwKxxByMr | A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database. |
| MONGODB_URI | mongodb://host.docker.internal:27017/OpenSignDB | Mongodb URI to connect to |
| PARSE_MOUNT |/app | Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions. |
| SERVER_URL | http://127.0.0.1:8080/app | Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead) |
| DO_SPACE | DOSPACENAME | Digital ocean space name or AWS S3 bucket name for uploading documents |
| DO_ENDPOINT | ams3.digitaloceanspaces.com | Digital ocean spaces endpoint or AWS S3 endpoint for uploading documents |
| DO_BASEURL |https://DOSPACENAME.ams3.digitaloceanspaces.com | Digital ocean baseurl or AWS S3 base URL |
| DO_ACCESS_KEY_ID | YOUR_S3_ACCESS_ID | Digital ocean spaces access key ID or AWS s3 Access key ID for uploading the docs |
| DO_SECRET_ACCESS_KEY | YOUR_S3_ACCESS_KEY | Digital ocean spaces secret access key or AWS s3 secret access key for uploading the docs |
| DO_REGION | YOUR_S3_REGION | Digital ocean spaces region or AWS s3 region |
| USE_LOCAL | FALSE | To use local file storage to save file |
| MAILGUN_API_KEY | YOUR_MAILGUNAPI_KEY | Mailgun API Key |
| MAILGUN_DOMAIN | YOUR_MAILGUNAPI_DOMAIN | Mailgun API Domain |
| MAILGUN_SENDER | - | Mailgun Sender Mail ID |
| PFX_BASE64 | - | Base64 encoded PFX or p12 document signing certificate file. You can generate base64 encoded self sign certificate using the passphrase `emudhra` |
# Steps to Generate Self Sign Certificate
```
# execute below command and use passphrase emudhra
openssl genrsa -des3 -out ./cert/local_dev.key 2048
openssl req -key ./cert/local_dev.key -new -x509 -days 365 -out ./cert/local_dev.crt
openssl pkcs12 -inkey ./cert/local_dev.key -in ./cert/local_dev.crt -export -out ./cert/local_dev.pfx
openssl base64 -in ./cert/local_dev.pfx -out ./cert/base64_pfx
```
# CORS Configuration
As document storage is delegated to S3-compatible services that reside in a different host than the OpenSign one, document operations (loading, storing, deleting) are subject to [Cross-Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) restriction policies; as a consequence, OpenSign app may fail with (browser console) errors like the following:
```
Access to fetch at 'https://foo.nyc3.digitaloceanspaces.com/exported_file_4627_0000-00-00T00%3A45%3A43.344Z.pdf'
from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource. If an opaque response serves your needs, set the request's mode to
'no-cors' to fetch the resource with CORS disabled.
```
In order to address this, your document storage system must be instructed to accept requests from other hosts; below the relevant documentation links:
- [How to Configure CORS on DigitalOcean Spaces](https://docs.digitalocean.com/products/spaces/how-to/configure-cors/)
- [Configuring cross-origin resource sharing on AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)
# Build Local Environment
Command to build project -
- Execute `make build`
Command to run project -
- Execute `make run`
+6
View File
@@ -1,3 +1,9 @@
build:
cp .env.local_dev .env
rm -rf apps/OpenSign/public/mfbuild
cd microfrontends/SignDocuments && npm install && npm run build
docker compose up -d
run:
cp .env.local_dev .env
docker compose up -d
+1
View File
@@ -124,6 +124,7 @@ We would like to thank all our contributors and users for their support and feed
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andrew-opensignlabs"><img src="https://avatars.githubusercontent.com/u/148278535?v=4?s=100" width="100px;" alt="Andrew"/><br /><sub><b>Andrew</b></sub></a><br /><a href="#code-andrew-opensignlabs" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://luisparra.dev"><img src="https://avatars.githubusercontent.com/u/16653744?v=4?s=100" width="100px;" alt="Luis Parra"/><br /><sub><b>Luis Parra</b></sub></a><br /><a href="#a11y-lsprr" title="Accessibility">️♿️</a></td>
</tr>
</tbody>
</table>
+1 -1
View File
@@ -21,5 +21,5 @@ EXPOSE 3000
# ENV NODE_ENV production
# Run the application
CMD ["npm", "start"]
ENTRYPOINT npm run start-dev
+2 -2
View File
@@ -40,8 +40,8 @@
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "mf-cra start",
"start-prod": "serve -s build",
"start": "serve -s build",
"start-dev": "mf-cra start",
"build": "rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
"test": "react-scripts test",
+2
View File
@@ -14,6 +14,7 @@ import PageNotFound from "./routes/PageNotFound";
import ForgetPassword from "./routes/ForgetPassword";
import ChangePassword from "./routes/ChangePassword";
import ReportMicroapp from "./components/ReportMicroapp";
import LoadMf from "./routes/LoadMf";
function App() {
const [isloading, setIsLoading] = useState(true);
@@ -59,6 +60,7 @@ function App() {
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} />
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
<>
+29 -3
View File
@@ -141,6 +141,32 @@ const GoogleSignInBtn = ({
alert("Please fill required details!");
}
};
const handleCloseModal = () => {
setIsModal(false);
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("BaseUrl12");
let appid = localStorage.getItem("AppID12");
localStorage.clear();
localStorage.setItem("appLogo", applogo);
localStorage.setItem("appName", appName);
localStorage.setItem("_appName", _appName);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("BaseUrl12", baseUrl);
localStorage.setItem("AppID12", appid);
};
return (
<div style={{ position: "relative" }}>
{thirdpartyLoader && (
@@ -259,16 +285,16 @@ const GoogleSignInBtn = ({
<div>
<button
type="button"
className="btn btn-secondary"
onClick={() => setIsModal(false)}
className="bg-[#6c757d] p-2 text-white rounded"
onClick={handleCloseModal}
style={{ marginRight: 10, width: 90 }}
>
Cancel
</button>
<button
type="button"
className="bg-[#17a2b8] p-2 text-white rounded"
onClick={() => handleSubmitbtn()}
className="btn btn-info"
>
Sign up
</button>
+26 -50
View File
@@ -1,6 +1,6 @@
import useFederatedComponent from "mf-cra";
import React, { Suspense, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
const MicroappModal = () => {
const { remoteApp } = useParams();
@@ -28,60 +28,36 @@ const MicroappModal = () => {
}
// console.log("app ", remoteApp);
const { Component: RemoteComponent } = useFederatedComponent(obj);
const navigate = useNavigate();
// console.log("RemoteComponent ", RemoteComponent);
return (
<div className="w-full h-full flex items-center justify-center ">
{/* <!-- Modal Container --> */}
<div className="bg-white rounded-lg shadow-lg w-full">
{/* <!-- Modal Header --> */}
<div className="flex w-full items-center border-b-2 mb-2 p-2">
<button
className="focus:outline-none bg-blue-500 text-white text-sm shadow px-2.5 py-1 rounded"
id="closeModal"
onClick={(e) => {
e.preventDefault();
// setIsModal(false);
navigate(-1);
<div className="w-full">
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<div
style={{
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
minHeight: "50vh"
}}
>
Back
</button>
{localStorage.getItem("domain") && (
<p className="flex-1 w-full text-sm md:text-base text-black text-center">
{localStorage.getItem("domain") === "contracts" ? "Document" : ""}
</p>
)}
</div>
{/* <!-- Modal Body --> */}
<div className="w-full">
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<div
style={{
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
minHeight: "50vh"
}}
>
{RemoteComponent && <RemoteComponent />}
</div>
</Suspense>
</div>
{RemoteComponent && <RemoteComponent />}
</div>
</Suspense>
</div>
</div>
);
@@ -82,7 +82,7 @@ const FileUpload = (props) => {
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
console.log("percentCompleted ", percentCompleted);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
@@ -0,0 +1,21 @@
import React from "react";
import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none">
<NavLink
to={`/${item.pageType}/${item.objectId}`}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
>
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{item.title}</span>
</NavLink>
</li>
)
}
export default Menu;
+34 -72
View File
@@ -1,115 +1,77 @@
import React, { useState, useEffect } from "react";
import { Link, NavLink } from "react-router-dom";
import Menu from './Menu';
import Submenu from "./SubMenu";
import dp from "../../assets/images/dp.png";
import SocialMedia from './SocialMedia';
import Parse from "parse";
import dp from "../../assets/images/dp.png";
const Sidebar = ({ isOpen, closeSidebar }) => {
const [menuList, setmenuList] = useState([]);
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("TenantName");
const [menuList, setmenuList] = useState([]);
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
menuItem();
}
}, []);
const menuItem = async () => {
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
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();
// console.log("resultjson ", resultjson);
let result = resultjson;
setmenuList(result.menuItems);
} catch (e) {
console.error("Problem", e);
}
};
return (
<div
className={`${
isOpen ? "block" : "hidden"
} bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 ${
isOpen ? "translate-x-0" : "-translate-x-full"
}`}
<aside
className={`${isOpen ? "block" : "hidden"
} bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 ${isOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<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">
<img className="w-full h-full object-contain" src={image} alt="img" />
<img className="w-full h-full object-cover" src={image} alt="Profile" />
</div>
<div>
<p className="text-[14px] font-bold">{username && username}</p>
<p
className={`text-[12px] ${tenantname && tenantname ? "mt-2" : ""}`}
>
{tenantname && tenantname}
<p className="text-[14px] font-bold">{username}</p>
<p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
{tenantname}
</p>
</div>
</div>
<ul className="text-sm">
{menuList.length > 0 && (
<>
{menuList.map((item) =>
!item.children ? (
<li key={item.title} onClick={() => closeSidebar()}>
<Link
className={`mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 hover:no-underline cursor-pointer`}
to={`/${item.pageType}/${item.objectId}`}
>
<i className={item.icon + " text-[18px]"}></i>
<span title={item.description} className="ml-3 lg:ml-4">
{item.title}
</span>
</Link>
</li>
) : (
<Submenu key={item.title} icon={item.icon} title={item.title}>
{item.children.map((item) => (
<li key={item.title} onClick={() => closeSidebar()} className="pl-6 md:pl-8 hover:bg-[#eef1f5] cursor-pointer">
<Link
className={`mx-auto flex items-center p-2 lg:p-3 hover:no-underline`}
to={`/${item.pageType}/${item.objectId}`}
>
<i className={item.icon + " text-[18px]"}></i>
<span title={item.description} className="ml-3 lg:ml-4">
{item.title}
</span>
</Link>
</li>
))}
</Submenu>
)
)}
</>
)}
</ul>
<div className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank">
<i className="fa-brands fa-github"></i>
</NavLink>
<NavLink
to="https://www.linkedin.com/company/opensign%E2%84%A2/"
target="_blank"
>
<i className="fa-brands fa-linkedin"></i>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank">
<i className="fa-brands fa-square-x-twitter"></i>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank">
<i className="fa-brands fa-discord"></i>
</NavLink>
</div>
</div>
<nav aria-label="OpenSign Sidebar Navigation">
<ul className="text-sm" role="menubar" aria-label="OpenSign Sidebar Navigation">
{menuList.map((item) => (
!item.children ? (
<Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
) : (
<Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
)
))}
</ul>
</nav>
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<SocialMedia />
</footer>
</aside>
);
};
@@ -0,0 +1,27 @@
import React from "react";
import { NavLink } from "react-router-dom";
const SocialMedia = () => {
return (
<React.Fragment>
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">OpenSign&apos;s Github</span>
</NavLink>
<NavLink to="https://www.linkedin.com/company/opensign%E2%84%A2/" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">OpenSign&apos;s LinkedIn</span>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">OpenSign&apos;s Twitter</span>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">OpenSign&apos;s Discord</span>
</NavLink>
</React.Fragment>
)
}
export default SocialMedia;
+33 -11
View File
@@ -1,30 +1,52 @@
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Submenu = ({ icon, title, children }) => {
const [isOpen, setIsOpen] = useState(false);
const Submenu = ({ item, closeSidebar }) => {
const [submenuOpen, setSubmenuOpen] = useState(false);
const { title, icon, children } = item;
const toggleSubmenu = () => {
setIsOpen(!isOpen);
setSubmenuOpen(!submenuOpen);
};
return (
<div>
<li role="none">
<button
onClick={toggleSubmenu}
className="w-full mx-auto flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:outline-none"
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
aria-expanded={submenuOpen}
aria-haspopup="true"
aria-controls={`submenu-${title}`}
>
<i className={icon + " text-[18px]"}></i>
<i className={`${icon} text-[18px]`}></i>
<div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4">{title}</span>
<i
className={
isOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
}
className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
}`}
aria-hidden="true"
></i>
</div>
</button>
{isOpen && <ul>{children}</ul>}
</div>
{submenuOpen && (
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
{children.map((childItem) => (
<li key={childItem.title} role="none">
<NavLink
to={`/${childItem.pageType}/${childItem.objectId}`}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
onClick={closeSidebar}
role="menuitem"
tabIndex={submenuOpen ? 0 : -1}
>
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{childItem.title}</span>
</NavLink>
</li>
))}
</ul>
)}
</li>
);
};
@@ -1,9 +1,37 @@
import React from "react";
import React, { useState, useEffect, useMemo } from "react";
import pad from "../assets/images/pad.svg";
import { useNavigate } from "react-router-dom";
const ReportTable = ({ ReportName, List, actions }) => {
const ReportTable = ({
ReportName,
List,
actions,
setIsNextRecord,
isMoreDocs
}) => {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const docPerPage = 10;
// const pageNumbers = [];
// For loop is used to calculate page numbers visible below table
// Initialize pageNumbers using useMemo to avoid unnecessary re-creation
const pageNumbers = useMemo(() => {
const calculatedPageNumbers = [];
for (let i = 1; i <= Math.ceil(List.length / docPerPage); i++) {
calculatedPageNumbers.push(i);
}
return calculatedPageNumbers;
}, [List]);
// below useEffect reset currenpage to 1 if user change route
useEffect(() => {
return () => setCurrentPage(1);
}, []);
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
// if no data available it will show hyphen "-"
const formatRow = (row) => {
if (Array.isArray(row)) {
let updateArr = row.map((x) => x.Name);
@@ -14,24 +42,17 @@ const ReportTable = ({ ReportName, List, actions }) => {
return "-";
}
};
// below useEffect is used to render next record if IsMoreDoc is true
// second last value of pageNumber array is same as currentPage
useEffect(() => {
if (isMoreDocs && pageNumbers[pageNumbers.length - 1] === currentPage) {
setIsNextRecord(true);
}
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
// `handlemicroapp` is used to open microapp
const handlemicroapp = (item, url) => {
localStorage.removeItem("rowlevel");
// const params = new URLSearchParams(url);
// let arr = [];
// for (let [key, value] of params.entries()) {
// if (key === "remoteUrl") {
// arr.push(
// `${key}=${window.btoa(
// window.location.origin + "/mfbuild/remoteEntry.js"
// )}`
// );
// } else {
// arr.push(`${key}=${value}`);
// }
// }
// const remoteUrl = arr.join("&");
// console.log("arr", remoteUrl);
navigate("/rpmf/" + url);
localStorage.setItem("rowlevel", JSON.stringify(item));
// localStorage.setItem("rowlevelMicro");
@@ -39,6 +60,17 @@ const ReportTable = ({ ReportName, List, actions }) => {
const handlebtn = () => {
console.log("clicked");
};
// Get current list
const indexOfLastDoc = currentPage * docPerPage;
const indexOfFirstDoc = indexOfLastDoc - docPerPage;
// `currentLists` is total record render on current page
const currentLists = List.slice(indexOfFirstDoc, indexOfLastDoc);
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
return (
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
<h2 className="text-[23px] font-light my-2">{ReportName}</h2>
@@ -60,8 +92,8 @@ const ReportTable = ({ ReportName, List, actions }) => {
<tbody className="text-[12px]">
{List?.length > 0 ? (
<>
{List.map((item, index) => (
<tr className="border-t-[1px]" key={index}>
{currentLists.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
<td className="px-4 py-2">{index + 1}</td>
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Note || "-"}</td>
@@ -121,8 +153,46 @@ const ReportTable = ({ ReportName, List, actions }) => {
)}
</tbody>
</table>
<div className="flex flex-wrap items-center gap-2 p-2 ">
{List.length > 10 && (
<>
{currentPage > 1 && (
<button
onClick={() => paginateBack()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Prev
</button>
)}
</>
)}
{pageNumbers.map((x) => (
<button
key={x}
onClick={() => setCurrentPage(x)}
className=" bg-sky-400 text-white rounded px-3 py-2 focus:outline-none"
>
{x}
</button>
))}
{isMoreDocs && (
<button className="text-black rounded px-1 py-2">...</button>
)}
{List.length > 10 && (
<>
{pageNumbers.includes(currentPage + 1) && (
<button
onClick={() => paginateFront()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Next
</button>
)}
</>
)}
</div>
{List?.length <= 0 && (
<div className="flex flex-col items-center justify-center w-full bg-white roundedm py-4">
<div className="flex flex-col items-center justify-center w-full bg-white rounded py-4">
<div className="w-[60px] h-[60px] overflow-hidden">
<img className="w-full h-full object-contain" src={pad} alt="img" />
</div>
+84
View File
@@ -0,0 +1,84 @@
import { Suspense } from "react";
import { useParams } from "react-router-dom";
import useFederatedComponent from "mf-cra";
import React from "react";
import Title from "../components/Title";
function RemoteApp({ app }) {
// console.log("app ", app);
const { Component: RemoteComponent } = useFederatedComponent(app);
// console.log("RemoteComponent ", RemoteComponent);
return (
<>
<Title title={app.remoteName} />
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<div
style={{
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)",
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
borderRadius: 3,
minHeight: "70vh"
}}
>
{RemoteComponent && <RemoteComponent />}
</div>
</Suspense>
</>
);
}
function checkObjectProperties(obj) {
// Check if all three properties exist in the object
if (
obj &&
obj.remoteName !== undefined &&
obj.remoteUrl !== undefined &&
obj.moduleToLoad !== undefined
) {
return true; // All properties are present
} else {
return false; // At least one property is missing
}
}
export default function RemoteAppContainer() {
const { remoteApp } = useParams();
const obj = {
remoteUrl: window.location.origin + "/mfbuild/remoteEntry.js",
moduleToLoad: "./AppRoutes",
remoteName: remoteApp
};
// console.log("obj ", obj);
const isMf = checkObjectProperties(obj);
if (isMf) {
const appToLoad = obj;
return <RemoteApp app={appToLoad} />;
} else {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">
Page Not Found
</p>
</div>
</div>
);
}
}
+430 -301
View File
@@ -21,17 +21,18 @@ function Login(props) {
passwordVisible: false,
mobile: "",
phone: "",
OTP: "",
hideNav: "",
scanResult: "",
baseUrl: localStorage.getItem("baseUrl"),
parseAppId: localStorage.getItem("parseAppId"),
loading: false,
thirdpartyLoader: false,
orgModal: false,
ReqOtp: ""
thirdpartyLoader: false
});
const [userDetails, setUserDetails] = useState({
Company: "",
Destination: ""
});
const [isModal, setIsModal] = useState(false);
const image = props?.appInfo?.applogo || undefined;
useEffect(() => {
@@ -94,7 +95,6 @@ function Login(props) {
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
let userRoles = [];
@@ -114,44 +114,36 @@ function Login(props) {
};
await axios
.post(url, JSON.stringify(body), { headers: headers })
.then((roles) => {
.then((axiosRes) => {
const roles = axiosRes.data.result;
if (roles) {
userRoles = roles.data.result;
userRoles = roles;
let _currentRole = "";
const valuesToExclude = [
"contracts_Guest",
`${localStorage.getItem("_appName")}_appeditor`
];
if (userRoles.length > 1) {
if (
userRoles[0] ===
`${localStorage.getItem("_appName")}_appeditor`
) {
_currentRole = userRoles[1];
} else {
const rolesfiltered = userRoles.filter(
(x) => x !== "contracts_Guest"
);
if (rolesfiltered.length > 0) {
_currentRole = rolesfiltered[0];
} else {
setThirdpartyLoader(false);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: `Does not have permissions to access this application.`
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
}
// _currentRole = userRoles[0];
}
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = rolesfiltered[0];
}
} else {
_currentRole = userRoles[0];
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = userRoles[0];
} else {
_currentRole = "";
}
}
if (
_currentRole &&
_currentRole !==
`${localStorage.getItem("_appName")}_appeditor`
`${localStorage.getItem("_appName")}_appeditor`
) {
userSettings.forEach(async (element) => {
if (element.role === _currentRole) {
@@ -178,7 +170,6 @@ function Login(props) {
query.include("TenantId");
await query.find().then(
(results) => {
// console.log("results ", results);
let tenentInfo = [];
if (results) {
let extendedInfo_stringify =
@@ -339,25 +330,27 @@ function Login(props) {
}
},
(error) => {
/* alert(
"You dont have access to this application."
); */
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription:
"You dont have access to this application."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace(
"show",
""
);
}, 2000);
localStorage.setItem("accesstoken", null);
const payload = {
sessionToken: user.getSessionToken()
};
handleSubmitbtn(payload);
// setState({
// ...state,
// loading: false,
// toastColor: "#d9534f",
// toastDescription:
// "You dont have access to this application."
// });
// const x = document.getElementById("snackbar");
// x.className = "show";
// setTimeout(function () {
// x.className = x.className.replace(
// "show",
// ""
// );
// }, 2000);
// localStorage.setItem("accesstoken", null);
console.error(
"Error while fetching Follow",
error
@@ -367,45 +360,18 @@ function Login(props) {
}
});
} else {
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: "User Role Not Found."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
setIsModal(true);
}
} else {
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: "User Role Not Found."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
setIsModal(true);
}
})
.catch((err) => {
console.log("err", err);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: `Does not have permissions to access this application.`
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
setIsModal(true);
});
}
} catch (error) {
@@ -460,7 +426,6 @@ function Login(props) {
await Parse.User.become(sessionToken).then(() => {
window.localStorage.setItem("accesstoken", sessionToken);
});
// console.log("me res ", res);
if (res.data) {
let _user = res.data;
localStorage.setItem("UserInformation", JSON.stringify(_user));
@@ -492,25 +457,36 @@ function Login(props) {
};
await axios
.post(url, JSON.stringify(body), { headers: headers })
.then((roles) => {
.then((axiosRes) => {
const roles = axiosRes.data.result;
if (roles) {
userRoles = roles.data.result;
userRoles = roles;
let _currentRole = "";
const valuesToExclude = [
"contracts_Guest",
`${localStorage.getItem("_appName")}_appeditor`
];
if (userRoles.length > 1) {
if (
userRoles[0] ===
`${localStorage.getItem("_appName")}_appeditor`
) {
_currentRole = userRoles[1];
} else {
_currentRole = userRoles[0];
}
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = rolesfiltered[0];
}
} else {
_currentRole = userRoles[0];
const rolesfiltered = userRoles.filter(
(x) => !valuesToExclude.includes(x)
);
if (rolesfiltered.length > 0) {
_currentRole = userRoles[0];
} else {
_currentRole = "";
}
}
if (
_currentRole &&
_currentRole !==
`${localStorage.getItem("_appName")}_appeditor`
`${localStorage.getItem("_appName")}_appeditor`
) {
userSettings.forEach(async (element) => {
if (element.role === _currentRole) {
@@ -581,7 +557,7 @@ function Login(props) {
);
setThirdpartyLoader(false);
setState({ ...state, loading: false });
navigate(`/`);
navigate("/");
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
@@ -619,6 +595,7 @@ function Login(props) {
);
setThirdpartyLoader(false);
setState({ ...state, loading: false });
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
if (billingDate) {
if (billingDate > new Date()) {
localStorage.removeItem("userDetails");
@@ -632,6 +609,7 @@ function Login(props) {
navigate(`/subscription`);
}
}
}
} else {
localStorage.setItem("PageLanding", element.pageId);
localStorage.setItem(
@@ -641,6 +619,7 @@ function Login(props) {
localStorage.setItem("pageType", element.pageType);
setState({ ...state, loading: false });
setThirdpartyLoader(false);
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
if (billingDate) {
if (billingDate > new Date()) {
localStorage.removeItem("userDetails");
@@ -654,27 +633,16 @@ function Login(props) {
navigate(`/subscription`);
}
}
},
}
},
(error) => {
/* alert(
"You dont have access to this application."
); */
const payload = {
sessionToken: sessionToken
};
setThirdpartyLoader(false);
handleSubmitbtn(payload);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription:
"You dont have access to this application."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
localStorage.setItem("accesstoken", null);
// localStorage.setItem("accesstoken", null);
console.error("Error while fetching Follow", error);
}
);
@@ -682,47 +650,18 @@ function Login(props) {
});
} else {
setThirdpartyLoader(false);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: "User Role Not Found."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
}
} else {
setThirdpartyLoader(false);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: "User Role Not Found."
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
}
})
.catch(() => {
.catch((err) => {
console.log("err", err);
setThirdpartyLoader(false);
setState({
...state,
loading: false,
toastColor: "#d9534f",
toastDescription: `Does not have permissions to access this application.`
});
const x = document.getElementById("snackbar");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 2000);
setState({ ...state, loading: false });
});
}
} catch (error) {
@@ -744,7 +683,7 @@ function Login(props) {
};
const GetLoginData = async () => {
setState({...state, loading: true})
setState({ ...state, loading: true });
try {
Parse.serverURL = localStorage.getItem("baseUrl");
Parse.initialize(localStorage.getItem("parseAppId"));
@@ -920,11 +859,86 @@ function Login(props) {
setState({ ...state, passwordVisible: !state.passwordVisible });
};
const handleSubmitbtn = async (e) => {
e.preventDefault();
if (userDetails.Destination && userDetails.Company) {
setThirdpartyLoader(true);
// console.log("handelSubmit", userDetails);
// const payload = await Parse.User.logIn(state.email, state.password);
const payload = { sessionToken: localStorage.getItem("accesstoken") };
const userInformation = JSON.parse(
localStorage.getItem("UserInformation")
);
// console.log("payload ", payload);
if (payload && payload.sessionToken) {
const params = {
userDetails: {
name: userInformation.name,
email: userInformation.email,
phone: userInformation.phone,
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
};
const userSignUp = await Parse.Cloud.run("usersignup", params);
// console.log("userSignUp ", userSignUp);
if (userSignUp && userSignUp.sessionToken) {
const LocalUserDetails = {
name: userInformation.name,
email: userInformation.email,
phone: userInformation.phone,
company: userDetails.Company,
jobTitle: userDetails.JobTitle
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(userSignUp.sessionToken);
} else {
alert(userSignUp.message);
}
} else if (
payload &&
payload.message.replace(/ /g, "_") === "Internal_server_err"
) {
alert("Internal server error !");
}
} else {
alert("Please fill required details!");
}
};
const handleCloseModal = () => {
setIsModal(false);
Parse.User.logOut();
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("BaseUrl12");
let appid = localStorage.getItem("AppID12");
localStorage.clear();
localStorage.setItem("appLogo", applogo);
localStorage.setItem("appName", appName);
localStorage.setItem("_appName", _appName);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("BaseUrl12", baseUrl);
localStorage.setItem("AppID12", appid);
};
return (
<div className="bg-white">
<Title title={"Login Page"} />
{state.loading && (
<div
aria-live="assertive"
style={{
position: "fixed",
width: "100vw",
@@ -936,145 +950,161 @@ function Login(props) {
}}
>
<div
role="status"
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
left: "50%",
transform: "translate(-50%, -50%)"
}}
className="loader-37"
></div>
>
<span className="sr-only">Loading...</span>
</div>
</div>
)}
{props.isloginVisible && props.isloginVisible ? (
<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 className="">
<>
<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">
<img
src={image}
width="100%"
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}>
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
<span className="text-[12px] text-[#878787]">
Login to your account
</span>
<div className="shadow-md rounded my-4">
<div className="px-6 py-4">
<label className="block text-xs">Username</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
name="email"
value={state.email}
onChange={handleChange}
required
/>
<hr className="my-2 border-none" />
<label className="block text-xs">Password</label>
<div className="relative">
<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
type={state.passwordVisible ? "text" : "password"}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
name="password"
value={state.password}
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
/>
<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>
<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>
</div>
</div>
<div className="flex flex-row justify-between items-center text-xs px-4 py-2">
<div>
<label className="form-check-label">
<input
type="checkbox"
className="form-check-input"
value=""
/>
<span className="text-center pl-1">
Remember Password
</span>
</label>
</div>
<div>
<span className="text-[13px]">
<NavLink to={`/forgetpassword`}>
</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] underline focus:outline-none focus:ring-2 focus:ring-blue-600"
>
Forgot Password ?
</NavLink>
</span>
</div>
</div>
</div>
<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 uppercase"
disabled={state.loading}
<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="/signup"
style={state.hideNav ? { textAlign: "center" } : {}}
>
Create Account
</NavLink>
</div>
</form>
<br />
{(props.appInfo.fbAppId || props.appInfo.googleClietId) && (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
className="text-sm"
>
{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="/signup"
style={state.hideNav ? { textAlign: "center" } : {}}
>
Create Account
</NavLink>
</div>
</form>
<br />
{(props.appInfo.fbAppId || props.appInfo.googleClietId) && (
<hr
className={"border-[1px] border-gray-300 w-full"}
style={{ color: "grey" }}
/>
<span style={{ color: "grey" }} className="px-2 ">
OR
</span>
<hr
className={"border-[1px] border-gray-300 w-full"}
style={{ color: "grey" }}
/>
</div>
)}
<br />
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
className="text-sm"
>
<hr
className={"border-[1px] border-gray-300 w-full"}
style={{ color: "grey" }}
/>
<span style={{ color: "grey" }} className="px-2 ">
OR
</span>
<hr
className={"border-[1px] border-gray-300 w-full"}
style={{ color: "grey" }}
/>
</div>
)}
<br />
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{/* {props.appInfo.fbAppId && props.appInfo.fbAppId !== "" ? (
{/* {props.appInfo.fbAppId && props.appInfo.fbAppId !== "" ? (
<LoginFacebook
FBCred={props.appInfo.fbAppId}
thirdpartyLoginfn={thirdpartyLoginfn}
@@ -1082,42 +1112,142 @@ function Login(props) {
setThirdpartyLoader={setThirdpartyLoader}
/>
) : null} */}
</div>
<div style={{ margin: "10px 0" }}></div>
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{props.appInfo.googleClietId &&
props.appInfo.googleClietId !== "" ? (
<GoogleSignInBtn
GoogleCred={props.appInfo.googleClietId}
thirdpartyLoginfn={thirdpartyLoginfn}
thirdpartyLoader={state.thirdpartyLoader}
setThirdpartyLoader={setThirdpartyLoader}
/>
) : null}
</div>
</div>
<div style={{ margin: "10px 0" }}></div>
<div
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
{props.appInfo.googleClietId &&
props.appInfo.googleClietId !== "" ? (
<GoogleSignInBtn
GoogleCred={props.appInfo.googleClietId}
thirdpartyLoginfn={thirdpartyLoginfn}
thirdpartyLoader={state.thirdpartyLoader}
setThirdpartyLoader={setThirdpartyLoader}
</div>
{!state.hideNav && (
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
<img
src={login_img}
alt="The image illustrates a person from behind, seated at a desk with a four-monitor computer setup, in an environment with a light blue and white color scheme, featuring a potted plant to the right."
width="100%"
/>
) : null}
</div>
</div>
)}
</div>
</div>
<div
id="snackbar"
role="alert"
style={{ backgroundColor: state.toastColor }}
>
{state.toastDescription}
</div>
</div>
{isModal && (
<div
className="modal fade show"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{
display: "block",
zIndex: 1,
backgroundColor: "rgba(0,0,0,0.5)"
}}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Login form</h5>
<span>
<span></span>
</span>
</div>
<div className="modal-body">
<form className="text-sm">
<div className="form-group">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="Destination"
style={{ display: "flex" }}
className="col-form-label"
>
Destination{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="Destination"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div className="mt-4">
<button
type="button"
className="bg-[#6c757d] text-sm p-2 text-white rounded uppercase"
onClick={handleCloseModal}
style={{ marginRight: 10, width: 90 }}
>
Cancel
</button>
<button
type="button"
className="bg-[#17a2b8] text-sm p-2 text-white rounded uppercase"
onClick={(e) => handleSubmitbtn(e)}
>
Login
</button>
</div>
</form>
</div>
</div>
</div>
{!state.hideNav && (
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[500px]">
<img src={login_img} alt="bisec" width="100%" />
</div>
</div>
)}
</div>
</div>
<div id="snackbar" style={{ backgroundColor: state.toastColor }}>
{state.toastDescription}
</div>
</div>
)}
</>
) : (
<div
style={{
@@ -1132,7 +1262,6 @@ function Login(props) {
)}
</div>
);
// }
}
const mapStateToProps = (state) => {
@@ -64,7 +64,7 @@ const PlanSubscriptions = () => {
style={{
backgroundColor: "white",
overflowY: "auto",
maxHeight: "600px",
maxHeight: "100%",
"--theme-color": "#7952b3",
"--plan-width": 30
}}
+57 -8
View File
@@ -12,14 +12,37 @@ const Report = () => {
const [isLoader, setIsLoader] = useState(true);
const [reportName, setReportName] = useState("");
const [actions, setActions] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true);
const abortController = new AbortController();
// below useEffect is call when id param change
useEffect(() => {
setReportName("");
setList([]);
getReportData();
// Function returned from useEffect is called on unmount
return () => {
setIsLoader(true);
setList([]);
setIsNextRecord(false);
// Here it'll abort the fetch
abortController.abort();
};
// eslint-disable-next-line
}, [id]);
const getReportData = async () => {
setIsLoader(true);
// below useEffect call when isNextRecord state is true and fetch next record
useEffect(() => {
if (isNextRecord) {
getReportData(List.length, 200);
}
// eslint-disable-next-line
}, [isNextRecord]);
const getReportData = async (skipUserRecord = 0, limit = 200) => {
// setIsLoader(true);
const json = reportJson(id);
if (json) {
setActions(json.actions);
@@ -39,8 +62,11 @@ const Report = () => {
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
try {
const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`;
const res = await axios.get(url, { headers: headers });
const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skipUserRecord}&limit=${limit}&include=AuditTrail.UserPtr`;
const res = await axios.get(url, {
headers: headers,
signal: abortController.signal // is used to cancel fetch query
});
if (id === "4Hhwbp482K") {
const listData = res.data?.results.filter(
(x) => x.Signers.length > 0
@@ -66,14 +92,35 @@ const Report = () => {
}
}
}
setList(arr);
if (arr.length === 10) {
setIsMoreDocs(true);
} else {
setIsMoreDocs(false);
}
setList((prevRecord) =>
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
);
} else {
setList(res.data?.results);
if (res.data.results.length === 10) {
setIsMoreDocs(true);
console.log("here");
} else {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.results]
: res.data.results
);
}
setIsLoader(false);
} catch (err) {
console.log("err ", err);
setIsLoader(false);
const isCancel = axios.isCancel(err);
if (!isCancel) {
console.log("err ", err);
setIsLoader(false);
}
}
} else {
setIsLoader(false);
@@ -106,6 +153,8 @@ const Report = () => {
ReportName={reportName}
List={List}
actions={actions}
setIsNextRecord={setIsNextRecord}
isMoreDocs={isMoreDocs}
/>
) : (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
+36 -6
View File
@@ -99,9 +99,38 @@ const Signup = (props) => {
}
}
})
.catch((err) => {
alert(err.message);
setState({ loading: false });
.catch(async (err) => {
if (err.code === 202) {
const userQuery = new Parse.Query("contracts_Users");
userQuery.equalTo("Email", email);
const res = await userQuery.first();
// console.log("Res ", res);
if (res) {
alert("User already exists with this username!");
setState({ loading: false });
} else {
let baseUrl = localStorage.getItem("BaseUrl12");
let parseAppId = localStorage.getItem("AppID12");
// console.log("state.email ", email);
try {
Parse.serverURL = baseUrl;
Parse.initialize(parseAppId);
await Parse.User.requestPasswordReset(email).then(
async function (res1) {
if (res1.data === undefined) {
alert("Email has been sent to your mail!");
}
}
);
} catch (err) {
console.log(err);
}
setState({ loading: false });
}
} else {
alert(err.message);
setState({ loading: false });
}
});
} catch (error) {
console.log("err ", error);
@@ -251,7 +280,7 @@ const Signup = (props) => {
element.pageType
);
setState({ loading: false });
navigate("/login");
navigate("/");
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
@@ -453,7 +482,7 @@ const Signup = (props) => {
<div className="">
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Create Account !</h2>
<div className="shadow-md rounded my-4">
<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{" "}
@@ -472,6 +501,7 @@ const Signup = (props) => {
<span style={{ color: "red", fontSize: 13 }}>*</span>
</label>
<input
id="email"
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={email}
@@ -563,7 +593,7 @@ const Signup = (props) => {
</div>
{!state.hideNav && (
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[500px]">
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
<img src={login_img} alt="bisec" width="100%" />
</div>
</div>
@@ -4,6 +4,12 @@ import multerS3 from 'multer-s3';
import aws from 'aws-sdk';
import dotenv from 'dotenv';
dotenv.config();
function sanitizeFileName(fileName) {
// Remove spaces and invalid characters
return fileName.replace(/[^a-zA-Z0-9._-]/g, '');
}
async function uploadFile(req, res) {
try {
//--size extended to 100 mb
@@ -50,16 +56,28 @@ async function uploadFile(req, res) {
region: process.env.DO_REGION,
});
// const s3 = new aws.S3();
const upload = multer({
fileFilter: function (req, file, cb) {
if (accepted_extensions.some(ext => file.originalname.toLowerCase().endsWith('.' + ext))) {
return cb(null, true);
const parseBaseUrl = process.env.SERVER_URL;
const parseAppId = process.env.APP_ID;
if (process.env.USE_LOCAL == "TRUE") {
var fileStorage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, "files/files");
},
metadata: function (req, file, cb) {
cb(null, { fieldName: 'OPENSIGN_METADATA' });
},
filename: function(req, file, cb) {
let filename = file.originalname;
let newFileName = filename.split('.')[0];
let extension = filename.split('.')[1];
newFileName = sanitizeFileName(newFileName + '_' + new Date().toISOString() + '.' + extension)
console.log(newFileName);
cb(null, newFileName);
}
// otherwise, return error
return cb('Only ' + accepted_extensions.join(', ') + ' files are allowed!');
},
storage: multerS3({
});
} else {
var fileStorage = multerS3({
acl: 'public-read',
s3,
bucket: DO_SPACE,
@@ -69,14 +87,25 @@ async function uploadFile(req, res) {
key: function (req, file, cb) {
//console.log(file);
let filename = file.originalname;
let filenam = filename.split('.')[0];
let newFileName = filename.split('.')[0];
let extension = filename.split('.')[1];
filenam = filenam + '_' + new Date().toISOString() + '.' + extension;
console.log(filenam);
cb(null, filenam);
},
}),
newFileName = sanitizeFileName(newFileName + '_' + new Date().toISOString() + '.' + extension)
console.log(newFileName);
cb(null, newFileName);
}
});
}
// const s3 = new aws.S3();
const upload = multer({
fileFilter: function (req, file, cb) {
if (accepted_extensions.some(ext => file.originalname.toLowerCase().endsWith('.' + ext))) {
return cb(null, true);
}
// otherwise, return error
return cb('Only ' + accepted_extensions.join(', ') + ' files are allowed!');
},
storage: fileStorage,
limits: { fileSize: size },
}).single('file');
@@ -93,7 +122,14 @@ async function uploadFile(req, res) {
const status = 'Success';
//res.header("Access-Control-Allow-Headers", "Content-Type");
//res.setHeader("Access-Control-Allow-Origin", "*");
return res.json({ status, imageUrl: req.file.location });
if (process.env.USE_LOCAL == "TRUE") {
console.log(req.file);
var fileUrl = `${parseBaseUrl}/files/${parseAppId}/${req.file.filename}`;
} else {
var fileUrl = req.file.location;
}
return res.json({ status, imageUrl: fileUrl });
});
} catch (err) {
console.log('Exeption in query ' + err.stack);
@@ -1 +1 @@
const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate,{passphrase:"emudhra"})}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF;
const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate, process.env.PASS_PHRASE ? {passphrase : process.env.PASS_PHRASE}: null)}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF;
@@ -123,6 +123,9 @@ export default async function (request) {
if (userDetails && userDetails.company) {
newObj.set("Company", userDetails.company);
}
if (userDetails && userDetails.jobTitle) {
newObj.set("JobTitle", userDetails.jobTitle);
}
if (planDetails && planDetails.customer_id) {
newObj.set("Next_billing_date", new Date(planDetails.nextBillingDate));
newObj.set("Plan", planDetails.plan);
+55
View File
@@ -0,0 +1,55 @@
MIIKLwIBAzCCCeUGCSqGSIb3DQEHAaCCCdYEggnSMIIJzjCCBEIGCSqGSIb3DQEH
BqCCBDMwggQvAgEAMIIEKAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG
SIb3DQEFDDAcBAh6cQqVpHfTzAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME
ASoEEFKEIGJTTRo3voO/FvsXs7yAggPAI7rMCboTFFBfdMuuV3Y9/xfphAgdGD7P
0xAN10CcmXlMtedc8cRxOaD9n3Q7zJx5iIbqEGDTc0dgp5EtdWsGl7MP76iHkSfB
ZUPc13nbnRU4Nq4hNFcSj1m6NVa0lT9UobbTsG+wUkJn8aLfijzZ1OY82KLlKUoJ
lqLsQ1yZkeLiLKI0aEew/2vzfiLUX9A99qnCRoryo8ZbLiWl0Vu/FEKM5mqIRFEp
BKeSUsRxBfJCMZ2Ks5t4/MTCV+yrcsuhLPF/AJpzKfIinEFDhm7MxP4RXtk2LWNe
+EO2GuFRjZCfsMO0oNWybFmDeNDMCHZj5TeoqL1JwAV5e1lEeqR38v4Wcr4OVefY
DfdrJ2xdvm7XbBZ8Lgx8L4fwugw7oDyTpR2jJqHMlpOsQrxipN6UKkN6EVYeCC5K
4dF5LHdCcIa7L/OI/feD5g/zz8SGCTc91husVN7zi+CVO0lQLRkAj02+VbiXPPDD
eQm/QBieG2yVe5IRfcnIs2aszulbQbgHdguSYSQa/7DEvN/9/uxUIRFbItwrreRI
VAUUNS02L680Zok9xzDed1I/WJoPr2+5n2eV1jc5zoH7mAni0+Z1d5qLoYV6pNaN
P9okvBf75LLNoJKsb/eWlkk73rmlH6/Qz+hudgnlkxUJuU/b79AMQpd0EWEWhcim
ZeB4d9lPLLwyvY/FN4aCGWzsoAW69RK9E1DyGZAZRYRwCKHSWXmCaenR1ilxcPFx
TDavpybObIISPtylv5Z/IGBjE19Lw0UHc6Waln/jx0YXXGGDWs1F/MrdJ/8mkVXf
tHB/5ds4u5md+CmXpaCITO3Cg9GltGSWyDoEJsz1u/Wzq4X4FYmolXiqQvMPQrxu
iThTnPDGbz9VqFSyNKPKswIQ8CUbvZ209A5vjERPzE0Lp1pO39K51fnHD9oD968t
tGbGmNiQ0++Orze79cH0UVwDa6NLGCmnE0oBgke1JDPlaU3kkx7+R9SAS0Zytsmj
aLxW5Z1cOFaqqBLjwgHUxOqRO+cl6P9ORox34wJjiwZKy1L+0ppw22Na/unwi2Rk
a8jvK7+ycOLaiSmI77T0l7VCWgKsRsWLvBA3cjX5XAGtatFXrY/7Imoxl0nWGLjG
n5Sv5XuDek4e4QAPeJmhpURMnzLDsSXAYOSYZvS/kgENYhYSnQLQp50TxrzquA2c
lAeFIEqqAJy9tAMfp5UAqMeOC60Ttn6ySsdOle003F2NrRilhPynNDZ3h1ZQknur
Y9zWUm9qBmXCjhPzdmzHz3Ozj2Bi6kd4MIIFhAYJKoZIhvcNAQcBoIIFdQSCBXEw
ggVtMIIFaQYLKoZIhvcNAQwKAQKgggUxMIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkq
hkiG9w0BBQwwHAQIZenzippqfSQCAggAMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUD
BAEqBBCddSIj/wfxowZAO1j3W9FwBIIE0C/guzeZtV/vPsaIDAtWEkmepCvhm9Rg
aUgOoR66wjyYx7upeuZJ+pYJdsEqUmTgGYN+ClBpsU0bqCM7PI0mepvIT9kLD6ZL
UkprtNAYP87NBA/e5dpXlGAf9ibDRT0F2Y+lCSF83rCIYKG8n7h5834F6Saf+dU7
6y5bgorbf2Fnxy123XfUSg5qdCraZZM+DwE1gpQdBGrFyq8cwlQ8hJpKHDWup7WA
f2HcT/X0Wjb24L6JWjC1FvTbTNdS/lzTOcqLYUc31iuU9rF6O4qtX9PK/yGWc+YE
qakuV+HrsF4je/YdVZRYHtkbbcYXW9PjtMv+4D1ut5X2SPeHwpj3WmGAb7Zno6SD
SfohMihXvDbIjVSMDBpW97bhgk0h7iS4ISIYoQJUGJv3QQ3XJq91LoIP3zpiO/Cq
ZuuHuGvZmO6lb0PGShRT7mFg3k3rswTfFyKdtY76zAVJIR2bz/6bRf4uQI48SOLR
NX1Z6lavUNiE4nPNlnfBp480C+YANKs/bdev0wkRVmFxPMyb5FFLXwLSqcfdSQag
RayrKd8xPOm6lor3cTvrkO3XH2Hf9TgFHjeIAQ3VZ1Cncb9jfra3V4zvIocek4dC
GREirPbEyL/js5O0aAa0mg9EontMWKRqncomKhUOckheP2dYiQOazDFDFP9OQCYV
MBb0RDwm3+hiMU0lPeO+NAjAv2SfFK7G4maMb2SL7inT7An7ZPPpnDNXLDt+nOgo
IKuaJpy8JX/OKB4ntYsdnQG4E0J3vpHWjYPKif7j1njIYFd76bIgNVlqWuQFArBy
U9BuPyF+eYouJc+TpX9R5ybPvabRR4NUjnrttr2E5AzQfIgNGrSFiewjvYZHQOKd
WyVR9YV0cUv591xyUDdXMoHh+J0v0xK1WCHe/9enY5y/C8YohL8h5tEvInAQconj
lJ2r9qX8iBmIupJij5M5Ebza8HQ4aVMdHu/s05Vj9BC6K0ok3h+DFTj2gUBD24sp
APHic0DmNnciUQuHuuoW/1SVb6I1HJswsgcC8CekvoXF26ygwWhJPyqw+5djip2w
I/l3uOnPPUWZQHKhnBH4noZydrzBBJN1po9dkxb8LKuKZDEcnKo+tQrTnZJaFRXK
pQDsAjQ0HcYi8ZqN/GPezXOqzRgvObevXeDYTJrNejr46B1HqjPw8doai+AEf//1
iiM9EK4y3bGbX837oebTByRi112AOctISHTWbF0zdFMiBO9X9PML4n3i14ToqLH1
EUp0WtCdKLJmrRnO9PKJUMpce3yNu6oRSBzB4EkgUMOTVx9tM66h/gcR3wr+qKJP
1EABMbbCsJeYYLujhQHVh0JluqLBXYuCmr5n6o8qmonj5tEfTn9zs2gi0OvKTliH
wRXWsar004alt8D8o4M/Oqu3qyVV1O8BlVEofUC1CfAMIh/T1NJBeKG/FPfAFoEz
9liLLkoDvGvFVJ4Ezy4BaPtg+G3yU/SeiruwsCB3unHRD7G6VvS4jY2B8vEYl0yM
Z1mLFbKK8CIEvg8+v/vbc0noYUOzd6bkwh6Y6cziR/u78y2nALYZpQK+3XiVVH/v
SaVoDckZgUwAX+dMd1P4U3a487MPXBZZmLVFMHTuv3NvW6NIQCMd4l35ViRd7YcS
V2QDKTnUtPN2MSUwIwYJKoZIhvcNAQkVMRYEFMSz7PlBO5VzTDqC/foidPWrMjkw
MEEwMTANBglghkgBZQMEAgEFAAQgPlLuG+67SxPOEPbgFuEnVgewvY6LUzFmrprx
c6yryFgECIld0S5kDk7JAgIIAA==
+21
View File
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIUdpdDPiNqilWn26oMvcpT9qmMtoswDQYJKoZIhvcNAQEL
BQAwPjELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk5JMQowCAYDVQQHDAFOMQowCAYD
VQQKDAFTMQowCAYDVQQLDAFTMB4XDTIzMTEwNTEzMDg1OFoXDTI0MTEwNDEzMDg1
OFowPjELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk5JMQowCAYDVQQHDAFOMQowCAYD
VQQKDAFTMQowCAYDVQQLDAFTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAzT30t7Adb6XdXWywzC4RF03r3piTt5MF4MZClvorsl8bySNKrYkl7i5+igpz
36SfLp090Dal0cUZ7it9QCxaxrWv3m8OFAwfwK5ePuzUryF6/QJcoF560Sq4k3Pb
640PJ5UlFwvUqsXQN8A/pAA6S96MqpwCrPQE0u5OcRQuVIsZxBL2s17lv5eROjX+
Ufx/6qtmuZscn9xKDP6GNO8aa6X0YZMoVXlll14iEn4bg0yagiY7QhvVCzK8q7nB
9yKCLvw/28eSoD08yJnHnUZnIOoNrn+cZKTkl8VcyKW55IeOiUilb5h3a07JD6bj
Q/VroPQE5UtveajB9CBa3eUCiwIDAQABo1MwUTAdBgNVHQ4EFgQUIQGrIwhvSNkG
L3ATZra96d4cDE4wHwYDVR0jBBgwFoAUIQGrIwhvSNkGL3ATZra96d4cDE4wDwYD
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAp2mCfhRf6Jft6lSgL4hi
I8xHfqcUjG+XnDM1Pi7gKVxxclwVQ67vVtfO0rU1SBPWUO3nIaaLN9Wyzcx+LXPV
4e3pyHlq2T6Z6ENuozOsMhHeY9+ows6wPBcxq57KLLHnBpx3lJiKHKOp0ueiAbkz
LaI+Mm+VQo0MbzhCw7KXs8VPFt8YO+S3XircVMWaa007VgGY/NjYF62hlG35fD/v
CTHccRj5Y3coQ2MjosTUQTd4u3N5VhgxdRbyMdpjpwDxu0u9BE7ISXKMBhnBLV/w
+/mxoF2bqExBuPiZS8vT6yW9rehdi/KR9c26GUreTsKLtjXyveeP6VLoTJ60T3D9
Jg==
-----END CERTIFICATE-----
+30
View File
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIJvntFBuTIWACAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEE+1CDDu5XvBIIEyEwR/61HhjJ0
P6oGr1E+uDTKVcUqK++fbsrhqxYq+oEoj+w5C1KOgUxQYQTm2he6PhfGkPh56V9w
OaArJ+QrGrSFRUdTtLZG+qBbfyWKQyah9VxwYhmG9Ptf6RyQXDJnup4WtgJWI2sw
fAcJ6QqPQkSFUq/0cn0s/RLNCy6P8qKfUjdVugEv0K9dsc7dFNWZQEQlpyKeZdbY
rJfKjf85qSmDpFfRnP1B0n//vzlPWK03fNH2be4EnBTFW7l1sDnju6iwasLSP7P+
EuGq8Bw9YGlf3I/STWGgfxWWLZACCjgVDiRjLakstkodLqC1tZAWawOKcDQGPyKG
nRrF/yF4H0ChmcMBA9Ur4zxBd4uayWujwqIPehfWU+ODYYaVDluSVrxiTaSHGAP2
XnT6vrOnR5sg/uzgsbuApHTbHCW+lplof/R0swvwyt15fKaD8HsngNMN6R75e0ar
6+ltI/BLgcSbu0f7Fe8qMumBNrJiU5j09EdvDKaIEe3+Ko2HWCV/zLgxvL8BwGyB
K9RSMw1YzivO/CahWE3cQcS4GQSX84+y8VXF43CxpmTRf3Hmdpj4JBGB0fOvGHzA
hv+st5E+hhSOOv2ZZ00XtEaUi3d2uQFYApxEoXubz2QG481M7ulNXaVd74DZf/Fr
J+R7BmCa633rEw34sXFKUcfdfVOzUfMnMGvO5danSrov6k/cFfLPH56ORb9Ep30a
U1fXHP+pcFQohR/kjAk5gjDUr8pjkOJ0/JHv106rptT4SNCznvJa8BvWtP9wDXDv
UZDA/UCLlnl20mJUjLtb0iM5vtqhi7XOxDoS8AauOimSEh+M0uLEZ7HOQvtGchLl
tDLd/DVew/WzPBhL4eNTmhwwno9JL21nPBEaFAa58djoiZXgzUdLQbBDbo/7nsRA
UJ9imt0HmhDU+xrimnMZJD051KcbohHjPvTnTb4qvd0Rt7u/dpL9NT07rQlVIe3u
qGl4sEe+1vEueRdHt5QCSGbxnwPPHdE9XMsK8et3M/DOYN+ToB9QmDm72lLsJNkm
0nxdYVpfXHUiPmMorkJgb9YXfY+j0UEWuT0l4bl7a5+mzGm/Zy9FCoLaW7MTWNJd
fyDSYsVotOhRdCBi0Osho21GldKmfX6XoqHYENVqsh7x9bgvjNwvLNgpCWXYh7/Q
K6N3iDQ3G3A6x9nOgeKpXYREd8fx8byM6u7FAttVTtcV71QaWU3TSn7Oice1Bklg
osIhUJydHny856D/WCD6EmnPBuukx9Tht8NKtJVS0XoXqbz69mx4xfq987HGkJJa
nvCgkZOr/LhcVylmRjg9ZVfiUrwim4Gp42gV3JtsZpOGE1m+1twSXShtr/PQ2FdF
jD41LOEG1OGdZ5XIDLxAI4085jwVD/WIRo/AZELENog1XKtjeXSqJ/H7njauB30w
MeNv0VRTIItVA0eXRDwoqjpqyGSdn9YF2SQ/qDR6WU+Y2bVD38m6ZJlsy/zHx9uc
sH1DK7hbbRRSuZ9Sx/TjD19DWJ4qNPTpUZTd0+dxsG/HGQKpkbpXI5f3aQ6nTWWM
MjlWF6mUI06iLIRhLyNwBrtEjJHL1k8fIOvO/MUKtj72VBGt/fnoutS0WifZLvfn
mFXB4iaMX0hGcgzpK4iNcg==
-----END ENCRYPTED PRIVATE KEY-----
Binary file not shown.
@@ -0,0 +1,16 @@
import React from "react";
const PageNotFound = () => {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">Page Not Found</p>
</div>
</div>
);
};
export default PageNotFound;
@@ -11,15 +11,13 @@ import { HTML5Backend } from "react-dnd-html5-backend";
import { useParams } from "react-router-dom";
import SignPad from "./component/signPad";
import RenderAllPdfPage from "./component/renderAllPdfPage";
import {
getBase64FromIMG,
getBase64FromUrl
} from "../utils/Utils";
import { getBase64FromIMG, getBase64FromUrl } from "../utils/Utils";
import Loader from "./component/loader";
import HandleError from "./component/HandleError";
import Nodata from "./component/Nodata";
import Header from "./component/header";
import RenderPdf from "./component/renderPdf";
import CustomModal from "./component/CustomModal";
function PdfRequestFiles() {
const { docId } = useParams();
@@ -64,7 +62,8 @@ function PdfRequestFiles() {
const [isSigned, setIsSigned] = useState(false);
const [isExpired, setIsExpired] = useState(false);
const [alreadySign, setAlreadySign] = useState(false);
const [containerWH, setContainerWH] = useState({});
const divRef = useRef(null);
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
@@ -96,6 +95,14 @@ function PdfRequestFiles() {
getDocumentDetails();
}
}, []);
useEffect(() => {
if (divRef.current) {
setContainerWH({
width: divRef.current.offsetWidth,
height: divRef.current.offsetHeight
});
}
}, [divRef.current]);
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async () => {
@@ -191,7 +198,7 @@ function PdfRequestFiles() {
//comparison auditTrail user details with signers user details
for (let i = 0; i < signerRes.length; i++) {
const signerId = signerRes[i].objectId;
let isSigned = false;
for (let j = 0; j < checkDocIdExist.length; j++) {
const signedExist =
@@ -912,6 +919,7 @@ function PdfRequestFiles() {
//function for set decline true on press decline button
const declineDoc = async () => {
setIsDecline({ isDeclined: false });
const data = {
IsDeclined: true
};
@@ -983,7 +991,39 @@ function PdfRequestFiles() {
</div>
)}
<div className="signatureContainer">
<div className="signatureContainer" ref={divRef}>
{/* this modal is used to show decline alert */}
<CustomModal
containerWH={containerWH}
show={isDecline.isDeclined}
headMsg="Document Declined Alert!"
bodyMssg={
isDecline.currnt === "Sure" ? (
<p className="pTagBody">
Are you sure want to decline this document ?
</p>
) : isDecline.currnt === "YouDeclined" ? (
<p className="pTagBody">You have declined this document!</p>
) : (
isDecline.currnt === "another" && (
<p className="pTagBody">
You cannot sign this document as it has been declined by
one or more person(s).
</p>
)
)
}
footerMessage={isDecline.currnt === "Sure"}
declineDoc={declineDoc}
setIsDecline={setIsDecline}
/>
{/* this modal is used for show expired alert */}
<CustomModal
containerWH={containerWH}
show={isExpired}
headMsg="Document Expired!"
bodyMssg="This Document is no longer available."
/>
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdfDetails[0].URL}
@@ -999,66 +1039,6 @@ function PdfRequestFiles() {
marginRight: pdfOriginalWidth > 500 && "20px"
}}
>
<Modal show={isExpired}>
<ModalHeader className="bg-danger">
<span className="spanTagHead">Document Expired!</span>
</ModalHeader>
<Modal.Body>
<p className="pTagBody">
This Document is no longer available.
</p>
</Modal.Body>
</Modal>
{/* this modal is used for show decline alert */}
<Modal show={isDecline.isDeclined}>
<ModalHeader className="bg-danger">
<span className="spanTagHead">Document Declined Alert!</span>
</ModalHeader>
<Modal.Body>
{isDecline.currnt === "Sure" ? (
<p className="pTagBody">
Are you sure want to decline this document ?
</p>
) : isDecline.currnt === "YouDeclined" ? (
<p className="pTagBody">You have declined this document!</p>
) : (
isDecline.currnt === "another" && (
<p className="pTagBody">
You cannot sign this document as it has been declined by
one or more person(s).
</p>
)
)}
</Modal.Body>
<Modal.Footer>
{isDecline.currnt === "Sure" && (
<>
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => setIsDecline({ isDeclined: false })}
>
Close
</button>
<button
style={{
background: "#de4337"
}}
type="button"
className="finishBtn"
onClick={() => declineDoc()}
>
Yes
</button>
</>
)}
</Modal.Footer>
</Modal>
{/* this modal is used show this document is already sign */}
<Modal show={isCompleted.isModal}>
@@ -0,0 +1,59 @@
import React from "react";
import "../../css/customModal.css";
import "../../css/signature.css";
function CustomModal({
show,
headMsg,
bodyMssg,
footerMessage,
declineDoc,
setIsDecline,
containerWH
}) {
const isMobile = window.innerWidth < 767;
return (
show && (
<div
className="parentDiv"
style={{
width: containerWH && containerWH.width,
height: isMobile ? "100%" : containerWH && containerWH.height
}}
>
<div className="childDiv">
<div className="modalHeadDiv bg-danger">{headMsg && headMsg}</div>
<div className="modalBodyDIv">
<p className="pTagBody">{bodyMssg && bodyMssg}</p>
</div>
{footerMessage && (
<div className="modalFooterDiv">
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => setIsDecline({ isDeclined: false })}
>
Close
</button>
<button
style={{
background: "#de4337"
}}
type="button"
className="finishBtn"
onClick={() => declineDoc()}
>
Yes
</button>
</div>
)}
</div>
</div>
)
);
}
export default CustomModal;
@@ -1,5 +1,5 @@
import React, { useEffect } from "react";
import axios from "axios";
import React from "react";
function DefaultSignature({
themeColor,
@@ -26,7 +26,7 @@ function EmailComponent({
//function for send email
const sendEmail = async () => {
setIsLoading(true);
let sendMail, recipent;
let sendMail;
for (let i = 0; i < emailCount.length; i++) {
try {
@@ -109,8 +109,17 @@ function EmailComponent({
const handleToPrint = async (event) => {
event.preventDefault();
const base64 = await getBase64FromUrl(pdfUrl);
printModule({ printable: base64, type: "pdf", base64: true });
const pdf = await getBase64FromUrl(pdfUrl);
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 });
}
};
@@ -37,7 +37,7 @@ function Header({
}) {
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const isGuestSigner = localStorage.getItem("isGuestSigner");
//for go to previous page
function previousPage() {
changePage(-1);
@@ -60,8 +60,16 @@ function Header({
event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl);
printModule({ printable: pdf, type: "pdf", base64: true });
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 });
}
};
//handle download signed pdf
@@ -73,14 +81,16 @@ function Header({
return (
<div
style={{ paddingBottom: "5px", paddingTop: "5px" }}
style={{ padding: !isGuestSigner && "5px 0px 5px 0px" }}
className="mobileHead"
>
{isMobile && isShowHeader ? (
<div
id="navbar"
className="stickyHead"
style={{ width: window.innerWidth - 30 + "px" }}
className={isGuestSigner ? "stickySignerHead" : "stickyHead"}
style={{
width: isGuestSigner ? window.innerWidth : window.innerWidth - 30 + "px"
}}
>
<div className="preBtn2">
<div
@@ -42,8 +42,42 @@ function RenderPdf({
const isMobile = window.innerWidth < 767;
const newWidth = window.innerWidth;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
//function for render placeholder block over pdf document
// handle signature block width and height according to screen
const posWidth = (pos) => {
let width;
if (isMobile) {
if (!pos.isMobile) {
width = pos.Width / scale ? pos.Width / scale : 150 / scale;
return width;
} else {
width = pos.Width ? pos.Width : 150;
return width;
}
} else {
width = pos.Width ? pos.Width : 150;
return width;
}
};
const posHeight = (pos) => {
let width;
if (isMobile) {
if (!pos.isMobile) {
width = pos.Height / scale ? pos.Height / scale : 60 / scale;
return width;
} else {
width = pos.Height ? pos.Height : 60;
return width;
}
} else {
width = pos.Height ? pos.Height : 60;
return width;
}
};
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
//function for render placeholder block over pdf document
const checkSignedSignes = (data) => {
const checkSign = signedSigners.filter(
(sign) => sign.objectId === data.signerObjId
@@ -56,7 +90,7 @@ function RenderPdf({
if (isMobile) {
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
if (!pos.isMobile) {
return pos.xPosition / scale;
return pos.xPosition / scale - 20;
}
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
else {
@@ -134,8 +168,8 @@ function RenderPdf({
borderWidth: "0.2px"
}}
size={{
width: pos.Width ? pos.Width : 150,
height: pos.Height ? pos.Height : 60
width: posWidth(pos),
height: posHeight(pos)
}}
lockAspectRatio={pos.Width && 2.5}
default={{
@@ -205,8 +239,8 @@ function RenderPdf({
y: yPos(pos)
}}
size={{
width: pos.Width ? pos.Width : 150,
height: pos.Height ? pos.Height : 60
width: posWidth(pos),
height: posHeight(pos)
}}
lockAspectRatio={pos.Width ? pos.Width / pos.Height : 2.5}
>
@@ -235,7 +269,8 @@ function RenderPdf({
{isMobile && scale ? (
<div
style={{
border: "0.1px solid #ebe8e8"
border: "0.1px solid #ebe8e8",
marginTop: isGuestSigner && "30px"
}}
ref={drop}
id="container"
@@ -294,12 +329,11 @@ function RenderPdf({
borderWidth: "0.2px"
}}
size={{
width: pos.Width ? pos.Width : 150,
height: pos.Height ? pos.Height : 60
width: posWidth(pos),
height: posHeight(pos)
}}
lockAspectRatio={pos.Width && 2.5}
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divide by scale
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
default={{
x: !pos.isMobile
@@ -361,14 +395,13 @@ function RenderPdf({
borderWidth: "0.2px"
}}
size={{
width: pos.Width ? pos.Width : 150,
height: pos.Height ? pos.Height : 60
width: posWidth(pos),
height: posHeight(pos)
}}
disableDragging={true}
default={{
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divide by scale
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
x: !pos.isMobile
? pos.xPosition / scale
: pos.xPosition * (pos.scale / scale) - 50,
@@ -1281,4 +1314,4 @@ function RenderPdf({
);
}
export default RenderPdf;
export default RenderPdf;
@@ -38,19 +38,15 @@ function Login() {
const handleChange = (event) => {
const { value } = event.target;
setOTP(value);
};
//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();
@@ -84,13 +80,10 @@ function Login() {
e.preventDefault();
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (OTP) {
setLoading(true);
try {
let url = `${serverUrl}functions/AuthLoginAsMail/`;
const headers = {
@@ -102,7 +95,6 @@ function Login() {
otp: OTP
};
let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") {
alert("Invalid Otp");
setLoading(false);
@@ -114,9 +106,12 @@ function Login() {
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken);
//save isGuestSigner true in local to handle login flow header in mobile view
localStorage.setItem("isGuestSigner", true);
setLoading(false);
//navigate user to on signature page
navigate(`/recipientSignPdf/${id}/${contactBookId}`);
navigate(
`/loadmf/signmicroapp/recipientSignPdf/${id}/${contactBookId}`
);
}
} catch (error) {}
} else {
@@ -172,9 +167,7 @@ function Login() {
<span className="KNLO">
Verification code is sent to your email
</span>
<div className="card card-box"
style={{borderRadius:"0px"}}
>
<div className="card card-box" style={{ borderRadius: "0px" }}>
<div className="card-body">
<input
type="email"
@@ -189,7 +182,6 @@ function Login() {
<div className="btnContainer">
{loading ? (
<button
// className="btn btn-info loadinBtn "
type="button"
style={{
background: themeColor(),
@@ -213,15 +205,7 @@ function Login() {
color: "white",
marginLeft: "0px !important"
}}
// className="btn btn-sm otpButton"
onClick={(e) => SendOtp(e)}
// style={{
// marginBottom: "4px",
// width: "210px",
// background: themeColor(),
// color: "white",
// fontWeight: "600"
// }}
>
Send OTP
</button>
@@ -166,7 +166,7 @@ function PlaceHolderSign() {
getDocumentDetails();
}
}, []);
//function for get document details
const getDocumentDetails = async () => {
await axios
@@ -275,7 +275,7 @@ function PlaceHolderSign() {
getSignerPos(item, monitor);
}
};
const getSignerPos = (item, monitor) => {
const key = Math.floor(1000 + Math.random() * 9000);
let filterSignerPos = signerPos.filter(
@@ -396,7 +396,7 @@ function PlaceHolderSign() {
blockColor: color[isSelectListId],
placeHolder: xyPosArr
};
setSignerPos((prev) => [...prev, placeHolderPos]);
}
};
@@ -626,7 +626,7 @@ function PlaceHolderSign() {
setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset);
}
//function for capture position on hover signature button
const handleDivClick = (e) => {
const divRect = e.currentTarget.getBoundingClientRect();
@@ -679,7 +679,7 @@ function PlaceHolderSign() {
year: "numeric"
});
let sender = signersdata.ExtUserPtr.Email;
const signerMail = signersdata.Signers;
const signerMail = signersdata.Signers;
for (let i = 0; i < signerMail.length; i++) {
try {
@@ -698,7 +698,8 @@ function PlaceHolderSign() {
"parseAppId"
)}&${localStorage.getItem("_appName")}`;
let signPdf = `https://qik-ai-org.github.io/Sign-MicroappV2/#/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
const hostUrl = window.location.origin + "/loadmf/signmicroapp";
let signPdf = `${hostUrl}/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
const themeBGcolor = themeColor();
let params = {
@@ -951,7 +952,7 @@ function PlaceHolderSign() {
background: themeColor()
}}
>
<span style={{ color: "white" }}>Send Mail </span>
<span style={{ color: "white" }}>Mails Sent</span>
</Modal.Header>
{/* signature modal */}
@@ -23,8 +23,8 @@ import Loader from "./component/loader";
import HandleError from "./component/HandleError";
import Nodata from "./component/Nodata";
import Header from "./component/header";
import ModalComponent from "./component/modalComponent";
import RenderPdf from "./component/renderPdf";
import CustomModal from "./component/CustomModal";
function EmbedPdfImage() {
const { id, contactBookId } = useParams();
const [isSignPad, setIsSignPad] = useState(false);
@@ -67,13 +67,15 @@ function EmbedPdfImage() {
status: false,
type: "load"
});
const [containerWH, setContainerWH] = useState({});
const docId = id && id;
const isMobile = window.innerWidth < 767;
const index = xyPostion.findIndex((object) => {
return object.pageNumber === pageNumber;
});
const divRef = useRef(null);
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
useEffect(() => {
const clientWidth = window.innerWidth;
const pdfWidth = clientWidth - 160 - 220 - 30;
@@ -82,6 +84,14 @@ function EmbedPdfImage() {
setPdfNewWidth(pdfWidth);
getDocumentDetails();
}, []);
useEffect(() => {
if (divRef.current) {
setContainerWH({
width: divRef.current.offsetWidth,
height: divRef.current.offsetHeight
});
}
}, [divRef.current]);
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async () => {
@@ -952,7 +962,37 @@ function EmbedPdfImage() {
) : noData ? (
<Nodata />
) : (
<div className="signatureContainer">
<div className="signatureContainer" ref={divRef}>
{/* this modal is used to show decline alert */}
<CustomModal
containerWH={containerWH}
show={isDecline.isDeclined}
headMsg="Document Declined Alert!"
bodyMssg={
isDecline.currnt === "Sure" ? (
<p>Are you sure want to decline this document ?</p>
) : isDecline.currnt === "YouDeclined" ? (
<p>You have declined this document!</p>
) : (
isDecline.currnt === "another" && (
<p>
You cannot sign this document as it has been declined by one
or more person(s).
</p>
)
)
}
footerMessage={isDecline.currnt === "Sure"}
declineDoc={declineDoc}
setIsDecline={setIsDecline}
/>
{/* this modal is used for show expired alert */}
<CustomModal
containerWH={containerWH}
show={isExpired}
headMsg="Document Expired!"
bodyMssg="This Document is no longer available."
/>
{/* this component used for UI interaction and show their functionality */}
{pdfLoadFail.status &&
!isExpired &&
@@ -971,61 +1011,10 @@ function EmbedPdfImage() {
{/* pdf render view */}
<div
style={{
marginLeft: pdfOriginalWidth > 500 && "20px",
marginRight: pdfOriginalWidth > 500 && "20px"
marginLeft: !isGuestSigner && pdfOriginalWidth > 500 && "20px",
marginRight: !isGuestSigner && pdfOriginalWidth > 500 && "20px"
}}
>
{/* this modal is used for show decline alert */}
<Modal show={isDecline.isDeclined}>
<ModalHeader className="bg-danger" style={{ color: "white" }}>
Document Declined Alert!
</ModalHeader>
<Modal.Body>
{isDecline.currnt === "Sure" ? (
<p>Are you sure want to decline this document ?</p>
) : isDecline.currnt === "YouDeclined" ? (
<p>You have declined this document!</p>
) : (
isDecline.currnt === "another" && (
<p>
You cannot sign this document as it has been declined by
one or more person(s).
</p>
)
)}
</Modal.Body>
<Modal.Footer>
{isDecline.currnt === "Sure" && (
<>
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => setIsDecline({ isDeclined: false })}
>
Close
</button>
<button
style={{
background: "#de4337"
}}
type="button"
className="finishBtn"
onClick={() => declineDoc()}
>
Yes
</button>
</>
)}
</Modal.Footer>
</Modal>
{/* this modal component is used for show expired document */}
<ModalComponent isShow={isExpired} type={"expire"} />
{/* this modal is used show this document is already sign */}
<Modal show={isAlreadySign.status}>
<ModalHeader style={{ background: themeColor() }}>
@@ -1138,9 +1127,9 @@ function EmbedPdfImage() {
)}
</div>
)}
{/* ndD5gdaxqw */}
</DndProvider>
);
}
export default EmbedPdfImage;
export default EmbedPdfImage;
+7 -1
View File
@@ -8,6 +8,7 @@ import Login from "./Component/login";
import DraftDocument from "./Component/DraftDocument";
import PdfRequestFiles from "./Component/PdfRequestFiles";
import LegaDrive from "./Component/LegaDrive/LegaDrive";
import PageNotFound from "./Component/PageNotFound";
// `AppRoutes` is used to define route path of app and
// it expose to host app, check moduleFederation.config.js for more
@@ -33,7 +34,10 @@ function AppRoutes() {
{/*Add default signature of user route */}
<Route path="/managesign" element={<ManageSign />} />
{/* login page route */}
<Route path="/login/:id/:userMail/:contactBookId/:serverUrl" element={<Login />} />
<Route
path="/login/:id/:userMail/:contactBookId/:serverUrl"
element={<Login />}
/>
{/* draft document route to handle and navigate route page accordiing to document status */}
<Route path="/draftDocument" element={<DraftDocument />} />
{/* for user signature (need your sign route) with row level data */}
@@ -42,6 +46,8 @@ function AppRoutes() {
<Route path="/pdfRequestFiles/:docId" element={<PdfRequestFiles />} />
{/* lega drive route */}
<Route path="/legadrive" element={<LegaDrive />} />
{/* Page Not Found */}
<Route path="*" element={<PageNotFound />} />
</Routes>
</div>
);
@@ -0,0 +1,59 @@
.parentDiv {
background: gray;
position: absolute;
z-index: 20;
background-color: rgba(0, 0, 0, 0.75);
display: flex;
flex-direction: column;
align-items: center;
}
.childDiv {
width: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
background-color: #fff;
margin: 30px 20px;
border: 0px 1px 1px 1px solid rgba(0, 0, 0, .2);
border-radius: 0.3rem;
outline: 0;
}
.modalHeadDiv {
padding: 10px;
color: #fff;
border-top-left-radius: 0.3rem;
border-top-right-radius: 0.3rem;
font-size: 17px;
font-weight: 500;
}
.modalBodyDIv {
padding: 20px;
}
.spanTagHead {
color: white !important;
font-size: 17px;
font-weight: 500;
}
.pTagBody {
font-size: 15px !important;
}
.modalFooterDiv {
margin: 10px;
display: flex;
justify-content: flex-end;
}
@media screen and (max-width:500px) {
.childDiv {
margin: 50px 7px;
width: 90%;
}
}
File diff suppressed because it is too large Load Diff
+22 -16
View File
@@ -57,37 +57,41 @@ export function onSaveImage(xyPostion, index, signKey, imgWH, image) {
(data, ind) =>
data.key === signKey && data.Width && data.Height && data.SignUrl
);
if (updateFilter.length > 0) {
let newWidth, nweHeight;
let newWidth, newHeight;
const aspectRatio = imgWH.width / imgWH.height;
const getXYdata = xyPostion[index].pos;
if (aspectRatio === 1) {
newWidth = aspectRatio * 100;
nweHeight = aspectRatio * 100;
newHeight = aspectRatio * 100;
} else if (aspectRatio < 2) {
newWidth = aspectRatio * 100;
nweHeight = 100;
newHeight = 100;
} else if (aspectRatio > 2 && aspectRatio < 4) {
newWidth = aspectRatio * 70;
nweHeight = 70;
newHeight = 70;
} else if (aspectRatio > 4) {
newWidth = aspectRatio * 40;
nweHeight = 40;
newHeight = 40;
} else if (aspectRatio > 5) {
newWidth = aspectRatio * 10;
nweHeight = 10;
newHeight = 10;
}
let getPosData = xyPostion[index].pos.filter((data) => data.key === signKey);
let getPosData = xyPostion[index].pos.filter(
(data) => data.key === signKey
);
const addSign = getXYdata.map((url, ind) => {
if (url.key === signKey) {
return {
...url,
Width: getPosData[0].Width ? getPosData[0].Width : 150,
Height: getPosData[0].Height ? getPosData[0].Height : 60,
Width: getPosData[0].Width ? getPosData[0].Width : newWidth,
Height: getPosData[0].Height ? getPosData[0].Height : newHeight,
SignUrl: image.src,
ImageType: image.imgType
};
@@ -106,7 +110,9 @@ export function onSaveImage(xyPostion, index, signKey, imgWH, image) {
} else {
const getXYdata = xyPostion[index].pos;
let getPosData = xyPostion[index].pos.filter((data) => data.key === signKey);
let getPosData = xyPostion[index].pos.filter(
(data) => data.key === signKey
);
const aspectRatio = imgWH.width / imgWH.height;
let newWidth, newHeight;
@@ -131,9 +137,9 @@ export function onSaveImage(xyPostion, index, signKey, imgWH, image) {
if (url.key === signKey) {
return {
...url,
Width: getPosData[0].Width ? getPosData[0].Width : 150,
Height: getPosData[0].Height ? getPosData[0].Height : 60,
SignUrl: image.src,
Width: getPosData[0].Width ? getPosData[0].Width : newWidth,
Height: getPosData[0].Height ? getPosData[0].Height : newHeight,
SignUrl: image.src,
ImageType: image.imgType
};
}