add SignDocument microfrontend

This commit is contained in:
prafullnavkar-nxg
2023-10-27 21:21:46 +05:30
parent 71001efb93
commit 9b6b5dbe71
69 changed files with 35235 additions and 17 deletions
-16
View File
@@ -51,7 +51,6 @@
"eslint": "^8.51.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.33.2",
"husky": "^8.0.3",
"lint-staged": "^15.0.1",
"postcss": "^8.4.31",
"prettier": "^2.8.0",
@@ -10479,21 +10478,6 @@
"node": ">=10.17.0"
}
},
"node_modules/husky": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
"integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
"dev": true,
"bin": {
"husky": "lib/bin.js"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+1 -1
View File
@@ -37,7 +37,7 @@ export default function reportJson(id) {
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
"remoteUrl=aHR0cDovL2xvY2FsaG9zdDozMDAwL21mYnVpbGQvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
}
]
};
+23
View File
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+70
View File
@@ -0,0 +1,70 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
@@ -0,0 +1,29 @@
const { dependencies } = require("./package.json");
module.exports = {
name: "signmicroapp", // change me
filename: "remoteEntry.js",
exposes: {
// key e.g"./AppRoutes" is path which will use as moduletoload in host app
// value e.g"./src/Routes" is path which will as a bind path to load
"./AppRoutes": "./src/Routes",
},
shared: {
...dependencies,
react: {
singleton: true,
import: "react", // fallback is also react
shareScope: "default",
requiredVersion: dependencies.react,
},
"react-dom": {
singleton: true,
requiredVersion: dependencies["react-dom"],
},
"react-router-dom": {
singleton: true,
requiredVersion: dependencies["react-router-dom"],
},
},
};
File diff suppressed because it is too large Load Diff
+60
View File
@@ -0,0 +1,60 @@
{
"name": "signmicroapp",
"version": "0.1.0",
"private": true,
"dependencies": {
"@radix-ui/themes": "^1.1.2",
"@react-pdf/renderer": "^3.1.9",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.5.1",
"file-saver": "^2.0.5",
"gh-pages": "^6.0.0",
"mf-cra": "^0.0.5",
"parse": "^4.2.0",
"pdf-lib": "^1.17.1",
"print-js": "^1.6.0",
"radix-ui": "^1.0.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-pdf": "^7.4.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-signature-canvas": "^1.0.6",
"reactour": "^1.19.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "mf-cra start",
"build": "CI=false && mf-cra build && mv build ../../apps/OpenSign/public/mfbuild",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> -->
<link
rel="icon"
type="image/png"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
sizes="40x40"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"
/>
<title>OpenSign App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!-- <script
src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"
></script> -->
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+51
View File
@@ -0,0 +1,51 @@
import React from "react";
import { HashRouter } from "react-router-dom";
import AppRoutes from "./Routes";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import {
DndProvider,
TouchTransition,
MouseTransition,
Preview
} from "react-dnd-multi-backend";
import DragElement from "./Component/component/DragElement";
export default function App() {
const HTML5toTouch = {
backends: [
{
id: "html5",
backend: HTML5Backend,
transition: MouseTransition,
},
{
id: "touch",
backend: TouchBackend,
options: { enableMouseEvents: true },
preview: true,
transition: TouchTransition,
},
],
};
const generatePreview = (props) => {
const { item, style } = props;
const newStyle = {
...style,
};
return (
<div style={newStyle}>
<DragElement {...item} />
</div>
);
};
return (
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<HashRouter>
<AppRoutes />
</HashRouter>
</DndProvider>
);
}
@@ -0,0 +1,280 @@
import React, { useEffect, useState } from "react";
import "./certificate.css";
import { Page, Text, View, Document, StyleSheet } from "@react-pdf/renderer";
function Certificate({ pdfData }) {
const [isMultiSigners, setIsMultiSigners] = useState();
const [multiSigner, setMultiSigners] = useState([]);
const [isLoad, setIsLoad] = useState(false);
useEffect(() => {
handleSignerData();
}, []);
const handleSignerData = () => {
const checkSigners = pdfData.filter((data) => data.Signers);
if (checkSigners && checkSigners.length > 0) {
setIsMultiSigners(true);
const checkSignSigners =
pdfData[0].AuditTrail &&
pdfData[0].AuditTrail.length > 0 &&
pdfData[0].AuditTrail.filter((data) => data.Activity === "Signed");
setMultiSigners(checkSignSigners);
} else {
setIsMultiSigners(false);
}
setIsLoad(true);
};
const styles = StyleSheet.create({
page: {
borderRadius: "5px",
padding: "10px",
backgroundColor: "white",
},
section1: {
border: "1px solid rgb(177, 174, 174)",
padding: "20px",
},
textStyle: {
fontWeight: "bold",
fontSize: "11px",
marginBottom: "10px",
},
textStyle2: {
fontWeight: "600",
fontSize: "11px",
marginBottom: "10px",
color: "gray",
},
});
const generatedDate = () => {
const newDate = new Date();
const localExpireDate = newDate.toLocaleDateString("en-US", {
day: "numeric",
month: "long",
year: "numeric",
});
var currentOffset = newDate.getTimezoneOffset();
var ISTOffset = 330; // IST offset UTC +5:30
var ISTTime = new Date(
newDate.getTime() + (ISTOffset + currentOffset) * 60000
);
// ISTTime now represents the time in IST coordinates
var hoursIST = ISTTime.getHours();
var minutesIST = ISTTime.getMinutes();
return (
<Text
style={{
textAlign: "right",
color: "gray",
fontSize: "10px",
marginBottom: "30px",
}}
>
Generated On {localExpireDate} {hoursIST}:{minutesIST} IST
</Text>
);
};
const changeCompletedDate = () => {
const completedOn = pdfData[0].updatedAt;
const newDate = new Date(completedOn);
const localExpireDate = newDate.toLocaleDateString("en-US", {
day: "numeric",
month: "long",
year: "numeric",
});
var currentOffset = newDate.getTimezoneOffset();
var ISTOffset = 330; // IST offset UTC +5:30
var ISTTime = new Date(
newDate.getTime() + (ISTOffset + currentOffset) * 60000
);
// ISTTime now represents the time in IST coordinates
var hoursIST = ISTTime.getHours();
var minutesIST = ISTTime.getMinutes();
return (
<Text style={styles.textStyle2}>
{localExpireDate} {hoursIST}:{minutesIST} IST
</Text>
);
};
const signerName = (data) => {
const getSignerName = pdfData[0].Signers.filter(
(sign) => sign.objectId === data.UserPtr.objectId
);
return (
getSignerName[0] &&
getSignerName.length > 0 && (
<>
<Text style={styles.textStyle}>
Name : &nbsp;
<Text style={styles.textStyle2}>{getSignerName[0].Name}</Text>
</Text>
<Text style={styles.textStyle}>
Email : &nbsp;
<Text style={styles.textStyle2}>{getSignerName[0].Email}</Text>
</Text>
</>
)
);
};
return (
isLoad && (
<Document>
{/** Page defines a single page of content. */}
<Page size="A4" style={styles.page}>
<View style={styles.section1}>
{generatedDate()}
<View style={{ justifyContent: "center" }}>
<Text
style={{
textAlign: "center",
fontSize: "20px",
fontWeight: "bold",
color: "#31bceb",
marginBottom: "10px",
}}
>
{" "}
Certificate of Completion
</Text>
<View style={{ border: "1px solid #bdbbbb" }}></View>
<View>
<View>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px",
}}
>
Summary
</Text>
</View>
<View style={{ display: "flex", flexDirection: "column" }}>
<Text style={styles.textStyle}>
Document ID : &nbsp;
<Text style={styles.textStyle2}>{pdfData[0].objectId}</Text>
</Text>
<Text style={styles.textStyle}>
Document Name : &nbsp;
<Text style={styles.textStyle2}>{pdfData[0].Name}</Text>
</Text>
<Text style={styles.textStyle}>
Organization : &nbsp;
<Text style={styles.textStyle2}>__</Text>
</Text>
<Text style={styles.textStyle}>
Completed on : &nbsp;{changeCompletedDate()}
</Text>
{multiSigner && multiSigner.length > 0 && (
<Text style={styles.textStyle}>
Signers : &nbsp;
<Text style={styles.textStyle2}>
{multiSigner.length}
</Text>
</Text>
)}
</View>
{isMultiSigners ? (
<View style={{ display: "flex", flexDirection: "column" }}>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px",
}}
>
Recipients
</Text>
<View>
{multiSigner && multiSigner.map((data, ind) => {
return (
<View
key={ind}
style={{ display: "flex", flexDirection: "column" }}
>
<View
style={{
border: "0.4px solid #bdbbbb",
marginBottom: "10px",
}}
></View>
{signerName(data)}
<Text style={styles.textStyle}>
Accessed from : &nbsp;
<Text style={styles.textStyle2}>
{data.ipAddress}
</Text>
</Text>
</View>
);
})}
</View>
</View>
) : (
<View style={{ display: "flex", flexDirection: "column" }}>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px",
}}
>
Recipients
</Text>
<Text style={styles.textStyle}>
Signers : &nbsp;<Text style={styles.textStyle2}>1</Text>
</Text>
<Text style={styles.textStyle}>
Name : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].ExtUserPtr.Name}
</Text>
</Text>
<Text style={styles.textStyle}>
Accessed from : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].AuditTrail && pdfData[0].AuditTrail[0].ipAddress}
</Text>
</Text>
<Text style={styles.textStyle}>
Signed on : &nbsp;{changeCompletedDate()}
</Text>
</View>
)}
</View>
</View>
</View>
</Page>
</Document>
)
);
}
export default Certificate;
@@ -0,0 +1,9 @@
.containerBox {
border-radius: 5px;
padding: 10px;
}
.certificateBox {
border: 1px solid rgb(177, 174, 174);
padding: 20px;
}
@@ -0,0 +1,137 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Loader from "./component/loader";
import { getHostUrl } from "../utils/Utils";
function DraftDocument() {
const navigate = useNavigate();
const [pdfDetails, setPdfDetails] = useState([]);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time",
});
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const signObjId =
rowLevel && rowLevel?.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
useEffect(() => {
if (signObjId) {
getDocumentDetails();
}
}, []);
//get document details
const getDocumentDetails = async () => {
await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document?where={"objectId":"${signObjId}"}&include=ExtUserPtr,Signers`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
// console.log("res", res);
if (res[0] && res.length > 0) {
setPdfDetails(res);
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
} else {
alert("No data found!");
}
})
.catch((err) => {
console.log("axois err ", err);
});
};
//check document type and render on signyour self and placeholder route
const handleDraftDoc = () => {
const data = pdfDetails[0];
const checkSignerExist =
pdfDetails[0] && pdfDetails[0].Signers && pdfDetails[0].Signers;
const isPlaceholder =
pdfDetails[0].Placeholders && pdfDetails[0].Placeholders;
const isDecline = data.IsDeclined && data.IsDeclined;
const signUrl = data.SignedUrl && data.SignedUrl;
const expireDate = pdfDetails[0] && pdfDetails[0].ExpiryDate.iso && pdfDetails[0].ExpiryDate.iso;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
const hostUrl = getHostUrl();
//checking document is completed and signer exist then navigate to pdfRequestFiles file
if (data.IsCompleted && checkSignerExist) {
// window.location.hash = `/pdfRequestFiles`;
navigate(`${hostUrl}pdfRequestFiles`);
}
//checking document is completed and signer does not exist then navigate to recipientSignPdf file
else if (data.IsCompleted && !checkSignerExist) {
// window.location.hash = `/recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`;
navigate(
`${hostUrl}recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`
);
}
//checking document is declined by someone then navigate to pdfRequestFiles file
else if (isDecline) {
// window.location.hash = `/pdfRequestFiles`;
navigate(`${hostUrl}pdfRequestFiles`);
}
//checking document has expired and signers exist and placeholder does not set yet then navigate to pdfRequestFiles file
//draft type request sign document
else if (
currDate > expireUpdateDate &&
checkSignerExist &&
!isPlaceholder
) {
// window.location.hash = `/placeHolderSign`;
navigate(`${hostUrl}placeHolderSign`);
}
//checking document has expired and signers does not exist and document not signed yet then navigate to pdfRequestFiles file
//draft type signyouselfdocument
else if (
currDate > expireUpdateDate &&
!checkSignerExist &&
!isPlaceholder &&
!signUrl
) {
// window.location.hash = `/signaturePdf`;
navigate(`${hostUrl}signaturePdf`);
}
//checking document has expired and signers exist and document then navigate to pdfRequestFiles file
else if (currDate > expireUpdateDate) {
// window.location.hash = `/pdfRequestFiles`;
navigate(`${hostUrl}pdfRequestFiles`);
}
//checking document has expired not been expired yet and signers exist and placeholder does not set yet then navigate to pdfRequestFiles file
//draft type request sign document
else if (!signUrl && checkSignerExist) {
navigate(`${hostUrl}placeHolderSign`);
// window.location.hash = `/placeHolderSign`;
} else {
// window.location.hash = `/signaturePdf/`;
navigate(`${hostUrl}signaturePdf/`);
}
};
return (
<div>
{isLoading.isLoad ? <Loader isLoading={isLoading} /> : handleDraftDoc()}
</div>
);
}
export default DraftDocument;
@@ -0,0 +1,608 @@
import React, { useState, useEffect, useRef } from "react";
import folder from "../../../assests/folder.png";
import "../LegaDrive.css";
import draftDoc from "../../../assests/draftDoc1.png";
import pdfLogo from "../../../assests/pdf3.png";
import axios from "axios";
import * as ContextMenu from "@radix-ui/react-context-menu";
import { saveAs } from "file-saver";
import { getHostUrl } from "../../../utils/Utils";
import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
function PdfFileComponent({
pdfData,
setFolderName,
setDocId,
setIsLoading,
setPdfData,
isList,
}) {
const [rename, setRename] = useState("");
const [renameValue, setRenameValue] = useState("");
const inputRef = useRef(null);
const navigate = useNavigate();
//to focus input box on press rename to change doc name
useEffect(() => {
if (rename && inputRef.current) {
// console.log("ref", inputRef.current)
setTimeout(() => {
inputRef.current.focus();
}, 10);
}
}, [rename]);
//function to handle folder component
const handleOnclikFolder = (data) => {
const folderData = {
name: data.Name,
objectId: data.objectId,
};
setFolderName((prev) => [...prev, folderData]);
const loadObj = {
isLoad: true,
message: "This might take some time",
};
setIsLoading(loadObj);
setDocId(data.objectId);
};
//function for change doc name and update doc name in _document class
const handledRenameDoc = async (data) => {
setRename("");
const trimmedValue = renameValue.trim();
if (trimmedValue.length > 0) {
const updateName = {
Name: renameValue,
};
const docId = data.objectId;
const docData = pdfData;
const updatedData = docData.map((item) => {
if (item.objectId === docId) {
// If the item's ID matches the target ID, update the name
return { ...item, Name: renameValue };
}
// If the item's ID doesn't match, keep it unchanged
return item;
});
setPdfData(updatedData);
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${docId}`,
updateName,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((result) => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("error updating field is decline ", err);
});
}
};
//function for navigate user to microapp-signature component
const checkPdfStatus = async (data) => {
// console.log("document", data);
const hostUrl = getHostUrl();
const expireDate = data.ExpiryDate.iso;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
const signerExist = data.Signers && data.Signers;
const signUrl = data.SignedUrl && data.SignedUrl;
const isDecline = data.IsDeclined && data.IsDeclined;
const isPlaceholder = data.Placeholders && data.Placeholders;
//checking and navigate to signyouself page
const checkPlaceHolder = data.Placeholders;
//checking if document has completed
if (data.IsCompleted && signerExist) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} else if (data.IsCompleted && !signerExist) {
navigate(
`${hostUrl}recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`
);
// window.location.hash = `/recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`;
}
//checking if document has declined by someone
else if (isDecline) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} else if (currDate > expireUpdateDate && signerExist && !isPlaceholder) {
// window.location.hash = `/placeHolderSign/${data.objectId}`;
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
} else if (
currDate > expireUpdateDate &&
!signerExist &&
!isPlaceholder &&
!signUrl
) {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
// window.location.hash = `/signaturePdf/${data.objectId}`;
}
//checking if document has expired
else if (currDate > expireUpdateDate) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} //checking if document is draft signers type and signers placeholder does not placed yet
else if (!signUrl && signerExist) {
// window.location.hash = `/placeHolderSign/${data.objectId}`;
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
}
//checking if document is request type and signers placeholder exist and does not completed yet
//then user can check progress of document and sign also
else if (
checkPlaceHolder &&
checkPlaceHolder.length > 0 &&
!data.IsCompleted
) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
}
//checking document is draft and signyourself type then user can sign document
else {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
// navigate(`/signaturePdf/${data.objectId}`);
// window.location.hash = `/signaturePdf/${data.objectId}`;
}
};
//component to handle type of document and render according to type
const handleFolderData = (data, ind, listType) => {
// console.log("data", data);
let createddate,
status,
isDecline,
signerExist,
isExpire,
isComplete,
signUrl,
isPlaceholder;
if (data.Type !== "Folder") {
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
const createdDate = data.createdAt && data.createdAt;
createddate = new Date(createdDate).toLocaleDateString();
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
isDecline = data.IsDeclined && data.IsDeclined;
isPlaceholder = data.Placeholders && data.Placeholders;
signerExist = data.Signers && data.Signers;
signUrl = data.SignedUrl && data.SignedUrl;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
if (currDate > expireUpdateDate) {
isExpire = true;
} else {
isExpire = false;
}
if (isComplete) {
status = "Completed";
} else if (isDecline) {
status = "Declined";
} else if (isExpire && !isPlaceholder && signerExist) {
// status = "Expired";
status = "Draft";
} else if (isExpire && !isPlaceholder && !signerExist && !signUrl) {
// status = "Expired";
status = "Draft";
} else if (isExpire) {
status = "Expired";
} else if (!signUrl) {
status = "Draft";
} else {
status = "InComplete";
}
}
const handleDraftDoc = (data) => {
window.location.hash = `/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9MZWdhR2VuaWUtTWljcm9hcHBWMi9yZW1vdGVFbnRyeS5qcw==&moduleToLoad=AppRoutes&remoteName=legageniemicroapp/legagenie?${data.objectId}`;
};
const handleMenuItemClick = (selectType, data) => {
// console.log("data",data)
if (selectType === "Download") {
// console.log("download")
const pdfName = data && data.Name;
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
saveAs(pdfUrl, `${pdfName}_signed_by_opensign.qik.ai.pdf`);
} else if (selectType === "Rename") {
// console.log("rename")
setRenameValue(data.Name);
setRename(data.objectId);
}
};
const handleEnterPress = (e, data) => {
if (e.key === "Enter") {
handledRenameDoc(data);
}
};
return listType === "table" ? (
data.Type === "Folder" ? (
<tr onClick={() => handleOnclikFolder(data)}>
<td>
<i
className="fa fa-folder"
aria-hidden="true"
style={{ color: "#f0ad26", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
</td>
<td>_</td>
<td>Folder</td>
<td>_</td>
<td>_</td>
</tr>
) : data.Type === "AIDoc" ? (
<tr onClick={() => handleDraftDoc(data)}>
<td>
<i
className="fa fa-file-text"
style={{ color: "#0ea3ed", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
</td>
<td>{createddate}</td>
<td>LegaGenie Draft</td>
<td>_</td>
<td>_</td>
</tr>
) : (
<tr onClick={() => checkPdfStatus(data)}>
<td>
<i
className="fa fa-file-pdf"
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
</td>
<td>{createddate}</td>
<td>Pdf</td>
<td>{status}</td>
<td>
<i
onClick={(e) => {
e.stopPropagation();
handleMenuItemClick("Download", data);
}}
className="fa fa-download"
aria-hidden="true"
style={{ color: "#ed280e", marginRight: "8px" }}
></i>
</td>
</tr>
)
) : listType === "list" && data.Type === "Folder" ? (
<div key={ind} className="folderBox">
<ContextMenu.Root>
<ContextMenu.Trigger className="ContextMenuTrigger">
<div
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer",
}}
>
<img
alt="no img"
onClick={() => handleOnclikFolder(data)}
src={folder}
width={100}
height={103}
/>
{rename === data.objectId ? (
<input
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
autoFocus={true}
type="text"
// onFocus={()=>console.log("focus")}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
}}
/>
) : (
<span className="foldName">{data.Name}</span>
)}
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content
className="ContextMenuContent"
sideOffset={5}
align="end"
>
{/* <ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item> */}
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
) : data.Type === "AIDoc" ? (
<div key={ind} className="folderBox">
<ContextMenu.Root>
<ContextMenu.Trigger className="ContextMenuTrigger">
<div
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer",
}}
>
<img
alt="no img"
onClick={() => handleDraftDoc(data)}
src={draftDoc}
width={100}
height={103}
/>
{rename === data.objectId ? (
<input
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
autoFocus={true}
type="text"
// onFocus={()=>console.log("focus")}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
}}
/>
) : (
<span className="foldName">{data.Name}</span>
)}
</div>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content
className="ContextMenuContent"
sideOffset={5}
align="end"
>
{/* <ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item> */}
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
) : (
<HoverCard.Root>
<HoverCard.Trigger asChild>
<div>
<ContextMenu.Root>
<div className="icon-container">
<ContextMenu.Trigger className="ContextMenuTrigger">
<img
alt="PDF"
className="pdf-icon"
src={pdfLogo}
onClick={() => checkPdfStatus(data)}
/>
{rename === data.objectId ? (
<input
autoFocus={true}
type="text"
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
}
}}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
}}
/>
) : (
<span className="fileName">{data.Name}</span>
)}
</ContextMenu.Trigger>
{status === "Completed" ? (
<div className="status-badge completed">
<i className="fas fa-check-circle"></i>
</div>
) : status === "Declined" ? (
<div className="status-badge declined">
<i className="fa fa-thumbs-down"></i>
</div>
) : status === "Expired" ? (
<div className="status-badge expired">
<i className="fa fa-hourglass-end"></i>
</div>
) : status === "Draft" ? (
<div className="status-badge draft">
<i className="fa fa-file"></i>
</div>
) : (
status === "InComplete" && (
<div className="status-badge in-progress">
<i className="fa fa-paper-plane"></i>
</div>
)
)}
</div>
<ContextMenu.Portal>
<ContextMenu.Content
className="ContextMenuContent"
sideOffset={5}
align="end"
>
<ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item>
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong style={{ fontSize: "13px" }}>Title: </strong>
<span className="statusSpan" style={{ marginBottom: "0px" }}>
{" "}
{data.Name}
</span>
<br />
<strong style={{ fontSize: "13px" }}>Status: </strong>
<span className="statusSpan"> {status}</span>
<br />
<strong style={{ fontSize: "13px" }}>Created Date: </strong>
<span className="statusSpan">{createddate}</span>
<br />
{signerExist && (
<>
<strong style={{ fontSize: "13px" }}>Signers: </strong>
{signerExist.map((data, key) => {
return (
<React.Fragment key={key}>
<span className="statusSpan">{data.Name}, </span>
</React.Fragment>
);
})}
</>
)}
<HoverCard.Arrow className="HoverCardArrow" />
</HoverCard.Content>
</HoverCard.Portal>
</HoverCard.Root>
);
};
//component to handle type of document and render according to type
return isList ? (
<div className="container" style={{overflowX:"auto"}}>
<Table striped bordered hover >
<thead>
<tr>
<th>Name</th>
<th>Created Date</th>
<th>Type</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{pdfData.map((data, ind) => {
return (
<React.Fragment key={ind}>
{handleFolderData(data, ind, "table")}
</React.Fragment>
);
})}
</tbody>
</Table>
</div>
) : (
<div className="pdfContainer">
{pdfData.map((data, ind) => {
return (
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
</div>
);
})}
</div>
);
}
export default PdfFileComponent;
@@ -0,0 +1,530 @@
@import '@radix-ui/colors/black-alpha.css';
@import '@radix-ui/colors/mauve.css';
@import '@radix-ui/colors/violet.css';
.ContextMenuContent,
.ContextMenuSubContent {
min-width: 150px;
background-color: white;
border-radius: 6px;
overflow: hidden;
padding: 5px;
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2);
}
.ContextMenuItem,
.ContextMenuCheckboxItem,
.ContextMenuRadioItem,
.ContextMenuSubTrigger {
font-size: 13px;
line-height: 1;
color: var(--violet-11);
border-radius: 3px;
display: flex;
align-items: center;
height: 25px;
padding: 0 5px;
position: relative;
padding-left: 25px;
user-select: none;
outline: none;
}
.ContextMenuSubTrigger[data-state='open'] {
background-color: gray;
color: var(--violet-11);
}
.ContextMenuItem[data-disabled],
.ContextMenuCheckboxItem[data-disabled],
.ContextMenuRadioItem[data-disabled],
.ContextMenuSubTrigger[data-disabled] {
color: var(--mauve-8);
pointer-events: 'none';
}
.ContextMenuItem[data-highlighted],
.ContextMenuCheckboxItem[data-highlighted],
.ContextMenuRadioItem[data-highlighted],
.ContextMenuSubTrigger[data-highlighted] {
background-color: #dad9db;
color: var(--violet-11);
}
.ContextMenuLabel {
padding-left: 25px;
font-size: 12px;
line-height: 25px;
color: var(--mauve-11);
}
.ContextMenuItemIndicator {
position: absolute;
left: 0;
width: 25px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.RightSlot {
margin-left: auto;
padding-left: 20px;
color: var(--mauve-11);
}
[data-highlighted]>.RightSlot {
color: white;
}
[data-disabled] .RightSlot {
color: var(--mauve-8);
}
.dropdown-menu {
min-width: 10rem;
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.3rem 1.2rem;
clear: both;
font-weight: 400;
color: #393a39;
text-align: inherit;
white-space: nowrap;
background-color: transparent;
border: 0;
}
.dropdown-item:hover {
background-color: #dad9db;
color: var(--violet-11);
}
.itemColor {
font-size: 13px !important;
}
.folderComponent {
margin: 30px;
height: 100%;
}
.folderComponent::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */
}
.modal {
margin-top: 50px;
}
.folderBox {
/* box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
margin: 10px ;
padding: 5px 0px 5px 40px; */
/* overflow: scroll;
display: flex;
justify-content: space-between;
width: 200px; */
margin: 10px;
/* box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px; */
}
.folderContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center
}
/* .folderBox:hover{
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
font-weight: bold;
} */
.pdfName {
font-size: 10px;
font-weight: 600;
}
.viewbotton {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 8px 20px;
color: white;
font-weight: 600;
font-size: 14px;
border: none;
border-radius: 2px;
margin-left: 10px;
}
.viewbotton:focus {
border: none;
outline: none;
}
.folderRender {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
width: 100px;
height: 100px;
margin: 20px;
display: inline-block;
}
.pdfContainer {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-top: 20px;
align-items: center;
justify-content: space-around
}
.pdfContainer .box:last-child {
margin-left: 0;
/* Override the margin on the last box */
margin-right: auto;
/* Align it to the start */
}
.icon-container {
position: relative;
width: 100px;
height: 100px;
margin: 10px;
display: inline-block;
cursor: pointer
}
.hrStyle {
margin-top: 0rem !important;
margin-bottom: 0rem !important;
border: 0;
border-top: 1px solid rgba(0, 0, 0, .1);
}
.pdf-icon {
width: 100%;
height: 100%;
}
.status-badge {
position: absolute;
top: 0;
right: 0;
background-color: #fff;
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.status-badge i {
font-size: 12px;
}
/* Status-specific colors */
.completed {
background-color: #4CAF50;
color: #fff;
}
.in-progress {
background-color: #FFC107;
color: #fff;
}
.declined {
background-color: #F44336;
color: #fff;
}
.expired {
background-color: #9E9E9E;
color: #fff;
}
.draft {
background-color: #079cdc;
color: #fff;
}
.add {
background-color: #05bbdb;
color: #fff;
padding: 8px 15px;
text-align: center;
display: flex;
margin: 5px;
}
.sort {
padding: 8px 15px;
margin: 5px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.sort:hover {
box-shadow: 0 2px 4px rgba(168, 204, 206, 0.1);
background-color: var(--mauve-3);
border-radius: 2px;
}
/* Hover effect */
.info-box {
position: absolute;
bottom: 100%;
left: 0;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px;
width: 180px;
display: none;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
text-align: center;
}
.icon-container:hover .info-box {
display: block;
}
.folderRender:hover .info-box {
display: block;
}
.info-box:hover {
z-index: 999;
}
.addFolderInput {
padding: 10px;
border-radius: 4px;
/* width: 440px; */
font-size: 15px;
border: 0.5px solid rgb(198, 195, 195);
}
.tooltip-inner {
background-color: #fff;
border: 1px solid black;
font-weight: 600;
opacity: 1 !important;
color: rgb(0, 0, 0);
border-radius: 3px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-size: 12px;
}
.fileName {
display: inline-block;
width: 100px;
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
font-size: 11px;
text-align: center;
font-weight: 480;
}
.foldName {
display: inline-block;
width: 100px;
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
font-size: 11px;
text-align: center;
font-weight: 480;
}
.statusSpan {
font-size: 12px;
font-weight: 500;
}
a {
all: unset;
}
.HoverCardContent {
border-radius: 6px;
padding: 5px 10px;
width: 170px;
background-color: white;
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
text-align: center;
align-items: center;
}
.HoverCardContent[data-side='top'] {
animation-name: slideDownAndFade;
}
.HoverCardContent[data-side='right'] {
animation-name: slideLeftAndFade;
}
.HoverCardContent[data-side='bottom'] {
animation-name: slideUpAndFade;
}
.HoverCardContent[data-side='left'] {
animation-name: slideRightAndFade;
}
.HoverCardArrow {
fill: white;
}
@keyframes slideUpAndFade {
0% {
opacity: 0;
transform: translateY(2px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideRightAndFade {
0% {
opacity: 0;
transform: translateX(-2px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideDownAndFade {
0% {
opacity: 0;
transform: translateY(-2px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideLeftAndFade {
0% {
opacity: 0;
transform: translateX(2px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.dropMenuBD {
display: flex;
flex-direction: row;
align-items: center;
}
.dropMenuSS {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
display: none;
}
.folderPath{
overflow-x: auto;
white-space: nowrap;
cursor: pointer;
user-select: none;
}
.folderPath::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */
}
@media (min-width: 310px) and (max-width:550px) {
.pdfContainer {
justify-content: space-around;
}
.sort {
padding: 2px;
}
.folderComponent{
margin: 10px;
}
}
@media screen and (max-width:309px) {
.pdfContainer {
justify-content: center;
}
.sort {
padding: 2px;
}
.folderComponent{
margin: 10px;
}
}
@@ -0,0 +1,722 @@
import React, { useEffect, useState, useRef } from "react";
import axios from "axios";
import "./LegaDrive.css";
import loader from "../../assests/loader2.gif";
import PdfFileComponent from "./FolderDrive/legaDriveComponent";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import { themeColor, iconColor } from "../../utils/ThemeColor/backColor";
function PdfFile() {
const scrollRef = useRef(null);
const [isList, setIsList] = useState(false);
const [selectedSort, setSelectedSort] = useState("Date");
const [sortingOrder, setSortingOrder] = useState("Decending");
const [pdfData, setPdfData] = useState([]);
const [isFolder, setIsFolder] = useState(false);
const [newFolderName, setNewFolderName] = useState();
// const [parentFolderName, setParentFolderName] = useState();
// const [parentFolder, setParentFolder] = useState([]);
const [error, setError] = useState();
const [folderLoader, setIsFolderLoader] = useState(false);
const [isShowSort, setIsShowSort] = useState(false);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time",
});
const [docId, setDocId] = useState();
const [handleError, setHandleError] = useState();
const [folderName, setFolderName] = useState([]);
const senderUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
) &&
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
useEffect(() => {
if (docId) {
getPdfFolderDocumentList();
} else {
getPdfDocumentList();
}
}, [docId]);
//function for get all pdf document list
const getPdfDocumentList = async () => {
const load = {
isLoad: true,
message: "This might take some time",
};
setIsLoading(load);
//?where={"Type":"Folder" }&include=ExtUserPtr,Signers
//?where={"$or":[{"Type":{"$exists":true}},{"Folder":{"$exists":false}}]}&include=ExtUserPtr,Signers
// "CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${jsonSender.objectId}"}
// where={"Folder":{"$exists":false} }&include=ExtUserPtr,Signers`,
await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document?where={"Folder":{"$exists":false},"$or":[{"CreatedBy":{"$exists":false}},{"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${
jsonSender.objectId
}"}}]}&include=ExtUserPtr,Signers`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
// console.log("result", res);
if (res[0] && res.length > 0) {
setPdfData(res);
}
const data = [
{
name: "OpenSignDrive™",
objectId: "",
},
];
setFolderName(data);
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
})
.catch((err) => {
const loadObj = {
isLoad: false,
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
});
};
//function for get parent folder document list
const getPdfFolderDocumentList = async () => {
const load = {
isLoad: true,
message: "This might take some time",
};
setIsLoading(load);
//{"$inQuery":{"where":{"objectId":"${docId}"},"className":"${appName}_Document"}}
//"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${jsonSender.objectId}"}
await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document?where={"Folder":{"__type":"Pointer","className":"${localStorage.getItem(
"_appName"
)}_Document","objectId":"${docId}"},"$or":[{"CreatedBy":{"$exists":false}},{"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${
jsonSender.objectId
}"}}]}&include=ExtUserPtr,Signers,Folder`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
if (res[0] && res.length > 0) {
setPdfData(res);
} else {
setPdfData([]);
}
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
})
.catch((err) => {
const loadObj = {
isLoad: false,
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
});
};
//function for get all pdf document list
const getParentFolder = async () => {
setIsFolder(true);
};
//function for handle folder name path
const handleRoute = (data) => {
let loadObj = {
isLoad: true,
message: "This might take some time",
};
if (data.name === "LegaDrive™") {
setIsLoading(loadObj);
if (docId) {
setDocId();
} else {
setTimeout(() => {
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
}, 1000);
}
} else if (data.name === folderName[folderName.length - 1].name) {
setIsLoading(loadObj);
setTimeout(() => {
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
}, 1000);
} else {
// console.log("go here1",data.objectId)
const findIndex = folderName.findIndex(
(fold) => fold.objectId === data.objectId
);
const newFolder = folderName.slice(0, findIndex + 1);
setFolderName(newFolder);
const getLastId = newFolder[newFolder.length - 1];
setDocId(getLastId.objectId);
setIsLoading(loadObj);
}
};
//function for add new folder name
const handleFolderName = (e) => {
setError();
const value = e.target.value;
setNewFolderName(value);
};
//function for add folder
const handleAddFolder = async () => {
if (newFolderName) {
setIsFolderLoader(true);
const getParentObjId = folderName[folderName.length - 1];
const isParentId = getParentObjId && getParentObjId.objectId;
let data;
if (isParentId) {
data = {
Name: newFolderName,
Type: "Folder",
Folder: {
__type: "Pointer",
className: `${localStorage.getItem("_appName")}_Document`,
objectId: isParentId,
},
};
} else {
data = {
Name: newFolderName,
Type: "Folder",
};
}
// console.log("data", data);
await axios
.post(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
// console.log("Listdata ", Listdata);
const json = Listdata.data;
// console.log("json ", json);
if (json) {
setNewFolderName();
setIsFolderLoader(false);
setIsFolder(false);
if (docId) {
getPdfFolderDocumentList();
} else {
getPdfDocumentList();
}
}
})
.catch((err) => {
console.log("axois err ", err);
alert("something went wrong");
});
} else {
setError("Please fill out this field");
}
};
const sortingApp = (appInfo, type, order) => {
if (type === "Name") {
if (order === "Accending") {
return appInfo.sort((a, b) => (a.Name > b.Name ? 1 : -1));
} else if (order === "Decending") {
return appInfo.sort((a, b) => (a.Name > b.Name ? -1 : 1));
}
} else if (type === "Date") {
if (order === "Accending") {
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1));
} else if (order === "Decending") {
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1));
}
}
};
const sortApps = () => {
const selectedSortType = selectedSort;
const sortOrder = sortingOrder;
let sortingData = pdfData;
if (selectedSortType === "Name") {
sortingApp(sortingData, "Name", sortOrder);
} else if (selectedSortType === "Date") {
sortingApp(sortingData, "Date", sortOrder);
}
setPdfData(sortingData);
};
//function for handle auto scroll on folder path
const handleMouseEnter = (e) => {
let side = "";
const container = scrollRef.current;
const containerRect = container.getBoundingClientRect();
const cursorX = e.clientX;
const containerX = containerRect.left;
const containerWidth = containerRect.width;
// Define a threshold (e.g., 10 pixels) for the start and end points
const threshold = 10;
if (cursorX - containerX <= threshold) {
side = "start";
} else if (containerX + containerWidth - cursorX <= threshold) {
side = "end";
} else {
side = "";
}
const scrollSpeed = 10;
let scrollAmount = 0;
const scroll = () => {
if (side === "start" && container.scrollLeft > 0) {
container.scrollLeft -= scrollAmount;
requestAnimationFrame(scroll);
} else if (
side === "end" &&
container.scrollLeft < container.scrollWidth - container.clientWidth
) {
container.scrollLeft += scrollAmount;
requestAnimationFrame(scroll);
}
};
container.addEventListener("mousemove", (e) => {
const rect = container.getBoundingClientRect();
const x = e.clientX - rect.left;
const containerWidth = rect.width;
// Calculate the scroll amount based on the cursor's position
scrollAmount = (x / containerWidth) * scrollSpeed;
});
scroll();
};
//handle to close drop down menu onclick screen
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (isShowSort && !e.target.closest("#menu-container")) {
setIsShowSort(false);
}
};
document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isShowSort]);
return (
<div className="folderComponent ">
<div>
<Modal show={isFolder}>
<ModalHeader style={{ background: themeColor() }}>
<span style={{ color: "white" }}>Add New Folder</span>
{!folderLoader && (
<span
style={{ cursor: "pointer" }}
onClick={() => {
setNewFolderName("");
setIsFolder(false);
}}
>
X
</span>
)}
</ModalHeader>
<Modal.Body>
{folderLoader ? (
<div
style={{
height: "200px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<img
alt="loader img"
src={loader}
style={{ width: "50px", height: "50px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
Loading...
</span>
</div>
) : (
<form style={{ display: "flex", flexDirection: "column" }}>
<label
style={{
margin: "10px 0px 10px 0px",
fontSize: "15px",
fontWeight: "400",
}}
>
Name*
</label>
<input
required
className="form-control"
type="text"
value={newFolderName}
onChange={(e) => handleFolderName(e)}
// className="addFolderInput"
/>
<span style={{ color: "red", fontSize: "12px" }}>{error}</span>
<div
style={{
margin: "20px 10px 10px 10px ",
display: "flex",
alignItems: "flex-end",
justifyContent: "flex-end",
alignContent: "flex-end",
}}
>
<button
type="submit"
style={{
borderRadius: "0px",
border: "1.5px solid #e3e2e1",
fontWeight: "600",
color: "black",
}}
className="finishBtn"
onClick={() => setIsFolder(false)}
>
Close
</button>
<button
onClick={() => handleAddFolder()}
style={{
background: themeColor(),
}}
type="button"
className="finishBtn"
>
Add
</button>
</div>
</form>
)}
</Modal.Body>
</Modal>
{isLoading.isLoad ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%",
flexDirection: "column",
}}
>
<img
alt="loader img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
) : handleError ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100%",
}}
>
<span style={{ fontSize: "20px", color: "gray" }}>
{handleError}
</span>
</div>
) : (
<>
<div className="folderContainer">
<div
onMouseEnter={(e) => handleMouseEnter(e)}
ref={scrollRef}
style={{
width: "100%",
}}
className="folderPath"
>
{folderName.map((data, id) => {
return (
<span
key={id}
onClick={() => handleRoute(data)}
style={{
color: "#a64b4e",
fontWeight: "400",
cursor: "pointer",
}}
>
{data.name}
<span
style={{
color: "#a64b4e",
fontWeight: "200",
cursor: "pointer",
margin: "0 4px",
}}
>
&gt;
</span>
</span>
);
})}
</div>
<div className="dropMenuBD">
<div
id="menu-container"
class={isShowSort ? "dropdown show" : "dropdown"}
onClick={() => setIsShowSort(!isShowSort)}
>
<div className=" sort " data-toggle="dropdown">
<i
className="fa fa-sort-amount-asc"
aria-hidden="true"
style={{
marginRight: "5px",
fontSize: "14px",
color: `${iconColor()}`,
}}
></i>
<span
style={{
fontSize: "15px",
color: `${iconColor()}`,
}}
>
{selectedSort}
</span>
</div>
<div
className={
isShowSort ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowSort ? "true" : "false"}
>
<span
onClick={() => {
setSelectedSort("Name");
sortApps();
}}
className="dropdown-item itemColor"
>
{selectedSort !== "Name" ? (
<i
className="fa fa-arrow-up"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
) : (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
Name
</span>
<span
onClick={() => {
setSelectedSort("Date");
sortApps();
}}
className="dropdown-item itemColor"
>
{selectedSort !== "Date" ? (
<i
className="fa fa-arrow-up"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
) : (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
Date
</span>
<hr className="hrStyle" />
<span
onClick={() => {
setSortingOrder("Accending");
sortApps();
}}
className="dropdown-item itemColor"
>
{sortingOrder !== "Accending" ? (
<i
className="fa fa-arrow-up"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
) : (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
Accending
</span>
<span
onClick={() => {
setSortingOrder("Decending");
sortApps();
}}
className="dropdown-item itemColor"
>
{sortingOrder !== "Decending" ? (
<i
className="fa fa-arrow-up"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
) : (
<i
className="fa fa-check"
aria-hidden="true"
style={{ marginRight: "5px" }}
></i>
)}
Decending
</span>
</div>
</div>
<div>
{isList ? (
<div className="sort" onClick={() => setIsList(!isList)}>
<i
onClick={() => setIsList(!isList)}
className="fa fa-th-large"
style={{ fontSize: "24px", color: `${iconColor()}` }}
aria-hidden="true"
></i>
</div>
) : (
<div className="sort" onClick={() => setIsList(!isList)}>
<i
className="fa fa-list"
aria-hidden="true"
style={{ fontSize: "23px", color: `${iconColor()}` }}
></i>
</div>
)}
</div>
<div className="sort" onClick={() => getParentFolder()}>
<i
className="fa fa-plus-square"
aria-hidden="true"
style={{ fontSize: "25px", color: `${iconColor()}` }}
></i>
</div>
</div>
</div>
{pdfData && pdfData.length === 0 ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "50vh",
width: "100%",
}}
>
<span style={{ fontWeight: "bold" }}>No Data Found!</span>
</div>
) : (
<PdfFileComponent
pdfData={pdfData}
setFolderName={setFolderName}
setIsLoading={setIsLoading}
setDocId={setDocId}
getPdfFolderDocumentList={getPdfFolderDocumentList}
getPdfDocumentList={getPdfDocumentList}
isDocId={docId}
setPdfData={setPdfData}
isList={isList}
/>
)}
</>
)}
</div>
</div>
);
}
export default PdfFile;
@@ -0,0 +1,704 @@
import React, { useState, useRef, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas";
import redPen from "../assests/redPen.png";
import bluePen from "../assests/bluePen.png";
import blackPen from "../assests/blackPen.png";
import "../css/custom.css";
import "../css/signature.css";
import axios from "axios";
import { toDataUrl } from "../utils/toDataUrl";
// console.log("appName ", appName)
const ManageSign = () => {
let appName;
const [penColor, setPenColor] = useState("blue");
const [initialPen, setInitialPen] = useState("blue");
const [image, setImage] = useState();
const [signName, setSignName] = useState("");
const [signature, setSignature] = useState("");
const [warning, setWarning] = useState(false);
const [nameWarning, setNameWarning] = useState(false);
const [isvalue, setIsValue] = useState(false);
const allColor = [bluePen, redPen, blackPen];
const [isLoader, setIsLoader] = useState(true);
const [isSuccess, setIsSuccess] = useState(false);
const [Initials, setInitials] = useState("");
const [isInitials, setIsInitials] = useState(false);
const [id, setId] = useState("");
const canvasRef = useRef(null);
const imageRef = useRef(null);
const initailsRef = useRef(null);
useEffect(() => {
getLocalStorageValue();
}, []);
//function for get localstorage value first and than call another function.
const getLocalStorageValue = () => {
let User = JSON.parse(
localStorage.getItem(
"Parse/" + localStorage.getItem("parseAppId") + "/currentUser"
)
);
appName =
localStorage.getItem("_appName") && localStorage.getItem("_appName");
if (User) {
fetchUserSign(User);
}
};
const fetchUserSign = async (User) => {
const userId = {
__type: "Pointer",
className: "_User",
objectId: User.objectId,
};
const strObj = JSON.stringify(userId);
const url =
localStorage.getItem("baseUrl") +
`classes/${appName}_Signature?where={"UserId":${strObj}}&limit=1`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// "X-Parse-Session-Token": localStorage.getItem("accesstoken"),
};
const res = await axios
.get(url, { headers: headers })
.then((res) => {
if (res.data.results.length > 0) {
setId(res.data.results[0].objectId);
setSignName(res.data.results[0].SignatureName);
// console.log(res.data.results[0].ImageURL);
setImage(res.data.results[0].ImageURL);
if (res.data.results[0] && res.data.results[0].Initials) {
setInitials(res.data.results[0].Initials);
setIsInitials(true);
}
}
setIsLoader(false);
return res;
})
.catch((error) => {
console.log(error);
alert(`${error.message}`);
});
return res;
};
const handleSignatureChange = () => {
if (imageRef.current) {
imageRef.current.value = "";
}
setImage("");
setSignature(canvasRef.current.toDataURL());
setIsValue(true);
};
const handleClear = () => {
if (canvasRef.current) {
canvasRef.current.clear();
}
if (imageRef.current) {
imageRef.current.value = "";
}
setImage("");
setSignature("");
setIsValue(false);
};
const handleClearInitials = () => {
if (initailsRef.current) {
initailsRef.current.clear();
}
setInitials("");
setIsValue(true);
setIsInitials(false);
};
// const handleReset = () => {
// if (canvasRef.current) {
// canvasRef.current.clear();
// }
// if (imageRef.current) {
// imageRef.current.value = "";
// }
// setImage("");
// setSignature("");
// setIsValue(false);
// setSignName("");
// };
const onImageChange = async (event) => {
if (canvasRef.current) {
canvasRef.current.clear();
}
setSignature("");
if (event.target.files && event.target.files[0]) {
const file = event.target.files[0];
const base64Img = await toDataUrl(file);
setImage(base64Img);
setIsValue(true);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
const isUrl = image.includes("https");
if (!signName) {
setNameWarning(true);
// setTimeout(() => setNameWarning(false), 1000);
} else if (!isvalue) {
setWarning(true);
setTimeout(() => setWarning(false), 1000);
} else {
setIsLoader(true);
const replaceSpace = signName.replace(/ /g, "_");
let file;
if (signature) {
file = base64StringtoFile(signature, `${replaceSpace}.png`);
} else {
if (!isUrl) {
file = base64StringtoFile(image, `${replaceSpace}.png`);
}
}
console.log("isUrl ", isUrl);
let imgUrl;
if (!isUrl) {
imgUrl = await uploadFile(file);
} else {
imgUrl = { data: { imageUrl: image } };
}
let initialsUrl = "";
if (Initials) {
const initialsImg = base64StringtoFile(Initials, `${replaceSpace}.png`);
initialsUrl = await uploadFile(initialsImg);
}
if (imgUrl.data && imgUrl.data.imageUrl) {
await saveEntry({
name: signName,
url: imgUrl.data.imageUrl,
initialsUrl: initialsUrl,
});
}
}
};
function base64StringtoFile(base64String, filename) {
var arr = base64String.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
const uploadFile = async (file) => {
let parseBaseUrl = localStorage.getItem("baseUrl");
parseBaseUrl = parseBaseUrl.slice(0, -4);
const url = parseBaseUrl + `file_upload`;
const formData = new FormData();
formData.append("file", file);
const config = {
headers: {
"content-type": "multipart/form-data",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
},
onUploadProgress: function (progressEvent) {
let percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
// setpercentage(percentCompleted);
},
};
const response = await axios
.post(url, formData, config)
.then((res) => {
if (res.data.status === "Error") {
alert(res.data.message);
}
return res;
})
.catch((error) => {
console.log(error);
setIsLoader(false);
alert(`${error.message}`);
});
return response;
};
const saveEntry = async (obj) => {
if (id) {
const User = JSON.parse(
localStorage.getItem(
"Parse/" + localStorage.getItem("parseAppId") + "/currentUser"
)
);
const url =
localStorage.getItem("baseUrl") +
`classes/${localStorage.getItem("_appName")}_Signature/${id}`;
const body = {
Initials: obj.initialsUrl ? obj.initialsUrl.data.imageUrl : "",
ImageURL: obj.url,
SignatureName: obj.name,
UserId: {
__type: "Pointer",
className: "_User",
objectId: User.objectId,
},
};
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// "X-Parse-Session-Token": localStorage.getItem("accesstoken"),
};
const res = await axios
.put(url, body, { headers: headers })
.then((res) => {
// handleReset();
setIsLoader(false);
setIsSuccess(true);
return res;
})
.catch((error) => {
console.log(error);
setIsLoader(false);
alert(`${error.message}`);
});
return res;
} else {
const User = JSON.parse(
localStorage.getItem(
"Parse/" + localStorage.getItem("parseAppId") + "/currentUser"
)
);
const url =
localStorage.getItem("baseUrl") +
`classes/${localStorage.getItem("_appName")}_Signature`;
const body = {
Initials: obj.initialsUrl ? obj.initialsUrl.data.imageUrl : "",
ImageURL: obj.url,
SignatureName: obj.name,
UserId: {
__type: "Pointer",
className: "_User",
objectId: User.objectId,
},
};
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// "X-Parse-Session-Token": localStorage.getItem("accesstoken"),
};
const res = await axios
.post(url, body, { headers: headers })
.then((res) => {
// handleReset();
setIsLoader(false);
setIsSuccess(true);
return res;
})
.catch((error) => {
console.log(error);
setIsLoader(false);
alert(`${error.message}`);
});
return res;
}
};
const handleSignatureBtn = () => {
if (imageRef.current) {
imageRef.current.value = "";
}
setImage("");
};
const handleUploadBtn = () => {
imageRef.current.click();
};
const handleInitialsChange = () => {
setInitials(initailsRef.current.toDataURL());
setIsValue(true);
};
return (
<div
style={{
maxHeight: "500px",
overflow: "auto",
}}
>
{isSuccess && (
<div
className="alert alert-success successBox"
role="alert"
onAnimationEnd={() => setIsSuccess(false)}
>
Signature saved successfully!
</div>
)}
<div
className="mainDiv"
style={{
// height: "100%",
width: "100%",
paddingRight: "10px",
// maxHeight:"500px",
}}
>
<div style={{ margin: 20 }}>
<div
style={{
fontWeight: "700",
fontSize: 15,
color: "#000",
paddingBottom: 8,
}}
>
Manage Signature
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<label
htmlFor="signName"
style={{
fontSize: 12,
position: "relative",
paddingBottom: 5,
}}
>
Signer's Name
{nameWarning && (
<div
className="warning nameWarning"
style={{ fontSize: 12 }}
onAnimationEnd={() => setNameWarning(false)}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please fill input field
</div>
)}
</label>
<input
id="signName"
className="signInputManage"
value={signName}
style={{
border: "1px solid #ccc",
borderRadius: 4,
height: 35,
padding: "6px 12px",
marginBottom: 10,
fontSize: 12,
}}
required
onFocus={() => setNameWarning(false)}
onChange={(e) => setSignName(e.target.value)}
/>
<label
htmlFor="imgoverview"
style={{
fontSize: 12,
paddingBottom: 5,
}}
>
Signature/Image
</label>
</div>
<div className="signBlock">
<div>
<div style={{ position: "relative" }}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "50%",
paddingLeft: 10,
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
gap: 10,
marginBottom: 10,
}}
>
<>
<span
onClick={() => handleSignatureBtn()}
className="signature"
>
Signature
</span>
</>
<span
onClick={() => handleUploadBtn()}
className="imgupload"
>
Upload Image
</span>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/*"
ref={imageRef}
hidden
/>
</div>
</div>
<div style={{ position: "relative" }}>
<div>
{image ? (
<div
style={{
position: "relative",
border: "2px solid #888",
marginBottom: 6,
}}
className="signatureCanvas"
>
<img
alt="preview image"
src={image}
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</div>
) : (
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
width: "456px",
height: "180px",
className: "signatureCanvas",
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
/>
)}
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
// width: 460,
}}
className="penContainer"
>
<div>
{!image && (
<div
style={{ display: "flex", flexDirection: "row" }}
>
{allColor.map((data, key) => {
return (
<img
alt="black img"
style={{
border: "none",
margin: "5px",
borderBottom:
key == 0 && penColor == "blue"
? "2px solid blue"
: key == 1 && penColor == "red"
? "2px solid red"
: key == 2 && penColor == "black"
? "2px solid black"
: "2px solid white",
}}
onClick={() => {
if (key == 0) {
setPenColor("blue");
} else if (key == 1) {
setPenColor("red");
} else if (key == 2) {
setPenColor("black");
}
}}
key={key}
src={data}
width={20}
height={20}
/>
);
})}
</div>
)}
</div>
<div>
<div
type="button"
className="clearbtn"
onClick={() => handleClear()}
>
Clear
</div>
</div>
</div>
</div>
{warning && (
<div
className="warning signWarning"
style={{ fontSize: 12 }}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please upload signature/Image
</div>
)}
</div>
</div>
</div>
<div style={{ position: "relative" }}>
<div style={{ margin: "6px 5px 18px" }}>
<span
// onClick={() => handleSignatureBtn()}
className="signature"
>
Initials
</span>
</div>
<div>
{isInitials ? (
<div
style={{
position: "relative",
border: "2px solid #888",
marginBottom: 6,
}}
className="signatureCanvas"
>
<img
alt="preview image"
src={Initials}
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</div>
) : (
<SignatureCanvas
ref={initailsRef}
penColor={initialPen}
canvasProps={{
width: "456px",
height: "180px",
className: "signatureCanvas",
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleInitialsChange(initailsRef.current.toDataURL())
}
dotSize={1}
/>
)}
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
// width: 460,
}}
className="penContainer"
>
<div>
{!isInitials && (
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<img
style={{
border: "none",
margin: "5px",
borderBottom:
key == 0 && initialPen == "blue"
? "2px solid blue"
: key == 1 && initialPen == "red"
? "2px solid red"
: key == 2 && initialPen == "black"
? "2px solid black"
: "2px solid white",
}}
onClick={() => {
if (key == 0) {
setInitialPen("blue");
} else if (key == 1) {
setInitialPen("red");
} else if (key == 2) {
setInitialPen("black");
}
}}
key={key}
src={data}
width={20}
height={20}
/>
);
})}
</div>
)}
</div>
<div>
<div
type="button"
className="clearbtn"
onClick={() => handleClearInitials()}
>
Clear
</div>
</div>
</div>
</div>
{/* {!warning && (
<div className="warning signWarning" style={{ fontSize: 12 }}>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please upload signature/Image
</div>
)} */}
</div>
</div>
<div style={{ paddingTop: 10 }}>
<button
className="customBtn successBtn"
onClick={(e) => handleSubmit(e)}
>
save
</button>
</div>
</div>
{isLoader && (
<div className="loaderContainer">
<div className="loader"></div>
</div>
)}
</div>
</div>
);
};
export default ManageSign;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,58 @@
import React from "react";
import stamp from "../../assests/stamp2.png";
import sign from "../../assests/sign3.png";
import { themeColor } from "../../utils/ThemeColor/backColor";
function DragElement({ type, id }) {
const selectedId = id;
return (
<div>
{selectedId === 3 ? (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none",
}}
>
<img
alt="sign img"
style={{
width: "25px",
height: "23px",
background: themeColor(),
}}
src={sign}
/>
<span style={{ color: themeColor(), fontSize: "12px" }}>
Signature
</span>
</div>
) : (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none",
}}
>
<img
alt="stamp img"
style={{
width: "25px",
height: "23px",
background: themeColor(),
}}
src={stamp}
/>
<span style={{ color: themeColor(), fontSize: "12px" }}>Stamp</span>
</div>
)}
</div>
);
}
export default DragElement;
@@ -0,0 +1,18 @@
import React from "react";
function HandleError({ handleError }) {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<span style={{ fontSize: "20px", color: "gray" }}>{handleError}</span>
</div>
);
}
export default HandleError;
@@ -0,0 +1,18 @@
import React from "react";
function Nodata() {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "50vh",
}}
>
<span style={{ fontWeight: "bold" }}>No Data Found!</span>
</div>
);
}
export default Nodata;
@@ -0,0 +1,122 @@
import React, { useEffect } from "react";
import axios from "axios";
function DefaultSignature({
themeColor,
defaultSignImg,
setShowAlreadySignDoc,
setDefaultSignImg,
userObjectId,
setIsLoading,
xyPostion,
}) {
useEffect(() => {
getDefaultSignature(userObjectId);
}, []);
//function for fetch default sigature added bu users
const getDefaultSignature = async (userObjectId) => {
await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${userObjectId}"}}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
if (res[0] && res.length > 0) {
setDefaultSignImg(res[0].ImageURL);
}
const loadObj = {
isLoad: false,
};
setIsLoading(loadObj);
})
.catch((err) => {
// this.setState({ loading: false });
console.log("axois err ", err);
});
};
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
const alreadySign = {
status: true,
mssg: "Are you sure you want to sign at requested locations?",
sure: true,
};
setShowAlreadySignDoc(alreadySign);
} else {
alert("please select position!");
}
};
return (
<div className="signerComponent">
<div
style={{
background: themeColor(),
color: "white",
padding: "5px",
fontFamily: "sans-serif",
}}
>
Signature
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginTop: "10px",
fontWeight: "600",
}}
>
{defaultSignImg ? (
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
src={defaultSignImg}
/>
</div>
<button
style={{
background: themeColor(),
color: "white",
marginTop: "20px",
cursor: "pointer",
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
) : (
<div style={{ margin: "10px" }}>
<span>You didn't add your default signature yet!</span>
</div>
)}
</div>
</div>
);
}
export default DefaultSignature;
@@ -0,0 +1,385 @@
import React, { useState } from "react";
import { saveAs } from "file-saver";
import celebration from "../../assests/newCeleb.gif";
import close from "../../assests/close.png";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import axios from "axios";
import { getBase64FromUrl } from "../../utils/Utils";
import { themeColor } from "../../utils/ThemeColor/backColor";
import printModule from "print-js";
import loader from "../../assests/loader2.gif";
function EmailComponent({
isEmail,
pdfUrl,
isCeleb,
setIsEmail,
setSuccessEmail,
signObjId,
pdfName,
sender,
}) {
const [emailCount, setEmailCount] = useState([]);
const [emailValue, setEmailValue] = useState();
const [isLoading, setIsLoading] = useState(false);
//function for send email
const sendEmail = async () => {
setIsLoading(true);
let sendMail, recipent;
for (let i = 0; i < emailCount.length; i++) {
// await axios
// .get(
// `${localStorage.getItem("baseUrl")}classes/${localStorage.getItem("_appName")}_Contactbook?where={"Email":"${emailCount[i]}"}`,
// {
// headers: {
// "Content-Type": "application/json",
// "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// sessionToken: localStorage.getItem("accesstoken"),
// },
// }
// )
// .then((Listdata) => {
// const json = Listdata.data;
// if (json.results[0]) {
// recipent = json.results[0];
// }
// })
// .catch((err) => {
// console.log("axois err ", err);
// });
try {
console.log("recipients", recipent);
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
// "https://qikinnovation.ams3.digitaloceanspaces.com/mailLogo_2023-08-18T12%3A51%3A31.573Z.png";
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken"),
};
// const serverUrl = localStorage.getItem("baseUrl");
// const newServer = serverUrl.replaceAll("/", "%2F");
// const serverParams = `${newServer}&${localStorage.getItem(
// "parseAppId"
// )}&${localStorage.getItem("_appName")}`;
// let pdfUrlWeb = `<span><a href=https://qik-ai-org.github.io/Sign-MicroappV2/#/login/${signObjId}/${recipent.Email}/${serverParams} style='cursor: pointer;'> ${pdfName}</a><span>`;
const themeBGcolor = themeColor();
let params = {
pdfName: pdfName,
url: pdfUrl,
recipient: emailCount[i],
subject: `${sender.name} has signed the doc - ${pdfName}`,
from: sender.email,
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
imgPng +
" height='50' style='padding:20px,width:170px,height:40px'/> </div><div style='padding:2px;font-family:system-ui; background-color:" +
themeBGcolor +
";'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " +
pdfName +
" Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender " +
sender.email +
" directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p></div></div></body></html>",
//"html":"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color: white;padding-bottom: 20px;'> <div><img src="+imgPng +" height='50' style='padding: 20px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color: #47a3ad;'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " + recipent.Name + " has requested you to review and sign" +pdfName+"</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><span>Sender</span><span>" + recipent.Email + " </span></div><div style='display: flex; justify-content: center;margin-top: 10px;'><button style='padding: 8px 15px 8px 15px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;'>Sign here</button></div></div></div><div><p> This is an automated email from Open Sign. For any queries regarding this email, please contact the sender " + recipent.Email + " directly.If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p> </div></div></body> </html>"
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log("error", error);
setIsLoading(false);
alert("Something went wrong!");
}
}
if (sendMail.data.result.status === "success") {
setIsEmail(false);
setSuccessEmail(true);
setTimeout(() => {
setSuccessEmail(false);
}, 3000);
setIsLoading(false);
} else if (sendMail.data.result.status === "error") {
setIsLoading(false);
alert("Something went wrong!");
} else {
setIsLoading(false);
alert("Something went wrong!");
}
};
//function for remove email
const removeChip = (index) => {
const updateEmailCount = emailCount.filter((data, key) => key !== index);
setEmailCount(updateEmailCount);
};
//function for get email value
const handleEmailValue = (e) => {
const value = e.target.value;
setEmailValue(value);
};
//function for save email in array after press enter
const handleEnterPress = (e) => {
if (e.key === "Enter" && emailValue) {
setEmailCount((prev) => [...prev, emailValue]);
setEmailValue("");
} else if (e === "add" && emailValue) {
setEmailCount((prev) => [...prev, emailValue]);
setEmailValue("");
}
};
// function for print signed pdf
const handleToPrint = async (event) => {
event.preventDefault();
const base64 = await getBase64FromUrl(pdfUrl);
printModule({ printable: base64, type: "pdf", base64: true });
// window.print('https://nxgcreator.s3.ap-south-1.amazonaws.com/exported_file_32_2023-08-22T04%3A59%3A13.432Z.pdf',);
};
//handle download signed pdf
const handleDownloadPdf = () => {
saveAs(pdfUrl, `${pdfName}_signed_by_opensign.qik.ai.pdf`);
};
return (
<div>
{/* isEmail */}
<Modal show={isEmail}>
{isLoading && (
<div
style={{
position: "absolute",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
zIndex: "20",
backgroundColor: "#e6f2f2",
opacity: 0.8,
}}
>
<img
alt="no img"
src={loader}
style={{ width: "70px", height: "70px" }}
/>
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
This might take some time
</span>
</div>
)}
<ModalHeader style={{ background: themeColor() }}>
<span style={{ color: "white" }}>Email Documents</span>
<div style={{ display: "flex", flexDirection: "row" }}>
<div></div>
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
className="btn btn-primary btn-sm"
>
<i
class="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white",
}}
aria-hidden="true"
></i>
Print
</button>
<button
className="btn btn-danger btn-sm"
onClick={() => handleDownloadPdf()}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px",
}}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Download
</button>
</div>
</ModalHeader>
<Modal.Body>
{isCeleb && (
<div
style={{
position: "absolute",
marginLeft: "50px",
}}
>
<img
alt="print img"
width={300}
height={250}
// style={styles.gifCeleb}
src={celebration}
/>
</div>
)}
<p
style={{
fontFamily: "system-ui",
verticalAlign: "baseline",
fontWeight: "500",
color: "#403f3e",
fontSize: "15px",
}}
>
Recipients added here will get a copy of the signed document.
</p>
{emailCount.length > 0 ? (
<>
<div className="addEmail">
<div
style={{
display: "flex",
flexDirection: "row",
width: "450px",
flexWrap: "wrap",
}}
>
{emailCount.map((data, ind) => {
return (
<div className="emailChip" key={ind}>
<span
style={{
color: "white",
fontSize: "13px",
marginRight: "20px",
}}
>
{data}
</span>
<img
alt="print img"
onClick={() => removeChip(ind)}
src={close}
width={10}
height={10}
style={{ fontWeight: "600" }}
/>
</div>
);
})}
</div>
{emailCount.length <= 9 && (
<input
type="text"
value={emailValue}
className="addEmailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
required
/>
)}
</div>
</>
) : (
<div>
<input
type="text"
value={emailValue}
className="emailInput"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
required
/>
</div>
)}
<span
style={{
cursor: emailValue && "pointer",
}}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i
style={{
backgroundColor: themeColor(),
padding: "2px 10px",
marginTop: "10px",
color: "white",
}}
class="fa fa-plus"
aria-hidden="true"
></i>
</span>
<div
style={{
background: "#e3e2e1",
marginTop: "30px",
padding: "5px",
borderRadius: "3px",
}}
>
<span style={{ fontWeight: "700" }}>Note:</span>
<span style={{ fontSize: "15px" }}>
{" "}
You can only send to ten recipients at a time.
</span>
</div>
</Modal.Body>
<Modal.Footer>
<button
style={{
color: "black",
}}
type="button"
className="finishBtn"
onClick={() => setIsEmail(false)}
>
Close
</button>
<button
disabled={emailCount.length === 0 && true}
style={{
background: themeColor(),
color: "white",
}}
type="button"
className={emailCount.length === 0 ? "defaultBtn" : "finishBtn"}
onClick={() => sendEmail()}
>
Send
</button>
</Modal.Footer>
</Modal>
</div>
);
}
export default EmailComponent;
@@ -0,0 +1,366 @@
import React, { useState } from "react";
import * as Select from "@radix-ui/react-select";
import classnames from "classnames";
function FieldsComponent({
pdfUrl,
sign,
stamp,
dragSignature,
signRef,
handleDivClick,
handleMouseLeave,
isDragSign,
themeColor,
dragStamp,
dragRef,
isDragStamp,
dragSignatureSS,
dragStampSS,
isDragSignatureSS,
isSignYourself,
addPositionOfSignature,
signerPos,
signersdata,
isSelectListId,
setSignerObjId,
setIsSelectId,
setContractName,
isSigners,
}) {
const signStyle = pdfUrl ? "disableSign" : "signatureBtn";
const isMobile = window.innerWidth < 712;
const [isShowSort, setIsShowSort] = useState(false);
const [selectedEmail, setSelectedEmail] = useState("");
const SelectItem = React.forwardRef(
({ children, className, ...props }, forwardedRef) => {
return (
<Select.Item
className={classnames("SelectItem", className)}
{...props}
ref={forwardedRef}
>
<Select.ItemText>{children}</Select.ItemText>
<Select.ItemIndicator className="SelectItemIndicator"></Select.ItemIndicator>
</Select.Item>
);
}
);
console.log("sign",isSignYourself)
return (
<>
<div className="signerComponent">
<div
style={{
background: themeColor(),
padding: "5px",
}}
>
<span className="signedStyle">Fields</span>
</div>
<div className="signLayoutContainer1">
{pdfUrl ? (
<>
<button className={signStyle} disabled={true}>
<span
style={{
fontWeight: "400",
fontSize: "15px",
padding: "3px 20px 0px 20px",
color: "#bfbfbf",
}}
>
Signature
</span>
<img
alt="sign img"
style={{
width: "30px",
height: "28px",
background: "#d3edeb",
}}
src={sign}
/>
</button>
<button className={signStyle} disabled={true}>
<span
style={{
fontWeight: "400",
fontSize: "15px",
padding: "3px 0px 0px 35px",
color: "#bfbfbf",
}}
>
Stamp
</span>
<img
alt="stamp img"
style={{
width: "25px",
height: "28px",
background: "#d3edeb",
}}
src={stamp}
/>
</button>
</>
) : (
<>
<div
ref={(element) => {
dragSignature(element);
if (element) {
signRef.current = element;
// const height = signRef && signRef.current.offsetHeight;
// const width = signRef && signRef.current.offsetWidth;
}
}}
className={signStyle}
onMouseMove={handleDivClick}
onMouseDown={handleMouseLeave}
style={{
opacity: isDragSign ? 0.5 : 1,
boxShadow:
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)",
}}
>
<span
style={{
fontWeight: "400",
fontSize: "15px",
padding: "3px 20px 0px 20px",
color: "black",
}}
>
Signature
</span>
<img
alt="sign img"
style={{
width: "30px",
height: "28px",
background: themeColor(),
}}
src={sign}
/>
</div>
<div
ref={(element) => {
dragStamp(element);
dragRef.current = element;
if (isDragStamp) {
// setIsStamp(true);
}
}}
className={!pdfUrl ? signStyle : "disableSign"}
disabled={pdfUrl && true}
onMouseMove={handleDivClick}
onMouseDown={handleMouseLeave}
style={{
opacity: isDragStamp ? 0.5 : 1,
boxShadow:
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)",
}}
>
<span
style={{
fontWeight: "400",
fontSize: "15px",
padding: "3px 0px 0px 35px",
color: !pdfUrl ? "black" : "#bfbfbf",
}}
>
Stamp
</span>
<img
alt="stamp img"
style={{
width: "25px",
height: "28px",
background: themeColor(),
}}
src={stamp}
/>
</div>
</>
)}
</div>
</div>
{isMobile && isSignYourself && (
<div
id="navbar"
className="stickyfooter"
style={{ width: window.innerWidth + "px" }}
>
{isSigners && (
<div
style={{
background: "#887abf",
padding: "10px 20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<span style={{ fontSize: "18px", fontWeight: "500" }}>
Signer :
</span>
<Select.Root onValueChange={(e) => console.log("value", e)}>
<Select.Trigger
onValueChange={(e) => console.log("value", e)}
className="SelectTrigger"
aria-label="Food"
>
<Select.Value placeholder="Select signer" />
<Select.Icon className="SelectIcon">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
class="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content
className="SelectContent"
style={{ zIndex: "5000" }}
>
<Select.ScrollUpButton className="SelectScrollButton">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
class="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.ScrollUpButton>
<Select.Viewport className="SelectViewport">
<Select.Group>
{signersdata.Signers.map((obj, ind) => {
return (
<SelectItem key={ind} value={selectedEmail}>
{" "}
{obj.Email}
</SelectItem>
);
})}
</Select.Group>
</Select.Viewport>
<Select.ScrollDownButton className="SelectScrollButton">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
class="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.ScrollDownButton>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
)}
<div
className="signLayoutContainer2"
style={{ backgroundColor: themeColor() }}
>
<div
onClick={() => addPositionOfSignature("onclick", false)}
ref={(element) => {
dragSignatureSS(element);
if (element) {
signRef.current = element;
// const height = signRef && signRef.current.offsetHeight;
// const width = signRef && signRef.current.offsetWidth;
}
}}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none",
opacity: isDragSignatureSS ? 0.5 : 1,
backgroundImage: `url(${sign})`,
backgroundSize: "70% 70%",
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
paddingBottom: "2.2rem",
}}
>
{/* <img
alt="sign img"
style={{
width: "30px",
height: "30px",
background: themeColor(),
}}
src={sign}
/> */}
<span
style={{
color: "white",
fontSize: "12px",
position: "relative",
top: "2.6rem",
}}
>
Signature
</span>
</div>
<div
onClick={() => addPositionOfSignature("onclick", true)}
ref={(element) => {
dragStampSS(element);
dragRef.current = element;
}}
onMouseMove={handleDivClick}
onMouseDown={handleMouseLeave}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
userSelect: "none",
backgroundImage: `url(${stamp})`,
backgroundSize: "32px 33px",
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
paddingBottom: "2.2rem",
}}
>
<span
style={{
color: "white",
fontSize: "12px",
position: "relative",
top: "2.6rem",
}}
>
Stamp
</span>
</div>
</div>
</div>
)}
</>
);
}
export default FieldsComponent;
@@ -0,0 +1,660 @@
import React from "react";
import PrevNext from "./prevNext";
import { PDFDownloadLink } from "@react-pdf/renderer";
import Certificate from "../Certificate/Certificate";
import printModule from "print-js";
import { getBase64FromUrl } from "../../utils/Utils";
import { saveAs } from "file-saver";
import "../../css/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import { themeColor } from "../../utils/ThemeColor/backColor";
function Header({
isPdfRequestFiles,
isPlaceholder,
recipient,
setIsDecline,
pageNumber,
isAlreadySign,
allPages,
changePage,
pdfUrl,
documentStatus,
embedImages,
pdfDetails,
signerPos,
signersdata,
isMailSend,
alertSendEmail,
isSigned,
isCompleted,
isShowHeader,
decline,
currentSigner,
}) {
const isMobile = window.innerWidth < 712;
const navigate = useNavigate();
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
//function for show decline alert
const handleDeclinePdfAlert = async () => {
const currentDecline = {
currnt: "Sure",
isDeclined: true,
};
setIsDecline(currentDecline);
};
//function for print digital sign pdf
const handleToPrint = async (event) => {
event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl);
printModule({ printable: pdf, type: "pdf", base64: true });
};
//handle download signed pdf
const handleDownloadPdf = () => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
saveAs(pdfUrl, `${pdfName}_signed_by_opensign.qik.ai.pdf`);
};
return (
<div
style={{ paddingBottom: "5px", paddingTop: "5px" }}
className="mobileHead"
>
<div className="signatureHeader headerBtn">
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{recipient ? (
pdfUrl || isAlreadySign.mssg ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{pdfDetails[0] && pdfDetails.length > 0 && (
// <Certificate
// pdfData={completePdfData}
// />
<PDFDownloadLink
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ blob, url, loading, error }) =>
loading ? (
"Loading document..."
) : (
<button
type="button"
className="defaultBtn certificateBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<i
class="fa fa-certificate"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Certificate
</button>
)
}
</PDFDownloadLink>
)}
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
className="defaultBtn printBtn"
>
<i
class="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white",
}}
aria-hidden="true"
></i>
Print
</button>
<button
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
className="defaultBtn downloadBtn"
onClick={() => handleDownloadPdf()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Download
</button>
</div>
) : (
<div style={{ display: "flex", flexDirection: "row" }}>
<button
style={{
background: "#de4337",
}}
className="finishBtn hoverCss"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
data-tut="reactourThird"
style={{
background: !pdfUrl ? "#188ae2" : "#d3edeb",
}}
className="finishBtn sendHover"
onClick={() => {
if (!pdfUrl) {
embedImages();
}
}}
>
Finish
</button>
</div>
)
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
<div style={{ display: "flex", flexDirection: "row" }}>
<PDFDownloadLink
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ blob, url, loading, error }) =>
loading ? (
"Loading document..."
) : (
<button
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
className="defaultBtn certificateBtn"
>
<i
class="fa fa-certificate"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Certificate
</button>
)
}
</PDFDownloadLink>
<button
onClick={handleToPrint}
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
className="defaultBtn printBtn"
>
<i
class="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white",
}}
aria-hidden="true"
></i>
Print
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px",
}}
onClick={() => handleDownloadPdf()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Download
</button>
</div>
) : isPlaceholder ? (
<>
{!isMailSend &&
signersdata.Signers &&
signersdata.Signers.length !== signerPos.length && (
<div>
{signerPos.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add all {signersdata.Signers.length - signerPos.length}{" "}
recipients signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.Signers.length - signerPos.length} more
recipients signature
</span>
)}
</div>
)}
<div>
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="defaultBtn backBtn"
>
Back
</button>
<button
data-tut="reactourFour"
style={{
background: !isMailSend ? "#188ae2" : "#d3edeb",
}}
onClick={() => {
alertSendEmail();
}}
className="finishBtn sendHover"
>
Send
</button>
</div>
</>
) : isPdfRequestFiles ? (
isSigned ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted.isCertificate && (
<PDFDownloadLink
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ blob, url, loading, error }) =>
loading ? (
"Loading document..."
) : (
<button
type="button"
className="defaultBtn certificateBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<i
class="fa fa-certificate"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Certificate
</button>
)
}
</PDFDownloadLink>
)}
<button
onClick={handleToPrint}
type="button"
className="defaultBtn printBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<i
class="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white",
}}
aria-hidden="true"
></i>
Print
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
onClick={() => handleDownloadPdf()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Download
</button>
</div>
) : (
<div>
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="defaultBtn backBtn"
>
Back
</button>
{currentSigner && (
<>
<button
style={{
background: "#de4337",
}}
className="finishBtn hoverCss"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
style={{
background: "#188ae2",
}}
type="button"
className="finishBtn sendHover"
onClick={() => {
embedImages();
}}
>
Finish
</button>
</>
)}
</div>
)
) : (
<div>
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="defaultBtn backBtn"
>
Back
</button>
<button
style={{
background: "#188ae2",
}}
type="button"
className="finishBtn sendHover"
onClick={() => {
if (!pdfUrl) {
embedImages();
}
}}
>
Finish
</button>
</div>
)}
</div>
{isMobile && isShowHeader && (
<div
id="navbar"
className="stickyHead"
style={{ width: window.innerWidth - 30 + "px" }}
>
<div className="preBtn2">
<div
onClick={() => {
navigate(-1);
}}
>
<i
className="fa fa-arrow-left"
aria-hidden="true"
style={{ color: themeColor(), cursor: "pointer" }}
></i>
</div>
<div>
<i
onClick={() => {
if (pageNumber > 1) {
previousPage();
}
}}
className="fa fa-backward"
style={{ color: "gray", cursor: "pointer" }}
></i>
<span
style={{
fontSize: "13px",
fontWeight: "600",
marginLeft: "10px",
marginRight: "10px",
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<i
onClick={() => {
if (pageNumber < allPages) {
nextPage();
}
}}
className="fa fa-forward"
style={{ color: "gray", cursor: "pointer" }}
></i>
</div>
{pdfUrl ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div
// onClick={() => handleDownloadPdf()}
style={{
color: themeColor(),
border: "none",
fontWeight: "650",
fontSize: "16px",
}}
>
<i class="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="DropdownMenuContent"
sideOffset={5}
>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadPdf()}
>
<div
style={{
display: "flex",
flexDirection: "row",
}}
>
<i class="fa fa-arrow-down" aria-hidden="true" style={{marginRight:"2px"}}></i>
Download
</div>
</DropdownMenu.Item>
<DropdownMenu.Item className="DropdownMenuItem">
<PDFDownloadLink
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ blob, url, loading, error }) =>
<>
{console.log("error",error)}
{loading ? (
"Loading document..."
) : (
<div
style={{
display: "flex",
flexDirection: "row",
}}
>
<i
class="fa fa-certificate"
style={{
fontSize: "15px",
marginRight: "3px",
}}
aria-hidden="true"
></i>
Certificate
</div>
)}
</>
}
</PDFDownloadLink>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
) : (
<div style={{ display: "flex", justifyContent: "space-around" }}>
{/* current signer is checking user send request and check status of pdf sign than if current
user exist than show finish button else no
*/}
{currentSigner && (
<>
{decline && (
<div
onClick={() => {
handleDeclinePdfAlert();
}}
style={{
color: "red",
border: "none",
fontWeight: "650",
fontSize: "16px",
marginRight: "10px",
}}
>
Decline
</div>
)}
{isPlaceholder ?(
<div
onClick={() => {
alertSendEmail();
}}
style={{
color: themeColor(),
border: "none",
fontWeight: "650",
fontSize: "16px",
}}
>
Send
</div>
):(
<div
onClick={() => {
if (!pdfUrl) {
embedImages();
}
}}
style={{
color: themeColor(),
border: "none",
fontWeight: "650",
fontSize: "16px",
}}
>
Finish
</div>
)}
</>
)}
</div>
)}
</div>
</div>
)}
</div>
);
}
export default Header;
@@ -0,0 +1,28 @@
import React from 'react'
import loader from "../../assests/loader2.gif";
function Loader({isLoading}) {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
flexDirection: "column",
}}
>
<img
alt="no img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
)
}
export default Loader
@@ -0,0 +1,28 @@
import React from "react";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
function ModalComponent({ isShow, type }) {
return (
<Modal show={isShow}>
<ModalHeader className="bg-danger" style={{ color: "white" }}>
{type === "pdfFail" ? (
<span>Failed to load pdf!</span>
) : (
<span>Document Expired!</span>
)}
</ModalHeader>
<Modal.Body>
{type === "pdfFail" ? (
<p>Something went wrong failed to load pdf file!</p>
) : (
<p>This Document is no longer available.</p>
)}
</Modal.Body>
</Modal>
);
}
export default ModalComponent;
@@ -0,0 +1,58 @@
import React from "react";
function PrevNext({ pageNumber, allPages, changePage }) {
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
return (
<div>
<div className="preBtn1">
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
border: "0px",
marginRight: "5px",
}}
disabled={pageNumber <= 1}
onClick={previousPage}
>
Prev
</button>
<span
style={{
fontSize: "13px",
fontWeight: "600",
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
marginLeft: "5px",
border: "0px",
}}
disabled={pageNumber >= allPages}
onClick={nextPage}
>
Next
</button>
</div>
</div>
);
}
export default PrevNext;
@@ -0,0 +1,96 @@
import React from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page, pdfjs } from "react-pdf";
import { themeColor } from "../../utils/ThemeColor/backColor";
function RenderAllPdfPage({
pdfUrl,
signPdfUrl,
allPages,
setAllPages,
setPageNumber,
setSignBtnPosition,
}) {
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
//set all number of pages after load pdf
function onDocumentLoad({ numPages }) {
setAllPages(numPages);
}
return (
<div className="showPages">
<div
style={{
display: "flex",
flexDirection: "row",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
backgroundColor: "white",
height:"100%"
}}
>
<div style={{ width: "140px" }}>
<div
style={{
background: themeColor(),
// color:"white"
}}
className="signedStyle"
>
Pages
</div>
<div>
<RSC id="RSC-Example" style={{width:"135px",height:window.innerHeight-130+'px'}} >
<Document
onLoadSuccess={onDocumentLoad}
file={pdfUrl ? pdfUrl : signPdfUrl}
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
style={{
width: "100px",
border: "2px solid #878787",
// padding: "5px",
margin: "10px",
display: "flex",
justifyContent: "center",
alignItems: "center",
contain: "content",
}}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
}}
>
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={100}
height={100}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
))}
</Document>
</RSC>
</div>
<hr />
</div>
</div>
</div>
);
}
export default RenderAllPdfPage;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,476 @@
import React from "react";
import { useRef, useState } from "react";
import SignatureCanvas from "react-signature-canvas";
import redPen from "../../assests/redPen.png";
import bluePen from "../../assests/bluePen.png";
import blackPen from "../../assests/blackPen.png";
import Modal from "react-bootstrap/Modal";
import { themeColor } from "../../utils/ThemeColor/backColor";
import { useEffect } from "react";
function SignPad({
isSignPad,
isStamp,
setIsImageSelect,
onSaveSign,
setIsSignPad,
setImage,
isImageSelect,
imageRef,
onImageChange,
onSaveImage,
image,
defaultSign,
setSignature,
}) {
const [penColor, setPenColor] = useState("blue");
const allColor = [bluePen, redPen, blackPen];
const canvasRef = useRef(null);
const [isDefaultSign, setIsDefaultSign] = useState(false);
const [isTab, setIsTab] = useState("signature");
const [isSignImg, setIsSignImg] = useState("");
//function for clear signature
const handleClear = () => {
if (canvasRef.current) {
canvasRef.current.clear();
}
setIsSignImg("");
};
//function for set signature url
const handleSignatureChange = (dataURL) => {
// canvasRef.current.backgroundColor = 'rgb(165, 26, 26)'
setSignature(canvasRef.current.toDataURL());
setIsSignImg(canvasRef.current.toDataURL());
};
//save button component
const SaveBtn = () => {
// console.log("isSign",isSignImg,image)
return (
<div>
{!isStamp && !isImageSelect && (
<button
style={{
color: "black",
}}
type="button"
className="finishBtn saveBtn"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
if (!image) {
if (isDefaultSign) {
setIsSignImg("");
onSaveSign(isDefaultSign);
} else {
setIsSignImg("");
onSaveSign();
}
setPenColor("blue");
} else {
setIsSignImg("");
onSaveImage();
}
setIsSignPad(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("signature");
}}
style={{
background: themeColor(),
color: "white",
}}
disabled={isSignImg || image || isDefaultSign ? false : true}
type="button"
className={
isSignImg || image ? "finishBtn saveBtn" : "disabledFinish saveBtn"
}
>
Save
</button>
</div>
);
};
useEffect(() => {
// Load the default signature after the component mounts
if (canvasRef.current) {
canvasRef.current.fromDataURL(isSignImg);
}
}, [isTab]);
return (
<div>
{/*isSignPad */}
<Modal show={isSignPad}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
background: "white",
borderBottom: "1.6px solid #ebe6e6",
marginTop: "15px",
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "end",
gap: 10,
// background: themeColor(),
}}
>
{isStamp ? (
<span style={{ color: themeColor() }} className="signTab">
Upload Image
</span>
) : (
<>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("signature");
setImage();
}}
style={{
color: isTab === "signature" ? themeColor() : "#515252",
marginLeft: "2px",
}}
className="signTab"
>
Signature
</span>
<div
style={{
border:
isTab === "signature"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
}}
></div>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(true);
setIsTab("uploadImage");
}}
style={{
color: isTab === "uploadImage" ? themeColor() : "#515252",
}}
className="signTab"
>
Upload Image
</span>
<div
style={{
border:
isTab === "uploadImage"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
}}
></div>
</div>
{defaultSign && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setImage();
}}
style={{
color:
isTab === "mysignature" ? themeColor() : "#515252",
}}
className="signTab"
>
My Signature
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
}}
></div>
</div>
)}
</>
)}
</div>
<button
style={{
border: "0px",
background: "none",
paddingLeft: "7px",
paddingRight: "7px",
marginRight: "5px",
}}
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("signature");
}}
>
X
</button>
</div>
{/* signature modal */}
<Modal.Body className="modalBody">
{isDefaultSign ? (
<>
<div
style={{
border: "1px solid black",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer",
// background:'rgb(255, 255, 255)'
}}
className="signatureCanvas"
>
<img
alt="stamp img"
style={{
width: "100%",
height: "100%",
background: "rgb(255, 255, 255)",
objectFit: "contain",
}}
src={defaultSign}
/>
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div
style={{
border: "1px solid black",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer",
}}
className="signatureCanvas"
onClick={() => imageRef.current.click()}
>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/*"
ref={imageRef}
hidden
// style={{ display: "none" }}
/>
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
<div className="uploadImg">Upload</div>
</div>
) : (
<>
<div
style={{
// position: "relative",
border: "1px solid black",
marginBottom: 6,
// justifyContent:"center"
overflow: "hidden",
}}
className="signatureCanvas"
>
<img
alt="print img"
ref={imageRef}
// alt="preview image"
src={image.src}
style={{
//overflow:"hidden",
objectFit: "contain",
}}
/>
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<SaveBtn />
</div>
</>
)
) : (
<>
{/* {isSignImg ? (
<div
style={{
position: "relative",
border: "2px solid #888",
marginBottom: 6,
}}
className="signatureCanvas"
>
<img
alt="preview image"
src={isSignImg}
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</div>
) : ( */}
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
// width: "460px",
// height: "184px",
// width:"280px",
// height:"112px",
className: "signatureCanvas",
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL())
}
dotSize={1}
/>
{/* )} */}
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop:"4px"
}}
>
<div style={{display:"flex",flexDirection:"row"}}>
{allColor.map((data, key) => {
return (
<img
key={key}
alt="pen img"
style={{
border: "none",
margin: "5px",
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white",
}}
onClick={() => {
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
src={data}
width={20}
height={20}
/>
// </button>
);
})}
</div>
<SaveBtn />
</div>
</>
)}
</Modal.Body>
{/* <Modal.Footer>
{!isStamp && !isImageSelect && (
<button
style={{
color: "black",
}}
type="button"
className="finishBtn"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
if (!image) {
if (isDefaultSign) {
onSaveSign(isDefaultSign);
} else {
onSaveSign();
}
setPenColor("blue");
} else {
onSaveImage();
}
setIsSignPad(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
}}
style={{
background: themeColor(),
color: "white",
}}
type="button"
className="finishBtn"
>
Save
</button>
</Modal.Footer> */}
</Modal>
</div>
);
}
export default SignPad;
@@ -0,0 +1,68 @@
import React from "react";
import { themeColor } from "../../utils/ThemeColor/backColor";
import "../../css/signature.css";
function Signedby({ pdfDetails }) {
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
};
return (
<div className="signerComponent">
<div
style={{
background: themeColor(),
// color:"white"
}}
className="signedStyle"
>
Signed By
</div>
<div style={{ marginTop: "2px" }}>
<div
style={{
display: "flex",
flexDirection: "row",
padding: "10px",
background: "#93a3db",
}}
>
<div
className="signerStyle"
style={{
background: "#abd1d0",
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px",
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold",
}}
>
{" "}
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
</span>
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
</div>
</div>
</div>
</div>
);
}
export default Signedby;
@@ -0,0 +1,163 @@
import React, { useState } from "react";
import check from "../../assests/checkBox.png";
import { themeColor } from "../../utils/ThemeColor/backColor";
function SignerListPlace({
signerPos,
signersdata,
isSelectListId,
setSignerObjId,
setIsSelectId,
setContractName
}) {
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc",
];
const nameColor = [
"#304fbf",
"#7d5270",
"#5f825b",
"#578077",
"#576e80",
"#6d527d",
"#cc00cc",
"#006666",
"#cc00ff",
"#ff9900",
"#336699",
"#cc9900",
];
const [isHover, setIsHover] = useState();
//function for onhover signer name change background color
const onHoverStyle = (ind) => {
const style = {
background: color[ind % color.length],
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
};
return style;
};
//function for onhover signer name remove background color
const nonHoverStyle = (ind) => {
const style = {
// width:"250px",
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
};
return style;
};
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
};
return (
<div >
<div
style={{
background: themeColor(),
padding: "5px",
}}
>
<span className="signedStyle">Reicipents</span>
</div>
<div className="signerList">
{signersdata.Signers &&
signersdata.Signers.map((obj, ind) => {
return (
<div
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
key={ind}
style={
isHover === ind || isSelectListId === ind
? onHoverStyle(ind)
: nonHoverStyle(ind)
}
onClick={() => {
setSignerObjId(obj.objectId);
setIsSelectId(ind);
setContractName(obj.className)
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
}}
>
<div
className="signerStyle"
style={{
background: nameColor[ind % nameColor.length],
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px",
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold",
}}
>
{" "}
{getFirstLetter(obj.Name)}
</span>
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<span className="userName">{obj.Name}</span>
<span className="useEmail">{obj.Email}</span>
</div>
</div>
{signerPos.map((data, key) => {
return (
data.signerObjId === obj.objectId && (
<div key={key}>
<img alt="no img" src={check} width={20} height={20} />
</div>
)
);
})}
<hr />
</div>
);
})}
</div>
</div>
);
}
export default SignerListPlace;
@@ -0,0 +1,310 @@
import React, { useState } from "react";
import signLogo from "../assests/mailLogo.png";
import "../css/LoginPage.css";
import loader from "../assests/loader2.gif";
import axios from "axios";
import { useParams } from "react-router-dom";
import { themeColor } from "../utils/ThemeColor/backColor";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
// import { useHistory } from "react-router-dom/cjs/react-router-dom.min";
function Login() {
const { id, userMail, serverUrl,serverId } = useParams();
let navigate = useNavigate();
// const history = useHistory();
const [email, setEmail] = useState(userMail);
const [OTP, setOTP] = useState("");
const [EnterOTP, setEnterOtp] = useState(false);
const [loading, setLoading] = useState(false);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
handleServerUrl();
}, []);
//https://qik-ai-org.github.io/Sign-MicroappV2/#/login/LqgGeZHd17/raktima.c@qik.ai/https:%2F%2Fserver3.qik.ai%2Fdefaultssty%2F&defaultssty&contracts
// login/LqgGeZHd17/raktima.c@qik.ai/https:%2F%2Fserver3.qik.ai%2Fdefaultssty&defaultssty&contracts
//function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = () => {
// console.log("params", serverUrl);
//split url in array from '&'
localStorage.clear();
const checkSplit = serverUrl.split("&");
const server = checkSplit[0];
const parseId = checkSplit[1];
const appName = checkSplit[2];
// console.log("appName", appName);
const newServer = server.replaceAll("%2F", "/");
localStorage.setItem("baseUrl", newServer);
localStorage.setItem("parseAppId", parseId);
localStorage.setItem("_appName", appName);
setIsLoading(false);
// console.log("checkSPlit", newServer, parseId, appName);
};
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");
// console.log("server",serverUrl)
if (serverUrl && localStorage) {
setLoading(true);
e.preventDefault();
setEmail(email);
try {
// const { baseUrl, parseAppId, mobile } = this.state;
let url = `${serverUrl}functions/SendOTPMailV1/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId,
};
let body = {
email: email.toString(),
// appId: {
// __type: "Pointer",
// className: "w_appinfo",
// objectId: localStorage.getItem("_app_objectId"),
// },
};
let Otp = await axios.post(url, body, { headers: headers });
if (Otp) {
setLoading(false);
setEnterOtp(true);
}
} catch (error) {
// this.setState({ loading: false });
}
} else {
alert("something went wrong!");
}
};
//verify OTP send on via email
const VerifyOTP = async (e) => {
e.preventDefault();
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (OTP) {
setLoading(true);
try {
// AuthLoginAs
let url = `${serverUrl}functions/AuthLoginAsMail/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId,
};
let body = {
email: email,
// email,
otp: OTP,
};
let user = await axios.post(url, body, { headers: headers });
// console.log("user", user.data.result);
if (user.data.result === "Invalid Otp") {
alert("Invalid Otp");
setLoading(false);
}else if(user.data.result === "user not found!"){
alert("User not found!");
setLoading(false);
}
else {
let _user = user.data.result;
localStorage.setItem("UserInformation", JSON.stringify(_user));
// localStorage.setItem("userEmail", email);
localStorage.setItem("username", _user.name);
// console.log("session token", _user.sessionToken);
localStorage.setItem("accesstoken", _user.sessionToken);
setLoading(false);
//navigate user to on signature page
// history.push(`/recipientSignPdf/${id}/${_user.phone}`);
navigate(`/recipientSignPdf/${id}/${_user.phone}`);
}
} catch (error) {}
} else {
alert("Please Enter OTP!");
}
};
return (
<React.Fragment>
{isLoading ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
flexDirection: "column",
}}
>
<img
alt="no img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
</div>
) : (
<div
style={{
margin: "10px",
border: "0.5px solid #c5c7c9",
padding: "30px",
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px",
// box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
}}
>
<div className="main_head">
<div className="main-logo">
<img alt="sign img" src="https://qikinnovation.ams3.digitaloceanspaces.com/logo.png" width="100%" />
</div>
</div>
{!EnterOTP ? (
<form>
<div className="row">
<div className="col-sm-6 KLO">
<h2>Welcome Back !</h2>
<span className="KNLO">
Verification code is sent to your email
</span>
<div className="card card-box">
<div className="card-body">
<input
type="email"
className="form-control"
name="mobile"
value={email}
disabled
height={30}
style={{ fontSize: "20px", fontWeight: "600" }}
/>
<br />
</div>
</div>
<div
className="col-md-4 col-sm-4"
style={{
textAlign: "center",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{loading ? (
<button
className="btn btn-info loadinBtn "
type="button"
style={{ marginBottom: "4px", width: "210px" }}
disabled
>
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
</button>
) : (
<button
className="btn btn-sm otpButton"
onClick={(e) => SendOtp(e)}
style={{
marginBottom: "4px",
width: "210px",
background: themeColor(),
color: "white",
fontWeight: "600",
}}
>
Send OTP
</button>
)}
</div>
</div>
</div>
</form>
) : (
<form>
<div className="row">
<div className="col-sm-6 KLO">
<h2>Welcome Back !</h2>
<span className="KNLO">You will get a OTP via Email</span>
<div className="card card-box">
<div className="card-body">
<label>Enter Verification Code</label>
<input
type="number"
className="form-control"
name="OTP"
value={OTP}
min="4"
max="4"
onChange={handleChange}
/>
<br />
</div>
</div>
<div
className="col-md-4 col-sm-4"
style={{ textAlign: "center" }}
>
{loading ? (
<button
className="btn btn-info "
type="button"
style={{ marginBottom: "4px", width: "210px" }}
disabled
>
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
</button>
) : (
<button
type="button"
onClick={(e) => VerifyOTP(e)}
className="btn btn-info login-btn"
>
Verify
</button>
)}
</div>
</div>
</div>
</form>
)}
</div>
)}
</React.Fragment>
);
}
export default Login;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
import React from "react";
import { Routes, Route } from "react-router-dom";
import SignYourselfPdf from "./Component/SignYourselfPdf";
import RecipientSignPdf from "./Component/recipientSignPdf";
import PlaceHolderSign from "./Component/placeHolderSign";
import ManageSign from "./Component/ManageSign";
import Login from "./Component/login";
import DraftDocument from "./Component/DraftDocument";
import PdfRequestFiles from "./Component/PdfRequestFiles";
import LegaDrive from "./Component/LegaDrive/LegaDrive";
// `AppRoutes` is used to define route path of app and
// it expose to host app, check moduleFederation.config.js for more
function AppRoutes() {
return (
<div>
<Routes>
{/* TODO: Check with images and other assets */}
<Route index element={<div>Microapp Home page route </div>} />
{/* signyouself route with rowlevel data */}
<Route path="/signaturePdf" element={<SignYourselfPdf />} />
{/* signyouself route with no rowlevel data using docId from url */}
<Route path="/signaturePdf/:docId" element={<SignYourselfPdf />} />
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:id/:userPhone"
element={<RecipientSignPdf />}
/>
{/* recipient placeholder set route with rowlevel data */}
<Route path="/placeHolderSign" element={<PlaceHolderSign />} />{" "}
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
<Route path="/placeHolderSign/:docId" element={<PlaceHolderSign />} />
{/*Add default signature of user route */}
<Route path="/managesign" element={<ManageSign />} />
{/* login page route */}
<Route path="/login/:id/:userMail/: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 */}
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
{/* for user signature (need your sign route) with no row level data */}
<Route path="/pdfRequestFiles/:docId" element={<PdfRequestFiles />} />
{/* lega drive route */}
<Route path="/legadrive" element={<LegaDrive />} />
</Routes>
</div>
);
}
export default AppRoutes;
Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

+7
View File
@@ -0,0 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
@@ -0,0 +1,151 @@
.react-tel-input .form-control {
width: 100%;
height: 45px;
margin-top: 4px;
}
.fakeimg {
height: 200px;
background: #aaa;
}
.GTRY {
border: 1px solid #dee2e6;
margin-top: 75px;
padding-bottom: 20px;
background-color: #fff;
}
.main_head {
width: 100%;
height: 66px;
background-color: #fff;
margin-bottom: 52px;
padding-top: 10px;
}
.main-logo {
width: 250px;
height: 66px;
display: inline-block;
}
.card-box {
background: #fff;
min-height: 50px;
box-shadow: 0 20px 20px rgba(0, 0, 0, 0.1);
position: relative;
margin-bottom: 20px;
transition: 0.5s;
border: 1px solid #f2f2f2;
border-radius: 7px;
margin-top: 10px;
}
.LKJH {
display: flex;
}
.GTR {
text-align: left;
}
.JUI {
text-align: right;
}
.btn-info {
color: #fff;
background-color: #15b4e9;
border-color: #15b4e9;
}
.btn-info:hover {
color: #fff;
background-color: #15b4e9;
border-color: #15b4e9;
}
.btn-reg {
color: #15b4e9;
background-color: #ffffff;
border-color: #15b4e9;
}
.btn-reg:hover {
color: #15b4e9;
background-color: #ffffff;
border-color: #15b4e9;
}
.SYTU {
display: flex;
}
.MLKI {
float: right;
margin-bottom: 12px;
}
.login-btn {
width: 100% !important;
display: block !important;
}
@media only screen and (max-width: 667px) {
.MLKI {
float: inherit;
}
.login-btn {
width: 210px !important;
}
}
.MLKI a {
color: #212529;
}
.KNLO {
color: #878787;
}
.form-check-label {
margin-left: 17px;
margin-bottom: 12px;
}
input:focus {
border-color: #ccc !important;
}
@media only screen and (max-width: 600px) {
.KLO1 {
display: none;
}
.MLKI a {
font-size: 12px;
}
.form-check-label {
font-size: 12px;
margin-bottom: 14px;
}
.MKUY {
text-align: center;
padding-top: 10px;
}
.main_head {
margin-bottom: 12px;
padding-top: 10px;
border-bottom: 1px dashed #eeeeee;
}
}
@@ -0,0 +1,225 @@
.mainDiv {
position: relative;
}
.successBtn {
background: #3ac9d6;
border: 1px solid #3ac9d6;
}
.warning {
position: absolute;
padding: 8px;
border: 1px solid black;
background: white;
width: 300px;
border-radius: 5px;
animation: inAnimation 2s ease-in-out;
z-index: 1;
}
.nameWarning {
position: absolute;
bottom: -70px;
left: 10px;
}
.nameWarning::before {
content: '';
width: 0px;
height: 0px;
border-style: outset;
border-width: 0px 10px 10px 10px;
border-color: transparent transparent black transparent;
display: inline-block;
position: absolute;
top: -29%;
left: 0;
right: 75%;
margin: 0 auto;
}
.signWarning {
bottom: 70px;
left: 50px;
}
.signWarning::before {
content: '';
width: 0px;
height: 0px;
border-style: outset;
border-width: 0px 10px 10px 10px;
border-color: transparent transparent black transparent;
display: inline-block;
position: absolute;
top: -29%;
left: 0;
right: 75%;
margin: 0 auto;
}
.successBox {
z-index: 1;
position: absolute;
top: 5%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 13px;
animation: inAnimation 2s ease-in-out;
}
@keyframes inAnimation {
0% {
opacity: 0;
visibility: hidden;
}
25% {
opacity: 1;
visibility: visible;
}
50% {
opacity: 1;
visibility: visible;
}
75% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
.signature {
color: white;
cursor: pointer;
padding: 10px 14px 10px;
background: #15b4e9;
font-size: 12px;
font-weight: 600;
border-radius: 2px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
}
.signature:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
background: #13a8da;
color: white;
}
.imgupload {
color: white;
cursor: pointer;
padding: 10px 14px 10px;
background: #15b4e9;
font-size: 12px;
font-weight: 600;
border-radius: 2px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
min-width: 105px;
}
.imgupload:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
background: #13a8da;
color: white;
}
.loaderContainer {
position: fixed;
inset: 0%;
width: 100%;
height: 100svh;
background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7));
display: flex;
align-items: center;
justify-content: center;
}
.loader {
width: 70px;
height: 70px;
border-top: 4px solid #1b8ef4;
border-left: 4px solid #1b8ef4;
border-radius: 80%;
animation: spin 0.5s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
.customBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 10px 14px 10px;
color: white;
font-weight: 600;
font-size: 12px;
text-transform: uppercase;
border-radius: 2px;
margin-left: 10px;
width: 100px;
}
.clearbtn {
font-size: 15px;
}
.clearbtn:hover {
text-decoration: underline;
text-underline-offset: 2px;
color: blue;
}
.successBtn:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
background: #1ab6ce;
color: white;
}
.resetBtn {
background: #188ae2;
margin-left: 10;
border: 1px solid #188ae2;
}
.resetBtn:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
background: #177ecd;
color: white;
}
.signBlock {
display: flex;
flex-direction: row;
gap: 12px;
}
.signInputManage{
width: 50%;
}
@media (max-width: 815px) {
.signBlock {
display: flex;
flex-direction: column;
}
.signInputManage{
width: 100%;
}
}
@@ -0,0 +1,723 @@
/* html, body {margin: 0; height: 100%; overflow: hidden} */
/* #04b565 */
/* div {
margin: 0px;
font-family:system-ui;
} */
.signatureCanvas {
border: 2px solid #888;
background-color: rgb(255, 255, 255);
width: 460px;
height: 184px;
}
.penContainer{
width: 460px;
}
.signatureBtn {
border: 1.5px solid #47a3ad;
margin-bottom: 10px;
border-radius: 3px;
display: flex;
padding: 0px;
justify-content: space-between;
background: white;
transition-duration: 0.4s;
cursor: all-scroll;
width: 150px;
height: 30px;
color: black;
/* transform: translate(); */
}
/* .signatureBtn:hover {
background-color:#47a3ad;
color: white;
} */
.disableSign {
border: none;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
margin-bottom: 10px;
border-radius: 3px;
display: flex;
padding: 0px;
justify-content: space-between;
background: #d3edeb;
transition-duration: 0.4s;
cursor: pointer;
color: white;
font-weight: 600;
}
.emailInput {
padding: 10px;
border: 1.5px solid gray;
border-radius: 4px;
width: 100%;
font-size: 15px;
padding-bottom: 20px;
}
.emailInput:focus {
outline: none;
border: 1.5px solid #47a3ad;
}
.addEmail {
padding: "0px";
border: 1.5px solid #47a3ad;
border-radius: 4px;
width: 100%;
font-size: 15px;
}
.addEmail,
.emailInput {
outline: none;
}
.addEmailInput{
border: none;
outline: none;
}
.emailChip {
background-color: #47a3ad;
margin: 4px;
border-radius: 10px;
padding: 5px 10px;
}
.addEmailInput {
padding: 10px;
border-radius: 4px;
width: 100%;
font-size: 15px;
}
.addEmailInput:focus {
outline: none;
border: none;
}
.finishBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 30px;
color: white;
font-weight: 500 !important;
font-size: 14px !important;
border: none;
margin-left: 10px;
}
.finishBtn:focus {
border: none;
outline: none;
}
.finishBtn:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
color: white;
}
.saveBtn {
padding: 3px 18px !important;
}
.disabledFinish {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 30px;
color: white;
font-weight: 600;
font-size: 14px;
border: none;
margin-left: 10px;
}
.sendHover:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
color: white;
}
.defaultSignBox {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
width: 180px;
height: 111px;
padding: 8px 20px;
color: white;
font-weight: 600;
font-size: 14px;
border: none;
border-radius: 2px;
margin-left: 10px;
}
.defaultBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 26px;
color: black;
font-weight: 500;
font-size: 14px;
border: 1px solid gray;
border-radius: 2px;
margin-left: 10px;
}
.backBtn {
background-color: white;
}
.backBtn:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.defaultBtn:focus {
border: none;
outline: none;
}
.downloadBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background-color: rgb(241 79 79);
border: none !important;
color: white !important;
}
.downloadBtn:hover {
box-shadow: 0 2px 4px rgba(154, 36, 36, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.printBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background: #188ae2;
border: none !important;
color: white !important;
}
.printBtn:hover {
box-shadow: 0 2px 4px rgba(23, 48, 137, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.certificateBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background: #08bc66;
border: none !important;
color: white !important;
}
.certificateBtn:hover {
box-shadow: 0 2px 4px rgba(15, 150, 87, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.signerList {
overflow-y: scroll;
max-height: 180px;
}
.signerList::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */
}
.showScroll {
height: 100%;
overflow: scroll;
}
.showScroll::-webkit-scrollbar {
display: none;
/* for Chrome, Safari, and Opera */
}
.hideScroll {
height: 100%;
overflow: scroll;
}
.signedStyle {
color: white;
padding: 5px;
font-family: sans-serif;
font-size: 15px !important;
}
.userName {
font-size: 12px !important;
font-weight: bold !important;
color: #424242 !important;
width: 100px;
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
}
.useEmail {
font-size: 10px;
color: #424242;
font-weight: 500;
width: 100px;
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
}
.spanTagHead {
color: white !important;
font-size: 17px;
font-weight: 500;
}
.pTagBody {
font-size: 15px !important;
}
.signTab {
cursor: pointer !important;
font-weight: 400 !important;
margin-left: 10px !important;
font-size: 15px !important;
}
.draggable-button {
padding: 10px 20px;
background: #3498db;
color: #fff;
border: none;
cursor: grab;
transition: transform 0.2s;
}
.draggable-button.dragging {
opacity: 0.5;
/* Reduce opacity while dragging */
transform: scale(1.1);
/* Apply a scaling effect while dragging */
}
.signatureContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #ebebeb;
/* max-height: 800px; */
/* overflow: auto; */
}
.signatureHeader {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-top: 5px;
}
.signerComponent {
/* box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px; */
width: 180px;
background-color: white;
height: 100%;
}
.modalBody {
padding: 1rem !important;
padding-top: 2rem !important;
}
.signLayoutContainer1 {
padding: 30px;
display: flex;
flex-direction: column;
align-items: center
}
.signLayoutContainer2 {
display: none;
}
.preBtn1 {
display: flex;
align-items: center;
}
.preBtn2 {
display: none;
}
.headerBtn2 {
display: none;
}
.uploadImgLogo {
font-size: 50px;
}
.DropdownMenuContent,
.DropdownMenuSubContent {
min-width: 130px;
background-color: #887abf;
border: none;
padding: 5px;
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
z-index: 30 !important;
}
.DropdownMenuContent[data-side='top'],
.DropdownMenuSubContent[data-side='top'] {
animation-name: slideDownAndFade;
}
.DropdownMenuContent[data-side='right'],
.DropdownMenuSubContent[data-side='right'] {
animation-name: slideLeftAndFade;
}
.DropdownMenuContent[data-side='bottom'],
.DropdownMenuSubContent[data-side='bottom'] {
animation-name: slideUpAndFade;
}
.DropdownMenuContent[data-side='left'],
.DropdownMenuSubContent[data-side='left'] {
animation-name: slideRightAndFade;
}
.DropdownMenuItem {
font-size: 13px;
line-height: 1;
color: black;
background-color: #887abf;
display: flex;
align-items: center;
height: 25px;
padding: 0 5px;
position: relative;
padding-left: 25px;
user-select: none;
outline: none;
border: none;
}
.SelectTrigger {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
padding: 0 15px;
font-size: 13px;
line-height: 1;
height: 35px;
gap: 5px;
border: none;
background-color: #887abf;
color: black
}
option{
overflow-y:scroll;
}
.SelectTrigger:hover {
border: none;
outline: none;
}
.SelectTrigger:focus {
/* box-shadow: 0 0 0 2px black; */
border: none;
outline: none;
}
.SelectTrigger[data-placeholder] {
color: black;
}
.SelectIcon {
color: Var(--violet-11);
}
.SelectContent {
overflow: hidden;
background-color: white;
border-radius: 6px;
box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2);
}
.SelectViewport {
padding: 5px;
}
.SelectItem {
font-size: 13px;
line-height: 1;
color: var(--violet-11);
border-radius: 3px;
display: flex;
align-items: center;
height: 25px;
padding: 0 35px 0 25px;
position: relative;
user-select: none;
z-index: 10 !important;
}
.SelectItem[data-disabled] {
color: var(--mauve-8);
pointer-events: none;
}
.SelectItem[data-highlighted] {
outline: none;
background-color: var(--violet-9);
color: var(--violet-1);
}
.SelectLabel {
padding: 0 25px;
font-size: 12px;
line-height: 25px;
color: var(--mauve-11);
}
.SelectSeparator {
height: 1px;
background-color: var(--violet-6);
margin: 5px;
}
.SelectItemIndicator {
position: absolute;
left: 0;
width: 25px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.SelectScrollButton {
display: flex;
align-items: center;
justify-content: center;
height: 25px;
background-color: white;
color: var(--violet-11);
cursor: default;
}
@media screen and (max-width:767px) {
.showPages {
display: none;
}
.signatureContainer {
display: flex;
flex-direction: column;
}
.signerComponent {
display: none;
}
.preBtn1 {
display: none;
}
.preBtn2 {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #ebebeb;
padding: 5px 10px;
}
#navbar {
overflow: hidden;
z-index: 10;
}
/* Navbar links */
#navbar a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px;
text-decoration: none;
}
#navbar a.active {
background-color: #4caf50;
color: white;
}
/* Page content */
.content {
padding: 16px;
}
/* The sticky class is added to the navbar
with JS when it reaches its scroll position */
.stickyHead {
position: fixed;
top: 50px;
left: 0.9rem;
}
.stickyfooter {
position: fixed;
bottom: 0px;
right: 0rem;
}
/* Add some top padding to the page content
to prevent sudden quick movement (as the
navigation bar gets a new position at the top of the
page (position:fixed and top:0) */
.stickyHead+.content {
padding-top: 60px;
}
.signLayoutContainer2 {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-top: 5px;
padding: 2px 5px 10px 5px;
position: sticky;
align-items: center;
}
.headerBtn {
display: none;
}
.headerBtn2 {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-top: 5px;
}
.mobileHead {
display: flex;
}
}
@media screen and (max-width:487px) and (min-width:351px) {
.signatureCanvas {
width: 300px;
height: 120px;
}
.penContainer{
width: 300px;
}
.uploadImgLogo {
font-size: 30px;
}
.hidePdf {
display: none;
}
.mobileHead {
display: flex;
}
}
@media screen and (max-width:350px) and (min-width:311px) {
.signatureCanvas {
width: 280px;
height: 112px;
}
.penContainer{
width: 280px;
}
.hidePdf {
display: none;
}
.signTab {
font-weight: 500 !important;
font-size: 10px !important;
}
.uploadImgLogo {
font-size: 25px;
}
.uploadImg {
font-size: 10px !important;
}
.mobileHead {
display: flex;
}
}
@media screen and (max-width:310px) {
.signatureCanvas {
width: 230px;
height: 92px;
}
.penContainer{
width: 230px;
}
.hidePdf {
display: none;
}
.signTab {
font-weight: 500 !important;
font-size: 10px !important;
}
.uploadImgLogo {
font-size: 20px;
}
.uploadImg {
font-size: 10px !important;
}
.mobileHead {
display: flex;
}
}
@@ -0,0 +1 @@
import("./bootstrap")
@@ -0,0 +1,7 @@
export const themeColor =()=>{
return "#47a3ad";
}
export const iconColor =()=>{
return "#686968"
}
@@ -0,0 +1,280 @@
import axios from "axios";
export async function getBase64FromUrl(url) {
const data = await fetch(url);
const blob = await data.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
const pdfBase = this.result;
const removeBase64Prefix = "data:application/octet-stream;base64,";
const suffixbase64 = pdfBase.replace(removeBase64Prefix, "");
resolve(suffixbase64);
};
});
}
export async function getBase64FromIMG(url) {
const data = await fetch(url);
const blob = await data.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
const pdfBase = this.result;
resolve(pdfBase);
};
});
}
export function getHostUrl() {
const hostUrl = window.location.href;
/// const hostUrl = "https://contracts-defaultssty.qik.ai/#/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive";
// const hostUrl = "https://contracts-defaultssty.qik.ai/#/"
// const hostUrl = "https://lionfish-app-75ly7.ondigitalocean.app/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive"
//const hostUrl = "https://app.opensignlabs.com/rpmf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument";
if (hostUrl) {
const urlSplit = hostUrl.split("/");
const concatUrl = urlSplit[3] + "/" + urlSplit[4];
if (urlSplit) {
const desireUrl = "/" + concatUrl;
if (desireUrl) {
return desireUrl + "/";
}
}
} else {
return "/";
}
}
//function for upload stamp or image
export function onSaveImage(xyPostion, index, signKey, imgWH, image) {
const updateFilter = xyPostion[index].pos.filter(
(data, ind) =>
data.key === signKey && data.Width && data.Height && data.SignUrl
);
if (updateFilter.length > 0) {
let newWidth, nweHeight;
const aspectRatio = imgWH.width / imgWH.height;
const getXYdata = xyPostion[index].pos;
if (aspectRatio === 1) {
newWidth = aspectRatio * 100;
nweHeight = aspectRatio * 100;
} else if (aspectRatio < 2) {
newWidth = aspectRatio * 100;
nweHeight = 100;
} else if (aspectRatio > 2 && aspectRatio < 4) {
newWidth = aspectRatio * 70;
nweHeight = 70;
} else if (aspectRatio > 4) {
newWidth = aspectRatio * 40;
nweHeight = 40;
} else if (aspectRatio > 5) {
newWidth = aspectRatio * 10;
nweHeight = 10;
}
const getPosData = getXYdata;
const addSign = getPosData.map((url, ind) => {
if (url.key === signKey) {
return {
...url,
Width: newWidth,
Height: nweHeight,
SignUrl: image.src,
ImageType: image.imgType,
};
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
return newUpdateUrl;
// setXyPostion(newUpdateUrl);
} else {
const getXYdata = xyPostion[index].pos;
const getPosData = getXYdata;
const aspectRatio = imgWH.width / imgWH.height;
let newWidth, newHeight;
if (aspectRatio === 1) {
newWidth = aspectRatio * 100;
newHeight = aspectRatio * 100;
} else if (aspectRatio < 2) {
newWidth = aspectRatio * 100;
newHeight = 100;
} else if (aspectRatio > 2 && aspectRatio < 4) {
newWidth = aspectRatio * 70;
newHeight = 70;
} else if (aspectRatio > 4) {
newWidth = aspectRatio * 40;
newHeight = 40;
} else if (aspectRatio > 5) {
newWidth = aspectRatio * 10;
newHeight = 10;
}
const addSign = getPosData.map((url, ind) => {
if (url.key === signKey) {
return {
...url,
Width: newWidth,
Height: newHeight,
SignUrl: image.src,
ImageType: image.imgType,
};
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
return newUpdateUrl;
// setXyPostion(newUpdateUrl);
}
}
//function for save button to save signature or image url
export function onSaveSign(xyPostion, index, signKey, signatureImg) {
const updateFilter = xyPostion[index].pos.filter(
(data) => data.key === signKey && data.SignUrl
);
if (updateFilter.length > 0) {
const getXYdata = xyPostion[index].pos;
// updateFilter[0].SignUrl = signatureImg;
const getPosData = getXYdata;
const addSign = getPosData.map((url, ind) => {
if (url.key === signKey) {
return {
...url,
Width: 150,
Height: 60,
SignUrl: signatureImg,
};
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
return newUpdateUrl;
} else {
const getXYdata = xyPostion[index].pos;
const getPosData = getXYdata;
const addSign = getPosData.map((url, ind) => {
if (url.key === signKey) {
return { ...url, SignUrl: signatureImg, Width: 150, Height: 60 };
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
return newUpdateUrl;
}
}
//function for getting contract_User details
export const contractUsers = async (objectId) => {
const result = await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Users?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${objectId}"}}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
return res;
})
.catch((err) => {
return "Error: Something went wrong!";
});
return result;
};
//function for getting contrscts_contactbook details
export const contactBook = async (objectId) => {
const result = await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Contactbook?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${objectId}"}}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
return res;
})
.catch((err) => {
return "Error: Something went wrong!";
});
return result;
};
export const contactBookName = async (userPhone, className) => {
const result = await axios
.get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${className}?where={"Phone":"${userPhone}"}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
}
)
.then((Listdata) => {
const json = Listdata.data;
return json;
})
.catch((err) => {
return "Error: Something went wrong!";
});
return result;
};
@@ -0,0 +1,10 @@
export const toDataUrl = (file) => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = (e) => {
resolve(e.target.result);
};
});
};
@@ -0,0 +1,68 @@
import { useState } from "react";
import axios from "axios";
export const useFileUpload = () => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [_fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
const [percentage, setpercentage] = useState(0);
const onChange = (e) => {
try {
let files = e.target.files;
if (typeof files[0] !== "undefined") {
fileUpload(files[0]);
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const fileUpload = async (file) => {
setfileload(true);
let image_url = parseBaseUrl.replace("/app", "");
const url = `${image_url}image_upload`;
const formData = new FormData();
formData.append("file", file);
const config = {
headers: {
"content-type": "multipart/form-data",
"X-Parse-Application-Id": parseAppId,
},
onUploadProgress: function (progressEvent) {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
setpercentage(percentCompleted);
},
};
try {
await axios
.post(url, formData, config)
.then((res) => {
if (res.data.status === "Error") {
alert(res.data.message);
}
setFileUpload(res.data.imageUrl);
setfileload(false);
setpercentage(0);
})
.catch((err) => {
alert(`${err.message}`);
setfileload(false);
setpercentage(0);
});
} catch (error) {
alert(error.message);
setfileload(false);
setpercentage(0);
}
};
return { onChange, _fileupload, fileload, percentage, setFileUpload };
};