refactor: remove mf-cra & change routes
@@ -1,25 +0,0 @@
|
||||
const { dependencies } = require("./package.json");
|
||||
|
||||
module.exports = {
|
||||
name: "shell_app", // change me
|
||||
filename: "remoteEntry.js",
|
||||
exposes: {},
|
||||
remotes: {},
|
||||
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"]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,46 +3,56 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@radix-ui/themes": "^2.0.3",
|
||||
"@react-pdf/renderer": "^3.3.8",
|
||||
"@reduxjs/toolkit": "^1.9.7",
|
||||
"axios": "^1.6.0",
|
||||
"daisyui": "^3.9.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"html-react-parser": "^4.2.2",
|
||||
"json-rules-engine-simplified": "^0.1.17",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mf-cra": "^0.0.5",
|
||||
"moment": "^2.29.4",
|
||||
"parse": "^4.3.1",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"print-js": "^1.6.0",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.9.0",
|
||||
"react-bootstrap": "^2.10.1",
|
||||
"react-datepicker": "^6.2.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-drag-listview": "^2.0.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.7.7",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"reactour": "^1.19.2",
|
||||
"redux": "^4.2.1",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"regex-parser": "^2.3.0",
|
||||
"serve": "^14.2.1",
|
||||
"styled-components": "^4.4.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "serve -s build",
|
||||
"start-dev": "mf-cra start",
|
||||
"start-dev" :"react-scripts start",
|
||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||
"build": "npm run version && rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
|
||||
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
|
||||
"docker-build": "npm run version && rm -rf public/mfbuild && npm run docker-micro && CI=false && mf-cra build",
|
||||
"docker-micro": "cd ./SignDocuments && npm install && npm run docker-build",
|
||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||
"build": "npm run version && react-scripts build",
|
||||
"build-win": "npm run version-win && react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"release": "standard-version",
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react";
|
||||
import { Routes, Route, BrowserRouter } from "react-router-dom";
|
||||
import Pgsignup from "./routes/Pgsignup";
|
||||
import Login from "./routes/Login";
|
||||
import Microapp from "./routes/RemoteApp";
|
||||
import Signup from "./routes/Signup";
|
||||
import Form from "./routes/Form";
|
||||
import Report from "./routes/Report";
|
||||
@@ -13,12 +12,21 @@ import UserProfile from "./routes/UserProfile";
|
||||
import PageNotFound from "./routes/PageNotFound";
|
||||
import ForgetPassword from "./routes/ForgetPassword";
|
||||
import ChangePassword from "./routes/ChangePassword";
|
||||
import ReportMicroapp from "./components/ReportMicroapp";
|
||||
import LoadMf from "./routes/LoadMf";
|
||||
import GenerateToken from "./routes/GenerateToken";
|
||||
import ValidateRoute from "./primitives/ValidateRoute";
|
||||
import Webhook from "./routes/Webhook";
|
||||
import Validate from "./primitives/Validate";
|
||||
import DebugPdf from "./routes/DebugPdf";
|
||||
import ManageSign from "./routes/Managesign";
|
||||
import Opensigndrive from "./routes/Opensigndrive";
|
||||
import TemplatePlaceholder from "./routes/TemplatePlaceholder";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import SignYourSelf from "./routes/SignyourselfPdf";
|
||||
import DraftDocument from "./components/pdf/DraftDocument";
|
||||
import PlaceHolderSign from "./routes/PlaceHolderSign";
|
||||
import PdfRequestFiles from "./routes/PdfRequestFiles";
|
||||
import GuestLogin from "./routes/GuestLogin";
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
||||
|
||||
function App() {
|
||||
const [isloading, setIsLoading] = useState(true);
|
||||
@@ -27,8 +35,10 @@ function App() {
|
||||
}, []);
|
||||
|
||||
const handleCredentials = () => {
|
||||
const appId = process.env.REACT_APP_APPID;
|
||||
const baseurl = process.env.REACT_APP_SERVERURL;
|
||||
const appId = "opensign";
|
||||
const baseurl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
const appName = "contracts";
|
||||
try {
|
||||
localStorage.setItem("BaseUrl12", `${baseurl}/`);
|
||||
@@ -67,9 +77,30 @@ function App() {
|
||||
<Route exact path="/signup" element={<Signup />} />
|
||||
</Route>
|
||||
<Route element={<Validate />}>
|
||||
<Route exact path="/load/:remoteApp/*" element={<LoadMf />} />
|
||||
<Route
|
||||
exact
|
||||
path="/load/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/placeholdersign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
</Route>
|
||||
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
|
||||
<Route path="/loadmf/signmicroapp/login" element={<GuestLogin />} />
|
||||
{/* login page route */}
|
||||
<Route
|
||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<GuestLogin />}
|
||||
/>
|
||||
|
||||
<Route path="/debugpdf" element={<DebugPdf />} />
|
||||
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
|
||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
||||
<>
|
||||
@@ -79,15 +110,39 @@ function App() {
|
||||
)}
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route path="/changepassword" element={<ChangePassword />} />
|
||||
<Route path="/mf/:remoteApp/*" element={<Microapp />} />
|
||||
<Route path="/asmf/:remoteApp/*" element={<Microapp />} />
|
||||
<Route path="/rpmf/:remoteApp/*" element={<ReportMicroapp />} />
|
||||
<Route path="/form/:id" element={<Form />} />
|
||||
<Route path="/report/:id" element={<Report />} />
|
||||
<Route path="/dashboard/:id" element={<Dashboard />} />
|
||||
<Route path="/profile" element={<UserProfile />} />
|
||||
<Route path="/generatetoken" element={<GenerateToken />} />
|
||||
<Route path="/webhook" element={<Webhook />} />
|
||||
<Route path="/managesign" element={<ManageSign />} />
|
||||
<Route path="/opensigndrive" element={<Opensigndrive />} />
|
||||
<Route
|
||||
path="/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
/>
|
||||
{/* signyouself route with no rowlevel data using docId from url */}
|
||||
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
||||
{/* draft document route to handle and navigate route page accordiing to document status */}
|
||||
<Route path="/draftDocument" element={<DraftDocument />} />
|
||||
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
||||
<Route
|
||||
path="/placeHolderSign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
{/* 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 />}
|
||||
/>
|
||||
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
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 "./microappComponent/DragElement";
|
||||
import TagManager from "react-gtm-module";
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.REACT_APP_GTM) {
|
||||
const tagManagerArgs = {
|
||||
gtmId: process.env.REACT_APP_GTM
|
||||
};
|
||||
TagManager.initialize(tagManagerArgs);
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
After Width: | Height: | Size: 430 B |
|
After Width: | Height: | Size: 574 B |
|
After Width: | Height: | Size: 448 B |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 569 B |
@@ -0,0 +1,84 @@
|
||||
import React from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { Stage, Layer, Rect } from "react-konva";
|
||||
const RenderDebugPdf = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
padding: 10,
|
||||
zIndex: 1,
|
||||
backgroundColor: "white",
|
||||
border: "1px solid grey",
|
||||
margin: "5px 0"
|
||||
}}
|
||||
>
|
||||
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
position: "relative",
|
||||
border: "1px solid grey",
|
||||
overflow: "auto"
|
||||
}}
|
||||
onMouseMove={props.handleMouseMoveDiv}
|
||||
>
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
props.setPdfLoadFail(load);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={props.pageDetails}
|
||||
ref={props.pdfRef}
|
||||
file={props.pdfUrl}
|
||||
>
|
||||
<Page
|
||||
onLoadSuccess={props.handlePageLoadSuccess}
|
||||
pageNumber={props.pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
</Document>
|
||||
|
||||
<Stage
|
||||
onMouseMove={props.handleMouseMove}
|
||||
onMouseDown={props.handleMouseDown}
|
||||
onMouseUp={props.handleMouseUp}
|
||||
width={props.pdfDimension.width}
|
||||
height={props.pdfDimension.height}
|
||||
style={{ position: "absolute", top: 0, left: 0 }}
|
||||
>
|
||||
<Layer>
|
||||
{props.annotations
|
||||
.filter((value) => value.page === props.pageNumber)
|
||||
.map((value) => {
|
||||
return (
|
||||
<Rect
|
||||
key={value.key}
|
||||
x={value.x}
|
||||
y={value.y}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
fill="transparent"
|
||||
stroke="black"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Layer>
|
||||
</Stage>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenderDebugPdf;
|
||||
@@ -1,66 +0,0 @@
|
||||
import useFederatedComponent from "mf-cra";
|
||||
import React, { Suspense, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const MicroappModal = () => {
|
||||
const { remoteApp } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
return () => localStorage.removeItem("rowlevel");
|
||||
}, []);
|
||||
let paramString = remoteApp;
|
||||
let queryString = new URLSearchParams(paramString);
|
||||
let obj = {};
|
||||
for (const [key, value] of queryString.entries()) {
|
||||
// console.log("value ", key + ":" + value);
|
||||
|
||||
if (key === "remoteUrl") {
|
||||
const url = window.location.origin + "/mfbuild/remoteEntry.js";
|
||||
// console.log("atob(value) ", atob(value))
|
||||
obj = { ...obj, [key]: url };
|
||||
} else {
|
||||
if (key === "moduleToLoad") {
|
||||
obj = { ...obj, [key]: "./" + value };
|
||||
} else {
|
||||
obj = { ...obj, [key]: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log("app ", remoteApp);
|
||||
const { Component: RemoteComponent } = useFederatedComponent(obj);
|
||||
// console.log("RemoteComponent ", RemoteComponent);
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center ">
|
||||
<div className="w-full">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#ffffff",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
minHeight: "50vh"
|
||||
}}
|
||||
>
|
||||
{RemoteComponent && <RemoteComponent />}
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MicroappModal;
|
||||
@@ -1,21 +1,11 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
function Title(props) {
|
||||
function Title({ title, drive }) {
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{`${props.title} - OpenSign™`}</title>
|
||||
{/* <title>
|
||||
{`${localStorage.getItem("appTitle") ? localStorage.getItem("appTitle") : ""} - ${props.title}`}
|
||||
</title> */}
|
||||
<meta
|
||||
name="description"
|
||||
content={`${props.title} - OpenSign™`}
|
||||
/>
|
||||
{/* <meta
|
||||
name="description"
|
||||
content={`${localStorage.getItem("appTitle")} - ${props.title}`}
|
||||
/> */}
|
||||
<title>{drive ? title : `${title} - OpenSign™`}</title>
|
||||
<meta name="description" content={`${title} - OpenSign™`} />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
|
||||
@@ -0,0 +1,768 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import folder from "../../assets/images/folder.png";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import draftDoc from "../../assets/images/draftDoc1.png";
|
||||
import pdfLogo from "../../assets/images/pdf3.png";
|
||||
import axios from "axios";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { saveAs } from "file-saver";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Table from "react-bootstrap/Table";
|
||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||
import SelectFolder from "../fields/SelectFolder"; //check this one
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function DriveBody({
|
||||
pdfData,
|
||||
setFolderName,
|
||||
setDocId,
|
||||
setIsLoading,
|
||||
setPdfData,
|
||||
isList,
|
||||
setIsAlert
|
||||
}) {
|
||||
const [rename, setRename] = useState("");
|
||||
const [renameValue, setRenameValue] = useState("");
|
||||
const inputRef = useRef(null);
|
||||
const [isOpenMoveModal, setIsOpenMoveModal] = useState(false);
|
||||
const [selectDoc, setSelectDoc] = useState();
|
||||
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
|
||||
const contextMenu = [
|
||||
{ type: "Download", icon: "fa-solid fa-arrow-down" },
|
||||
{ type: "Rename", icon: "fa-solid fa-font" },
|
||||
{ type: "Move", icon: "fa-solid fa-file-export" },
|
||||
{ type: "Delete", icon: "fa-solid fa-trash" }
|
||||
];
|
||||
const navigate = useNavigate();
|
||||
|
||||
//to focus input box on press rename to change doc name
|
||||
useEffect(() => {
|
||||
if (rename && 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(() => {
|
||||
// const res = result.data;
|
||||
// console.log("res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
isPlaceholder?.length > 0 &&
|
||||
!signedUrl
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
(signerExist?.length > 0 &&
|
||||
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (selectType, data) => {
|
||||
switch (selectType) {
|
||||
case "Download": {
|
||||
const pdfName = data && data.Name;
|
||||
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
break;
|
||||
}
|
||||
case "Rename": {
|
||||
setRenameValue(data.Name);
|
||||
setRename(data.objectId);
|
||||
break;
|
||||
}
|
||||
case "Delete": {
|
||||
setIsDeleteDoc(true);
|
||||
setSelectDoc(data);
|
||||
break;
|
||||
}
|
||||
case "Move": {
|
||||
handleMoveDocument(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
null;
|
||||
}
|
||||
};
|
||||
//function for delete document
|
||||
const handleDeleteDocument = async (docData) => {
|
||||
setIsDeleteDoc(false);
|
||||
const docId = docData.objectId;
|
||||
const data = {
|
||||
IsArchive: true
|
||||
};
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${docId}`,
|
||||
data,
|
||||
{
|
||||
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;
|
||||
if (res) {
|
||||
const updatedData = pdfData.filter((x) => x.objectId !== docId);
|
||||
setPdfData(updatedData);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
};
|
||||
const handleMoveDocument = async (docData) => {
|
||||
setIsOpenMoveModal(true);
|
||||
setSelectDoc(docData);
|
||||
};
|
||||
//function for move document from one folder to another folder
|
||||
const handleMoveFolder = async (selectFolderData) => {
|
||||
const selecFolderId = selectDoc?.Folder?.objectId;
|
||||
const moveFolderId = selectFolderData?.ObjectId;
|
||||
let updateDocId = selectDoc?.objectId;
|
||||
let updateData;
|
||||
const checkExist = moveFolderId
|
||||
? selecFolderId === moveFolderId
|
||||
? true
|
||||
: false
|
||||
: selecFolderId
|
||||
? false
|
||||
: true;
|
||||
if (!checkExist) {
|
||||
if (moveFolderId) {
|
||||
updateData = {
|
||||
Folder: {
|
||||
__type: "Pointer",
|
||||
className: `${localStorage.getItem("_appName")}_Document`,
|
||||
objectId: moveFolderId
|
||||
}
|
||||
};
|
||||
} else {
|
||||
updateData = {
|
||||
Folder: { __op: "Delete" }
|
||||
};
|
||||
}
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${updateDocId}`,
|
||||
updateData,
|
||||
{
|
||||
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 res = Listdata.data;
|
||||
if (res) {
|
||||
const updatedData = pdfData.filter(
|
||||
(x) => x.objectId !== updateDocId
|
||||
);
|
||||
setPdfData(updatedData);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
});
|
||||
|
||||
setIsOpenMoveModal(false);
|
||||
} else {
|
||||
alert("folder already exist!");
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDraftDoc = (data) => {
|
||||
navigate(`/legagenie?${data.objectId}`);
|
||||
};
|
||||
|
||||
//component to handle type of document and render according to type
|
||||
const handleFolderData = (data, ind, listType) => {
|
||||
let createddate, status, isDecline, signerExist, isComplete, 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;
|
||||
const signedUrl = data.SignedUrl;
|
||||
|
||||
const expireUpdateDate = new Date(expireDate).getTime();
|
||||
const currDate = new Date().getTime();
|
||||
let isExpire = false;
|
||||
if (currDate > expireUpdateDate) {
|
||||
isExpire = true;
|
||||
}
|
||||
|
||||
if (isComplete) {
|
||||
status = "Completed";
|
||||
} else if (isDecline) {
|
||||
status = "Declined";
|
||||
} else if (!signerExist || signerExist?.length === 0) {
|
||||
status = "Draft";
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
(!isPlaceholder || isPlaceholder?.length === 0)
|
||||
) {
|
||||
status = "Draft";
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
isPlaceholder?.length > 0 &&
|
||||
!signedUrl
|
||||
) {
|
||||
status = "Draft";
|
||||
} else if (isExpire) {
|
||||
status = "Expired";
|
||||
} else {
|
||||
status = "In Progress";
|
||||
}
|
||||
}
|
||||
|
||||
const signersName = () => {
|
||||
const getSignersName = signerExist.map((data) => data.Name);
|
||||
const signerName = getSignersName.join(",");
|
||||
|
||||
return (
|
||||
<span
|
||||
className="statusSpan"
|
||||
style={{ width: "90%", wordWrap: "break-word" }}
|
||||
>
|
||||
{signerName}{" "}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
return listType === "table" ? (
|
||||
data.Type === "Folder" ? (
|
||||
<tr onClick={() => handleOnclikFolder(data)}>
|
||||
<td
|
||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<i
|
||||
className="fa fa-folder"
|
||||
aria-hidden="true"
|
||||
style={{ color: "#f0ad26", marginRight: "8px", fontSize: "26px" }}
|
||||
></i>
|
||||
|
||||
<span style={{ fontSize: "12px", fontWeight: "500" }}>
|
||||
{data.Name}
|
||||
</span>
|
||||
</td>
|
||||
<td>_</td>
|
||||
<td>Folder</td>
|
||||
<td>_</td>
|
||||
<td>_</td>
|
||||
</tr>
|
||||
) : data.Type === "AIDoc" ? (
|
||||
<tr onClick={() => handleDraftDoc(data)}>
|
||||
<td
|
||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<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
|
||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<i
|
||||
className="fa fa-file-pdf"
|
||||
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
|
||||
></i>
|
||||
|
||||
<span style={{ fontSize: "12px", fontWeight: "500" }}>
|
||||
{data.Name}
|
||||
</span>
|
||||
</td>
|
||||
<td>{createddate}</td>
|
||||
<td>Pdf</td>
|
||||
<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"
|
||||
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("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-solid fa-font"></i>
|
||||
<span style={{ marginLeft: "8px" }}>Rename</span>
|
||||
</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"
|
||||
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("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-solid fa-font"></i>
|
||||
<span style={{ marginLeft: "8px" }}>Rename</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
) : (
|
||||
<HoverCard.Root openDelay={0} closeDelay={100}>
|
||||
<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 === "In Progress" && (
|
||||
<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.map((menu, ind) => {
|
||||
return (
|
||||
<ContextMenu.Item
|
||||
key={ind}
|
||||
onClick={() => handleMenuItemClick(menu.type, data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className={menu.icon}></i>
|
||||
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
|
||||
</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>
|
||||
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
||||
{signersName()}
|
||||
</>
|
||||
)}
|
||||
|
||||
<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>
|
||||
)}
|
||||
{isOpenMoveModal && (
|
||||
<SelectFolder
|
||||
onSuccess={handleMoveFolder}
|
||||
isOpenModal={isOpenMoveModal}
|
||||
folderCls={"contracts_Document"}
|
||||
setIsOpenMoveModal={setIsOpenMoveModal}
|
||||
setPdfData={setPdfData}
|
||||
/>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isDeleteDoc}
|
||||
headerColor={themeColor}
|
||||
title={"Delete Document"}
|
||||
handleClose={() => {
|
||||
setIsDeleteDoc(false);
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<p>Are you sure you want to delete this document?</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
handleDeleteDocument(selectDoc);
|
||||
}}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteDoc(false);
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default DriveBody;
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
|
||||
function BorderResize({ right, top }) {
|
||||
return (
|
||||
<div
|
||||
className="borderResize"
|
||||
style={{
|
||||
right: right ? right + "px" : "-1px",
|
||||
bottom: top ? top + "px" : "-1px",
|
||||
borderRight: "3px solid #188ae2",
|
||||
borderBottom: "3px solid #188ae2"
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BorderResize;
|
||||
@@ -0,0 +1,277 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "../../styles/certificate.css";
|
||||
import opensignLogo from "../../assets/images/open-sign-logo.png";
|
||||
import {
|
||||
Page,
|
||||
Text,
|
||||
View,
|
||||
Document,
|
||||
StyleSheet,
|
||||
Image
|
||||
} from "@react-pdf/renderer";
|
||||
|
||||
function Certificate({ pdfData }) {
|
||||
const [isMultiSigners, setIsMultiSigners] = useState();
|
||||
const [multiSigner, setMultiSigners] = useState([]);
|
||||
const [isLoad, setIsLoad] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
handleSignerData();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
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"
|
||||
},
|
||||
image: {
|
||||
width: "71px",
|
||||
height: "17px"
|
||||
}
|
||||
});
|
||||
|
||||
const generatedDate = () => {
|
||||
const newDate = new Date();
|
||||
const utcTime = newDate.toUTCString();
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={{
|
||||
color: "gray",
|
||||
fontSize: "10px"
|
||||
}}
|
||||
>
|
||||
Generated On {utcTime}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
const changeCompletedDate = () => {
|
||||
const completedOn = pdfData[0].updatedAt;
|
||||
const newDate = new Date(completedOn);
|
||||
const utcTime = newDate.toUTCString();
|
||||
|
||||
return <Text style={styles.textStyle2}>{utcTime}</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 :
|
||||
<Text style={styles.textStyle2}>{getSignerName[0].Name}</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Email :
|
||||
<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}>
|
||||
<View
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "30px"
|
||||
}}
|
||||
>
|
||||
<Image src={opensignLogo} style={styles.image} />
|
||||
{generatedDate()}
|
||||
</View>
|
||||
|
||||
<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 :
|
||||
<Text style={styles.textStyle2}>{pdfData[0].objectId}</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Document Name :
|
||||
<Text style={styles.textStyle2}>{pdfData[0].Name}</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Organization :
|
||||
<Text style={styles.textStyle2}>
|
||||
{pdfData[0].ExtUserPtr.Company}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Completed on : {changeCompletedDate()}
|
||||
</Text>
|
||||
{multiSigner && multiSigner.length > 0 && (
|
||||
<Text style={styles.textStyle}>
|
||||
Signers :
|
||||
<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 :
|
||||
<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 : <Text style={styles.textStyle2}>1</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Name :
|
||||
<Text style={styles.textStyle2}>
|
||||
{pdfData[0].ExtUserPtr.Name}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Email :
|
||||
<Text style={styles.textStyle2}>
|
||||
{pdfData[0].ExtUserPtr.Email}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text style={styles.textStyle}>
|
||||
Accessed from :
|
||||
<Text style={styles.textStyle2}>
|
||||
{pdfData[0].AuditTrail &&
|
||||
pdfData[0].AuditTrail[0].ipAddress}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
<Text style={styles.textStyle}>
|
||||
Signed on : {changeCompletedDate()}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Certificate;
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
const confirmToaddDefaultSign = () => {
|
||||
if (xyPostion.length > 0) {
|
||||
setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage:
|
||||
"Are you sure you want to auto sign at requested all locations?"
|
||||
});
|
||||
} else {
|
||||
setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: "please select position!"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
padding: "5px",
|
||||
fontFamily: "sans-serif",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
Signature
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginTop: "10px",
|
||||
fontWeight: "600"
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<p>Your Signature</p>
|
||||
<div className="defaultSignBox">
|
||||
<img
|
||||
alt="default img"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
src={defaultSignImg}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
marginTop: "20px",
|
||||
cursor: "pointer",
|
||||
marginBottom: "10px"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn finishnHover"
|
||||
onClick={() => confirmToaddDefaultSign()}
|
||||
>
|
||||
Auto Sign All
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DefaultSignature;
|
||||
@@ -0,0 +1,97 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Loader from "./Loader";
|
||||
import { contractDocument } from "../../constant/Utils";
|
||||
import HandleError from "../../primitives/HandleError";
|
||||
function DraftDocument() {
|
||||
const navigate = useNavigate();
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoader: true,
|
||||
message: "This might take some time"
|
||||
});
|
||||
|
||||
const rowLevel =
|
||||
localStorage.getItem("rowlevel") &&
|
||||
JSON.parse(localStorage.getItem("rowlevel"));
|
||||
const docId =
|
||||
rowLevel && rowLevel?.id
|
||||
? rowLevel.id
|
||||
: rowLevel?.objectId && rowLevel.objectId;
|
||||
|
||||
useEffect(() => {
|
||||
getDocumentDetails();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//get document details
|
||||
const getDocumentDetails = async () => {
|
||||
//getting document details
|
||||
const documentData = await contractDocument(docId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
handleDraftDoc(documentData);
|
||||
} else if (
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: "Error: Something went wrong!"
|
||||
});
|
||||
} else {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: "No data found!"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//check document type and render on signyour self and placeholder route
|
||||
const handleDraftDoc = (documentData) => {
|
||||
const data = documentData[0];
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
isPlaceholder?.length > 0 &&
|
||||
!signedUrl
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
(signerExist?.length > 0 &&
|
||||
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading.isLoader ? (
|
||||
<Loader isLoading={isLoading} />
|
||||
) : (
|
||||
<HandleError handleError={isLoading.message} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DraftDocument;
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import { getWidgetType, widgets } from "../../constant/Utils";
|
||||
|
||||
function DragElement(item) {
|
||||
const getWidgets = widgets;
|
||||
const filterWidgetPreview = getWidgets.filter(
|
||||
(data) => data.type === item?.text
|
||||
);
|
||||
|
||||
return <div>{getWidgetType(filterWidgetPreview[0])}</div>;
|
||||
}
|
||||
|
||||
export default DragElement;
|
||||
@@ -0,0 +1,374 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
function DropdownWidgetOption(props) {
|
||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||
"option-1",
|
||||
"option-2"
|
||||
]);
|
||||
const [minCount, setMinCount] = useState(0);
|
||||
const [maxCount, setMaxCount] = useState(0);
|
||||
const [dropdownName, setDropdownName] = useState(props.type);
|
||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||
const [status, setStatus] = useState("required");
|
||||
const [defaultValue, setDefaultValue] = useState("");
|
||||
const statusArr = ["required", "optional"];
|
||||
const [defaultCheckbox, setDefaultCheckbox] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.currWidgetsDetails?.options?.name &&
|
||||
props.currWidgetsDetails?.options?.values
|
||||
) {
|
||||
setDropdownName(props.currWidgetsDetails?.options?.name);
|
||||
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
|
||||
setMinCount(
|
||||
props.currWidgetsDetails?.options?.validation?.minRequiredCount
|
||||
);
|
||||
setMaxCount(
|
||||
props.currWidgetsDetails?.options?.validation?.maxRequiredCount
|
||||
);
|
||||
setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly);
|
||||
setStatus(props.currWidgetsDetails?.options?.status || "required");
|
||||
setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || "");
|
||||
setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []);
|
||||
} else {
|
||||
setStatus("required");
|
||||
}
|
||||
}, [props.currWidgetsDetails]);
|
||||
const handleInputChange = (index, value) => {
|
||||
setDropdownOptionList((prevInputs) => {
|
||||
const newInputs = [...prevInputs];
|
||||
newInputs[index] = value;
|
||||
return newInputs;
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddInput = () => {
|
||||
const flage = true;
|
||||
setDropdownOptionList((prevInputs) => [...prevInputs, ""]);
|
||||
props.handleSaveWidgetsOptions(null, null, null, null, null, flage, false);
|
||||
};
|
||||
|
||||
const handleDeleteInput = (ind) => {
|
||||
const flage = true;
|
||||
const getUpdatedOptions = dropdownOptionList.filter(
|
||||
(data, index) => index !== ind
|
||||
);
|
||||
setDropdownOptionList(getUpdatedOptions);
|
||||
props.handleSaveWidgetsOptions(null, null, null, null, null, false, flage);
|
||||
};
|
||||
|
||||
const handleSaveOption = () => {
|
||||
const defaultData =
|
||||
defaultCheckbox && defaultCheckbox.length > 0
|
||||
? defaultCheckbox
|
||||
: defaultValue;
|
||||
|
||||
props.handleSaveWidgetsOptions(
|
||||
dropdownName,
|
||||
dropdownOptionList,
|
||||
minCount,
|
||||
maxCount,
|
||||
isReadOnly,
|
||||
null,
|
||||
null,
|
||||
status,
|
||||
defaultData
|
||||
);
|
||||
props.setShowDropdown(false);
|
||||
setDropdownOptionList(["option-1", "option-2"]);
|
||||
setDropdownName(props.type);
|
||||
props.setCurrWidgetsDetails({});
|
||||
setIsReadOnly(false);
|
||||
setMinCount(0);
|
||||
setMaxCount(0);
|
||||
setDefaultCheckbox([]);
|
||||
setDefaultValue("");
|
||||
};
|
||||
|
||||
const handleSetMinMax = (e) => {
|
||||
const minValue = e.target.value;
|
||||
if (minValue > dropdownOptionList.length) {
|
||||
return "";
|
||||
} else {
|
||||
return minValue;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDefaultCheck = (index) => {
|
||||
const getDefaultCheck = defaultCheckbox.includes(index);
|
||||
if (getDefaultCheck) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
//props.showDropdown
|
||||
<ModalUi
|
||||
// styleClass={"dropdownModal"}
|
||||
isOpen={props.showDropdown}
|
||||
title={props.title}
|
||||
closeOff={true}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSaveOption();
|
||||
}}
|
||||
>
|
||||
<div className="dropdownContainer">
|
||||
{["checkbox", "radio"].includes(props.type) &&
|
||||
!props.isSignYourself && (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isReadOnly}
|
||||
onChange={(e) => {
|
||||
setIsReadOnly(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<label style={{ marginLeft: "10px" }}>Is read only</label>
|
||||
</div>
|
||||
)}
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={dropdownName}
|
||||
value={dropdownName}
|
||||
onChange={(e) => setDropdownName(e.target.value)}
|
||||
className="drodown-input"
|
||||
/>
|
||||
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Minimun check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={minCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMinCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<label
|
||||
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
|
||||
>
|
||||
Options
|
||||
</label>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
{dropdownOptionList.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
marginBottom: "5px",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={handleDefaultCheck(index)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
const getDefaultCheck =
|
||||
defaultCheckbox?.includes(index);
|
||||
if (!getDefaultCheck) {
|
||||
setDefaultCheckbox((prev) => [...prev, index]);
|
||||
}
|
||||
} else {
|
||||
const removeOption = defaultCheckbox.filter(
|
||||
(data) => data !== index
|
||||
);
|
||||
setDefaultCheckbox(removeOption);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
width: "23px",
|
||||
height: "19px",
|
||||
marginRight: "5px"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
required
|
||||
className="drodown-input"
|
||||
type="text"
|
||||
value={option}
|
||||
onChange={(e) => handleInputChange(index, e.target.value)}
|
||||
/>
|
||||
|
||||
<i
|
||||
className="fa-solid fa-rectangle-xmark"
|
||||
onClick={() => handleDeleteInput(index)}
|
||||
style={{
|
||||
color: "red",
|
||||
fontSize: "25px",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
></i>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<i
|
||||
onClick={handleAddInput}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: themeColor,
|
||||
fontSize: "25px",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
className="fa-solid fa-square-plus"
|
||||
></i>
|
||||
</div>
|
||||
{["dropdown", "radio"].includes(props.type) && (
|
||||
<>
|
||||
<label
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
Default value
|
||||
</label>
|
||||
<select
|
||||
onChange={(e) => setDefaultValue(e.target.value)}
|
||||
className="drodown-input"
|
||||
name="defaultvalue"
|
||||
value={defaultValue}
|
||||
placeholder="select default value"
|
||||
>
|
||||
<option value="" disabled hidden style={{ fontSize: "13px" }}>
|
||||
Select...
|
||||
</option>
|
||||
{dropdownOptionList.map((data, ind) => {
|
||||
return (
|
||||
<option
|
||||
style={{ fontSize: "13px" }}
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
{props.type !== "checkbox" && props.type !== "radio" && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginTop: "0.5rem"
|
||||
}}
|
||||
>
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 5,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<input
|
||||
style={{ accentColor: "red" }}
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() => setStatus(data.toLowerCase())}
|
||||
checked={status.toLowerCase() === data.toLowerCase()}
|
||||
/>
|
||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
||||
{data}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
// onClick={() => handleSaveOption()}
|
||||
disabled={dropdownOptionList.length === 0 && true}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="submit"
|
||||
className={"finishBtn"}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
||||
<button
|
||||
type="submit"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
setDropdownOptionList(["option-1", "option-2"]);
|
||||
setDropdownName(props.type);
|
||||
props.setCurrWidgetsDetails([]);
|
||||
props.setShowDropdown(false);
|
||||
setDefaultCheckbox([]);
|
||||
setDefaultValue("");
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DropdownWidgetOption;
|
||||
@@ -0,0 +1,142 @@
|
||||
import React, { useState } from "react";
|
||||
import "../../styles/AddUser.css";
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
Name: template?.Name || "",
|
||||
Note: template?.Note || "",
|
||||
Description: template?.Description || "",
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
|
||||
});
|
||||
|
||||
const handleStrInput = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
// const handleFolder = (data) => {
|
||||
// console.log("handleFolder ", data)
|
||||
// setFolder(data);
|
||||
// };
|
||||
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const data = { ...formData, SendinOrder: isChecked };
|
||||
onSuccess(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addusercontainer">
|
||||
<div className="form-wrapper">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
File
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
padding: "0.5rem 0.75rem",
|
||||
border: "1px solid #d1d5db",
|
||||
borderRadius: "0.375rem",
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: "700"
|
||||
}}
|
||||
>
|
||||
{template.URL?.split("/")?.pop()?.split("_")[1]}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Name"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="Note" style={{ fontSize: 13 }}>
|
||||
Note
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Note"
|
||||
id="Note"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="Description" style={{ fontSize: 13 }}>
|
||||
Description
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Description"
|
||||
id="Description"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label style={{ fontSize: 13 }}>Send In Order</label>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginLeft: 8,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div style={{ fontSize: 12 }}>Yes</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginLeft: 8,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div style={{ fontSize: 12 }}>No</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
|
||||
<div className="buttoncontainer">
|
||||
<button type="submit" className="submitbutton">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditTemplate;
|
||||
@@ -0,0 +1,430 @@
|
||||
import React, { useState } from "react";
|
||||
import { saveAs } from "file-saver";
|
||||
import celebration from "../../assets/images/newCeleb.gif";
|
||||
import axios from "axios";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import printModule from "print-js";
|
||||
import loader from "../../assets/images/loader2.gif";
|
||||
|
||||
function EmailComponent({
|
||||
isEmail,
|
||||
pdfUrl,
|
||||
isCeleb,
|
||||
setIsEmail,
|
||||
setSuccessEmail,
|
||||
pdfName,
|
||||
sender,
|
||||
setIsAlert
|
||||
}) {
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
//function for send email
|
||||
const sendEmail = async () => {
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
for (let i = 0; i < emailList.length; i++) {
|
||||
try {
|
||||
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 openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||
const themeBGcolor = themeColor;
|
||||
let params = {
|
||||
pdfName: pdfName,
|
||||
url: pdfUrl,
|
||||
recipient: emailList[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 <strong>" +
|
||||
pdfName +
|
||||
" </strong>is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. 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 OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a> </p></div></div></body></html>"
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMail && sendMail.data.result.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setTimeout(() => {
|
||||
setSuccessEmail(false);
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}, 1500);
|
||||
|
||||
setIsLoading(false);
|
||||
} else if (sendMail && sendMail.data.result.status === "error") {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}
|
||||
};
|
||||
|
||||
//function for remove email
|
||||
const removeChip = (index) => {
|
||||
const updateEmailCount = emailList.filter((data, key) => key !== index);
|
||||
setEmailList(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) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
} else if (e === "add" && emailValue) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
}
|
||||
};
|
||||
|
||||
// function for print signed pdf
|
||||
const handleToPrint = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const pdf = await getBase64FromUrl(pdfUrl);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadPdf = () => {
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const isAndroid = /Android/i.test(navigator.userAgent);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
{isEmail && (
|
||||
<div className="modaloverlay">
|
||||
<div className="modalcontainer">
|
||||
{isLoading && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
zIndex: "20",
|
||||
backgroundColor: "#e6f2f2",
|
||||
opacity: 0.8
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="no img"
|
||||
src={loader}
|
||||
style={{ width: "70px", height: "70px" }}
|
||||
/>
|
||||
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
|
||||
This might take some time
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ background: "#32a3ac" }} className="modalheader">
|
||||
<span style={{ color: "white" }}>Successfully signed!</span>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div></div>
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="btn btn-primary btn-sm"
|
||||
>
|
||||
<i
|
||||
className="fa fa-print"
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
marginRight: "3px",
|
||||
color: "white"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{isCeleb && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
marginLeft: "50px"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
width={300}
|
||||
height={250}
|
||||
// style={styles.gifCeleb}
|
||||
src={celebration}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "system-ui",
|
||||
verticalAlign: "baseline",
|
||||
fontWeight: "500",
|
||||
color: "#403f3e",
|
||||
fontSize: "15px",
|
||||
marginBottom: "5px"
|
||||
}}
|
||||
>
|
||||
Recipients added here will get a copy of the signed document.
|
||||
</p>
|
||||
{emailList.length > 0 ? (
|
||||
<>
|
||||
<div className="addEmail">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{emailList.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
className="emailChip"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
key={ind}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "13px"
|
||||
}}
|
||||
>
|
||||
{data}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
marginLeft: 7,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => removeChip(ind)}
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{emailList.length <= 9 && (
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="addEmailInput"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="emailInput"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
placeholder="Add the email addresses"
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span
|
||||
style={{
|
||||
cursor: emailValue && "pointer"
|
||||
}}
|
||||
onClick={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
style={{
|
||||
backgroundColor: themeColor,
|
||||
padding: "5px 7px",
|
||||
marginTop: "10px",
|
||||
color: "white",
|
||||
borderRadius: "2px"
|
||||
}}
|
||||
className="fa fa-plus"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: "#e3e2e1",
|
||||
marginTop: "10px",
|
||||
padding: "5px",
|
||||
borderRadius: "3px"
|
||||
}}
|
||||
>
|
||||
<span style={{ fontWeight: "700" }}>Note:</span>
|
||||
<span style={{ fontSize: "15px" }}>
|
||||
{" "}
|
||||
You can only send to ten recipients at a time.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
disabled={emailList.length === 0 && true}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="button"
|
||||
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
|
||||
onClick={() => sendEmail()}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmailComponent;
|
||||
@@ -0,0 +1,762 @@
|
||||
import React from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
import Certificate from "./Certificate";
|
||||
import printModule from "print-js";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { saveAs } from "file-saver";
|
||||
import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function Header({
|
||||
isPdfRequestFiles,
|
||||
isPlaceholder,
|
||||
recipient,
|
||||
setIsDecline,
|
||||
pageNumber,
|
||||
isAlreadySign,
|
||||
allPages,
|
||||
changePage,
|
||||
pdfUrl,
|
||||
documentStatus,
|
||||
embedWidgetsData,
|
||||
pdfDetails,
|
||||
signerPos,
|
||||
signersdata,
|
||||
isMailSend,
|
||||
alertSendEmail,
|
||||
isCompleted,
|
||||
isShowHeader,
|
||||
decline,
|
||||
currentSigner,
|
||||
dataTut4,
|
||||
alreadySign,
|
||||
isSignYourself,
|
||||
setIsEmail,
|
||||
completeBtnTitle,
|
||||
setIsEditTemplate
|
||||
}) {
|
||||
const filterPrefill =
|
||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const navigate = useNavigate();
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
//for go to previous page
|
||||
function previousPage() {
|
||||
changePage(-1);
|
||||
}
|
||||
//for go to next page
|
||||
function nextPage() {
|
||||
changePage(1);
|
||||
}
|
||||
//function for show decline alert
|
||||
const handleDeclinePdfAlert = async () => {
|
||||
const currentDecline = {
|
||||
currnt: "Sure",
|
||||
isDeclined: true
|
||||
};
|
||||
setIsDecline(currentDecline);
|
||||
};
|
||||
|
||||
//function for print digital sign pdf
|
||||
const handleToPrint = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const pdf = await getBase64FromUrl(pdfUrl);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadPdf = () => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
//certificate generate and download component in mobile view
|
||||
const CertificateDropDown = () => {
|
||||
//after generate download certifcate pdf
|
||||
const handleDownload = (pdfBlob, fileName) => {
|
||||
if (pdfBlob) {
|
||||
const url = window.URL.createObjectURL(pdfBlob);
|
||||
// Create a temporary anchor element
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
// Append the anchor to the body
|
||||
document.body.appendChild(link);
|
||||
// Programmatically click the anchor to trigger the download
|
||||
link.click();
|
||||
// Remove the anchor from the body
|
||||
document.body.removeChild(link);
|
||||
// Release the object URL to free up resources
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PDFDownloadLink
|
||||
onClick={(e) => e.preventDefault()}
|
||||
style={{ textDecoration: "none", zIndex: "35" }}
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
>
|
||||
{({ blob, loading }) => (
|
||||
<>
|
||||
{loading ? (
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
marginRight: "2px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
onClick={() =>
|
||||
handleDownload(
|
||||
blob,
|
||||
`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`
|
||||
)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
marginRight: "2px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
);
|
||||
};
|
||||
const CertificateComponent = () => {
|
||||
return (
|
||||
<PDFDownloadLink
|
||||
style={{ textDecoration: "none" }}
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
fileName={`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`}
|
||||
>
|
||||
{({ loading }) => (
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn certificateBtn"
|
||||
disabled={loading}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</button>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||
{isMobile && isShowHeader ? (
|
||||
<div
|
||||
id="navbar"
|
||||
className={isGuestSigner ? "stickySignerHead" : "stickyHead"}
|
||||
style={{
|
||||
width: isGuestSigner
|
||||
? window.innerWidth
|
||||
: 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 && alreadySign ? (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div
|
||||
style={{
|
||||
color: themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px",
|
||||
padding: "0px 3px 0px 5px"
|
||||
}}
|
||||
>
|
||||
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="DropdownMenuContent"
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-arrow-down"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "2px" }}
|
||||
></i>
|
||||
Download
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{recipient && pdfDetails[0] && pdfDetails.length > 0 ? (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
) : isPdfRequestFiles &&
|
||||
alreadySign &&
|
||||
isCompleted.isCertificate ? (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
) : (
|
||||
isSignYourself && (
|
||||
<>
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-envelope"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={handleToPrint}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<i
|
||||
className="fa fa-print"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "2px" }}
|
||||
></i>
|
||||
Print
|
||||
</div>
|
||||
</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={() => {
|
||||
if (!isMailSend) {
|
||||
alertSendEmail();
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: isMailSend ? "gray" : themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
data-tut={dataTut4}
|
||||
>
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
} else if (isPdfRequestFiles) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<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 && (
|
||||
<CertificateComponent />
|
||||
)}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="defaultBtn printBtn"
|
||||
>
|
||||
<i
|
||||
className="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) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
) : isPlaceholder ? (
|
||||
<>
|
||||
{!isMailSend &&
|
||||
signersdata.length > 0 &&
|
||||
signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length}{" "}
|
||||
recipients signature
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length} more
|
||||
recipients signature
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
{setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => setIsEditTemplate(true)}
|
||||
style={{
|
||||
border: "none",
|
||||
outline: "none",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
type="button"
|
||||
className="defaultBtn backBtn"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
|
||||
<button
|
||||
disabled={isMailSend && true}
|
||||
data-tut="reactourFour"
|
||||
style={{
|
||||
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
|
||||
color: isMailSend && "rgb(77, 75, 75)"
|
||||
}}
|
||||
onClick={() => {
|
||||
alertSendEmail();
|
||||
}}
|
||||
className={isMailSend ? "sendMail" : "sendMail sendHover"}
|
||||
>
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : isPdfRequestFiles ? (
|
||||
alreadySign ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{isCompleted.isCertificate && <CertificateComponent />}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
type="button"
|
||||
className="defaultBtn printBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="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={() => {
|
||||
embedWidgetsData();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<CertificateComponent />
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
type="button"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="defaultBtn printBtn"
|
||||
>
|
||||
<i
|
||||
className="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>
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn mailBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<i
|
||||
className="fa fa-envelope"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
</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) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import loader from "../../assets/images/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,624 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import BorderResize from "./BorderResize";
|
||||
import PlaceholderBorder from "./PlaceholderBorder";
|
||||
import { Rnd } from "react-rnd";
|
||||
import { defaultWidthHeight, isMobile } from "../../constant/Utils";
|
||||
import PlaceholderType from "./PlaceholderType";
|
||||
import moment from "moment";
|
||||
import "../../styles/opensigndrive.css";
|
||||
|
||||
function Placeholder(props) {
|
||||
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
||||
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
|
||||
const [selectDate, setSelectDate] = useState({
|
||||
date: moment(new Date().getTime()).format("MM/DD/YYYY"),
|
||||
format: "MM/dd/YYYY"
|
||||
});
|
||||
const [dateFormat, setDateFormat] = useState([]);
|
||||
const [saveDateFormat, setSaveDateFormat] = useState("");
|
||||
const dateFormatArr = [
|
||||
"L",
|
||||
"DD-MM-YYYY",
|
||||
"YYYY-MM-DD",
|
||||
"MM.DD.YYYY",
|
||||
"MM-DD-YYYY",
|
||||
"MMM DD, YYYY",
|
||||
"LL",
|
||||
"DD MMM, YYYY",
|
||||
"DD MMMM, YYYY"
|
||||
];
|
||||
|
||||
const selectFormat = (data) => {
|
||||
switch (data) {
|
||||
case "L":
|
||||
return "MM/dd/yyyy";
|
||||
case "DD-MM-YYYY":
|
||||
return "dd-MM-yyyy";
|
||||
case "DD/MM/YYYY":
|
||||
return "dd/MM/yyyy";
|
||||
case "LL":
|
||||
return "MMMM dd, yyyy";
|
||||
case "DD MMM, YYYY":
|
||||
return "dd MMM, YYYY";
|
||||
case "YYYY-MM-DD":
|
||||
return "YYYY-MM-dd";
|
||||
case "MM-DD-YYYY":
|
||||
return "MM-dd-YYYY";
|
||||
case "MM.DD.YYYY":
|
||||
return "MM.dd.YYYY";
|
||||
case "MMM DD, YYYY":
|
||||
return "MMM dd, YYYY";
|
||||
case "DD MMMM, YYYY":
|
||||
return "dd MMMM, YYYY";
|
||||
default:
|
||||
return "dd/MM/yyyy";
|
||||
}
|
||||
};
|
||||
|
||||
//function for add selected date and format in selectFormat
|
||||
const changeDateFormat = () => {
|
||||
const updateDate = [];
|
||||
dateFormatArr.map((data) => {
|
||||
let date;
|
||||
if (selectDate.format === "dd-MM-yyyy") {
|
||||
const [day, month, year] = selectDate.date.split("-");
|
||||
date = new Date(`${year}-${month}-${day}`);
|
||||
} else {
|
||||
date = new Date(selectDate?.date);
|
||||
}
|
||||
const milliseconds = date.getTime();
|
||||
const newDate = moment(milliseconds).format(data);
|
||||
const dateObj = {
|
||||
date: newDate,
|
||||
format: selectFormat(data)
|
||||
};
|
||||
updateDate.push(dateObj);
|
||||
});
|
||||
setDateFormat(updateDate);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (props.isPlaceholder || props.isSignYourself) {
|
||||
selectDate && changeDateFormat();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectDate]);
|
||||
//handle to close drop down menu onclick screen
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (isShowDateFormat && !e.target.closest("#menu-container")) {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
}
|
||||
};
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isShowDateFormat]);
|
||||
|
||||
//onclick placeholder function to open signature pad
|
||||
const handlePlaceholderClick = () => {
|
||||
if (props.setSelectWidgetId) {
|
||||
props.setSelectWidgetId(props.pos.key);
|
||||
}
|
||||
|
||||
const widgetTypeExist = [
|
||||
"text",
|
||||
"checkbox",
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"date",
|
||||
"email"
|
||||
].includes(props.pos.type);
|
||||
|
||||
if (widgetTypeExist) {
|
||||
setDraggingEnabled(false);
|
||||
}
|
||||
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
|
||||
if (props.pos.type) {
|
||||
if (
|
||||
props.pos.type === "signature" ||
|
||||
props.pos.type === "stamp" ||
|
||||
props.pos.type === "image"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.pos.type === "initials") {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
props.setIsInitial(true);
|
||||
}
|
||||
} else {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
|
||||
}
|
||||
} else if (
|
||||
props.isPlaceholder &&
|
||||
!props.isDragging &&
|
||||
props.pos.type !== "label"
|
||||
) {
|
||||
if (props.pos.key === props.selectWidgetId) {
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
} else if (!props.pos.type) {
|
||||
if (
|
||||
!props.pos.type &&
|
||||
props.isNeedSign &&
|
||||
props.data.signerObjId === props.signerObjId
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (
|
||||
(props.isNeedSign && props.pos.type === "signature") ||
|
||||
props.pos.type === "stamp"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.isNeedSign && props.pos.type === "dropdown") {
|
||||
props.setSignKey(props.pos.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleWidgetsOnclick = () => {
|
||||
if (props.pos.type === "radio") {
|
||||
props.setIsRadio(true);
|
||||
} else if (props.pos.type === "dropdown") {
|
||||
props?.setShowDropdown(true);
|
||||
} else if (props.pos.type === "checkbox") {
|
||||
props?.setIsCheckbox(true);
|
||||
} else {
|
||||
props?.handleNameModal(true);
|
||||
}
|
||||
|
||||
if (props.isPlaceholder) {
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setWidgetType(props.pos.type);
|
||||
props.setCurrWidgetsDetails(props.pos);
|
||||
};
|
||||
const PlaceholderIcon = () => {
|
||||
return (
|
||||
props.isShowBorder && (
|
||||
<>
|
||||
{(props.isPlaceholder || props.isSignYourself) && (
|
||||
<>
|
||||
{props.pos.type === "checkbox" && props.isSignYourself ? (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleWidgetsOnclick();
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
handleWidgetsOnclick();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: "9px",
|
||||
top: "-28px"
|
||||
}}
|
||||
></i>
|
||||
) : (
|
||||
props.pos.type !== "date" &&
|
||||
props.pos.type !== "label" &&
|
||||
props.pos.type !== "signature" &&
|
||||
!props.isSignYourself && (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleWidgetsOnclick();
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
handleWidgetsOnclick();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: ["checkbox", "radio"].includes(props.pos.type)
|
||||
? "24px"
|
||||
: "47px",
|
||||
top: ["checkbox", "radio"].includes(props.pos.type)
|
||||
? "-28px"
|
||||
: "-19px"
|
||||
}}
|
||||
></i>
|
||||
)
|
||||
)}
|
||||
|
||||
{props.pos.type !== "label" && !props.isSignYourself && (
|
||||
<i
|
||||
data-tut="reactourLinkUser"
|
||||
className="fa-regular fa-user signUserIcon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}}
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right:
|
||||
props.pos.type === "checkbox" ||
|
||||
props.pos.type === "radio"
|
||||
? "8px"
|
||||
: "32px",
|
||||
top:
|
||||
props.pos.type === "checkbox" ||
|
||||
props.pos.type === "radio"
|
||||
? "-28px"
|
||||
: "-18px"
|
||||
}}
|
||||
></i>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{props.pos.type === "date" && selectDate && (
|
||||
<div
|
||||
id="menu-container"
|
||||
style={{
|
||||
zIndex: "99",
|
||||
top: "-19px",
|
||||
right: props.isPlaceholder ? "60px" : "44px"
|
||||
}}
|
||||
className={
|
||||
isShowDateFormat
|
||||
? "dropdown signUserIcon show"
|
||||
: "dropdown signUserIcon"
|
||||
}
|
||||
onClick={(e) => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
e.stopPropagation();
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
fontSize: "14px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className={
|
||||
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
|
||||
}
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isShowDateFormat ? "true" : "false"}
|
||||
>
|
||||
{dateFormat.map((data, ind) => {
|
||||
return (
|
||||
<span
|
||||
key={ind}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<i
|
||||
className="fa-regular fa-copy signCopy"
|
||||
onClick={(e) => {
|
||||
if (props.data) {
|
||||
props.setSignerObjId(props.data.signerObjId);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
e.stopPropagation();
|
||||
props.setIsPageCopy(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
if (props.data) {
|
||||
props.setSignerObjId(props.data.signerObjId);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
e.stopPropagation();
|
||||
props.setIsPageCopy(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
}}
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right:
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? "-9px"
|
||||
: "12px",
|
||||
top:
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? "-28px"
|
||||
: "-18px"
|
||||
}}
|
||||
></i>
|
||||
<i
|
||||
className="fa-regular fa-circle-xmark signCloseBtn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data.Id);
|
||||
} else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data?.Id);
|
||||
} else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right:
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? "-27px"
|
||||
: "-8px",
|
||||
top:
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? "-28px"
|
||||
: "-18px"
|
||||
}}
|
||||
></i>
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Rnd
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
//ref={nodeRef}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
props.pos.Width
|
||||
? props.pos.Width / props.pos.Height
|
||||
: defaultWidthHeight(props.pos.type).width /
|
||||
defaultWidthHeight(props.pos.type).height
|
||||
}
|
||||
enableResizing={{
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
topRight: false,
|
||||
bottomRight:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.type !== "radio"
|
||||
? true
|
||||
: false
|
||||
: props.pos.type !== "radio" &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId &&
|
||||
true,
|
||||
bottomLeft: false,
|
||||
topLeft: false
|
||||
}}
|
||||
bounds="parent"
|
||||
className="signYourselfBlock"
|
||||
style={{
|
||||
border: props.pos.type !== "checkbox" && "1px solid #007bff",
|
||||
borderRadius: props.pos.type !== "checkbox" && "2px",
|
||||
textAlign:
|
||||
props.pos.type !== "name" &&
|
||||
props.pos.type !== "company" &&
|
||||
props.pos.type !== "job title" &&
|
||||
"center",
|
||||
cursor:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId
|
||||
? "pointer"
|
||||
: "not-allowed"
|
||||
: "all-scroll",
|
||||
zIndex:
|
||||
props.pos.type === "date"
|
||||
? "99"
|
||||
: props?.pos?.zIndex
|
||||
? props.pos.zIndex
|
||||
: "5",
|
||||
background: props.data
|
||||
? props.pos.type !== "checkbox" && props.data.blockColor
|
||||
: props.pos.type !== "checkbox" && "rgb(203 233 237)"
|
||||
}}
|
||||
onDrag={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||
}}
|
||||
size={{
|
||||
width: props.posWidth(props.pos, props.isSignYourself),
|
||||
height: props.posHeight(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResizeStart={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={() => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
}}
|
||||
disableDragging={
|
||||
props.isNeedSign
|
||||
? true
|
||||
: props.isPlaceholder && props.pos.type !== "date"
|
||||
? false
|
||||
: !isDraggingEnabled
|
||||
}
|
||||
onDragStop={(event, dragElement) =>
|
||||
props.handleStop &&
|
||||
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||
}
|
||||
default={{
|
||||
x: props.xPos(props.pos, props.isSignYourself),
|
||||
y: props.yPos(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResize={(e, direction, ref) => {
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
props.data && props.data.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
onClick={() => {
|
||||
!props.isNeedSign && props.setWidgetType(props.pos.type);
|
||||
props.isNeedSign && props.data?.signerObjId === props.signerObjId
|
||||
? handlePlaceholderClick()
|
||||
: props.isPlaceholder
|
||||
? handlePlaceholderClick()
|
||||
: props.isSignYourself && handlePlaceholderClick();
|
||||
}}
|
||||
>
|
||||
{props.isShowBorder &&
|
||||
props.pos.type !== "radio" &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId ? (
|
||||
<BorderResize
|
||||
right={
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? -21
|
||||
: -12
|
||||
}
|
||||
top={
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? -21
|
||||
: -11
|
||||
}
|
||||
/>
|
||||
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||
props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== "radio" &&
|
||||
props.pos.type !== "checkbox" ? (
|
||||
<BorderResize />
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
) : (
|
||||
props.pos.type !== "radio" &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId && <BorderResize />
|
||||
)}
|
||||
|
||||
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
|
||||
<PlaceholderBorder
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pos={props.pos}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
/>
|
||||
)}
|
||||
{isMobile ? (
|
||||
<div
|
||||
style={{
|
||||
left: props.xPos(props.pos, props.isSignYourself),
|
||||
top: props.yPos(props.pos, props.isSignYourself),
|
||||
width: props.posWidth(props.pos, props.isSignYourself),
|
||||
// height: props.posHeight(props.pos, props.isSignYourself),
|
||||
zIndex: "10"
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
!props.isNeedSign && props.setWidgetType(props.pos.type);
|
||||
props.isNeedSign && props.data?.signerObjId === props.signerObjId
|
||||
? handlePlaceholderClick()
|
||||
: props.isPlaceholder
|
||||
? handlePlaceholderClick()
|
||||
: props.isSignYourself && handlePlaceholderClick();
|
||||
}}
|
||||
>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setSaveDateFormat={setSaveDateFormat}
|
||||
saveDateFormat={saveDateFormat}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setSaveDateFormat={setSaveDateFormat}
|
||||
saveDateFormat={saveDateFormat}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Rnd>
|
||||
);
|
||||
}
|
||||
|
||||
export default Placeholder;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
resizeBorderExtraWidth
|
||||
} from "../../constant/Utils";
|
||||
function PlaceholderBorder(props) {
|
||||
const getResizeBorderExtraWidth =
|
||||
props.pos.type === "checkbox" || props.pos.type === "radio"
|
||||
? 38
|
||||
: resizeBorderExtraWidth();
|
||||
const defaultWidth = defaultWidthHeight(props.pos.type).width;
|
||||
const defaultHeight = defaultWidthHeight(props.pos.type).height;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="borderResize"
|
||||
style={{
|
||||
borderColor: themeColor,
|
||||
borderStyle: "dashed",
|
||||
|
||||
width: props.pos.Width
|
||||
? props.pos.Width + getResizeBorderExtraWidth
|
||||
: defaultWidth + getResizeBorderExtraWidth,
|
||||
height: props.pos.Height
|
||||
? props.pos.Height + getResizeBorderExtraWidth
|
||||
: defaultHeight + getResizeBorderExtraWidth,
|
||||
borderWidth: "0.2px",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlaceholderBorder;
|
||||
@@ -0,0 +1,249 @@
|
||||
import React, { useState } from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const copyType = ["All pages", "All pages but last", "All pages but first"];
|
||||
const [selectCopyType, setSelectCopyType] = useState("");
|
||||
|
||||
//get RandomKey Id
|
||||
const randomKey = () => {
|
||||
const randomId = Math.floor(1000 + Math.random() * 9000);
|
||||
return randomId;
|
||||
};
|
||||
|
||||
//function for get copy placeholder position
|
||||
const getCopyPlaceholderPosition = (
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
) => {
|
||||
const filterPosition =
|
||||
existPlaceholderPosition &&
|
||||
existPlaceholderPosition.filter(
|
||||
(data) => data.xPosition !== currentPlaceholder.xPosition
|
||||
);
|
||||
//copy all page placeholder at requested position except first page
|
||||
if (newPageNumber === 1 && type === "first") {
|
||||
if (filterPosition && filterPosition.length > 0) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position except last page
|
||||
else if (newPageNumber === props.allPages && type === "last") {
|
||||
if (existPlaceholderPosition) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position with existing placeholder position
|
||||
else if (existPlaceholderPosition) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition, currentPlaceholder]
|
||||
};
|
||||
}
|
||||
//copy all page placeholder at requested position
|
||||
else {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [currentPlaceholder]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
//function for copy placeholder as per select copy type
|
||||
const copyPlaceholder = (type) => {
|
||||
let newPlaceholderPosition = [];
|
||||
let newPageNumber = 1;
|
||||
const signerPosition = props.xyPostion;
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
|
||||
//handle placeholder array and copy for multiple signers placeholder at requested location
|
||||
if (signerId) {
|
||||
//get current signers data
|
||||
let filterSignerPosition;
|
||||
if (props?.signerObjId) {
|
||||
filterSignerPosition = signerPosition.filter(
|
||||
(data) => data.signerObjId === signerId
|
||||
);
|
||||
} else {
|
||||
filterSignerPosition = signerPosition.filter(
|
||||
(item) => item.Id === signerId
|
||||
);
|
||||
}
|
||||
//get current pagenumber's all placeholder position data
|
||||
const placeholderPosition = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||
(position) => position.key === props.signKey
|
||||
);
|
||||
|
||||
for (let i = 0; i < props.allPages; i++) {
|
||||
const newId = randomKey();
|
||||
currentPlaceholder.key = newId;
|
||||
//get exist placeholder position for particular page
|
||||
const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
newPageNumber++;
|
||||
}
|
||||
let updatedSignerPlaceholder;
|
||||
if (props?.signerObjId) {
|
||||
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||
if (signersData.signerObjId === props.signerObjId) {
|
||||
return {
|
||||
...signersData,
|
||||
placeHolder: newPlaceholderPosition
|
||||
};
|
||||
}
|
||||
return signersData;
|
||||
});
|
||||
} else {
|
||||
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||
if (signersData.Id === props.Id) {
|
||||
return {
|
||||
...signersData,
|
||||
placeHolder: newPlaceholderPosition
|
||||
};
|
||||
}
|
||||
return signersData;
|
||||
});
|
||||
}
|
||||
|
||||
const signersData = signerPosition;
|
||||
signersData.splice(0, signerPosition.length, ...updatedSignerPlaceholder);
|
||||
props.setXyPostion(signersData);
|
||||
}
|
||||
//handle signyourself array and copy for single signers placeholder at requested location
|
||||
else {
|
||||
const xyPostion = props.xyPostion;
|
||||
|
||||
const placeholderPosition = xyPostion.filter(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||
(pos) => pos.key === props.signKey
|
||||
);
|
||||
|
||||
for (let i = 0; i < props.allPages; i++) {
|
||||
//get exist placeholder position for particular page
|
||||
const existPlaceholder = xyPostion.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
const newId = randomKey();
|
||||
currentPlaceholder.key = newId;
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
|
||||
newPageNumber++;
|
||||
}
|
||||
props.setXyPostion(newPlaceholderPosition);
|
||||
}
|
||||
};
|
||||
|
||||
//function for getting selected type placeholder copy
|
||||
const handleApplyCopy = () => {
|
||||
if (selectCopyType === "All pages") {
|
||||
copyPlaceholder("all");
|
||||
} else if (selectCopyType === "All pages but last") {
|
||||
copyPlaceholder("last");
|
||||
} else if (selectCopyType === "All pages but first") {
|
||||
copyPlaceholder("first");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isPageCopy}
|
||||
title={"Copy to all pages"}
|
||||
handleClose={() => {
|
||||
props.setIsPageCopy(false);
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{copyType.map((data, key) => {
|
||||
return (
|
||||
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
|
||||
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
|
||||
<input
|
||||
style={{ accentColor: "red", marginRight: "10px" }}
|
||||
type="radio"
|
||||
value={data}
|
||||
onChange={() => setSelectCopyType(data)}
|
||||
checked={selectCopyType === data}
|
||||
/>
|
||||
|
||||
{data}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleApplyCopy();
|
||||
props.setIsPageCopy(false);
|
||||
}}
|
||||
style={{ background: themeColor }}
|
||||
type="button"
|
||||
disabled={!selectCopyType}
|
||||
className="finishBtn"
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => props.setIsPageCopy(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlaceholderCopy;
|
||||
@@ -0,0 +1,741 @@
|
||||
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||
import { onChangeInput } from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import "../../styles/signature.css";
|
||||
import RegexParser from "regex-parser";
|
||||
|
||||
function PlaceholderType(props) {
|
||||
const type = props.pos.type;
|
||||
const [selectOption, setSelectOption] = useState(
|
||||
props.pos?.options?.defaultValue ? props.pos?.options?.defaultValue : ""
|
||||
);
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
const [validatePlaceholder, setValidatePlaceholder] = useState("");
|
||||
const inputRef = useRef(null);
|
||||
const [textValue, setTextValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
|
||||
const validateExpression = (regexValidation) => {
|
||||
let regexObject = regexValidation;
|
||||
if (props.pos?.options.validation.type === "regex") {
|
||||
regexObject = RegexParser(regexValidation);
|
||||
}
|
||||
// new RegExp(regexValidation);
|
||||
let isValidate = regexObject.test(textValue);
|
||||
if (!isValidate) {
|
||||
props.setValidateAlert(true);
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputBlur = () => {
|
||||
props.setDraggingEnabled(true);
|
||||
const validateType = props.pos?.options?.validation?.type;
|
||||
|
||||
let regexValidation;
|
||||
if (validateType) {
|
||||
switch (validateType) {
|
||||
case "email":
|
||||
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "number":
|
||||
regexValidation = /^\d+$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "text":
|
||||
regexValidation = /^[a-zA-Z\s]+$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
default:
|
||||
regexValidation = props.pos?.options.validation.pattern;
|
||||
validateExpression(regexValidation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTextValid = (e) => {
|
||||
const textInput = e.target.value;
|
||||
setTextValue(textInput);
|
||||
};
|
||||
function checkRegularExpress(validateType) {
|
||||
switch (validateType) {
|
||||
case "email":
|
||||
setValidatePlaceholder("demo@gmail.com");
|
||||
break;
|
||||
case "number":
|
||||
setValidatePlaceholder("12345");
|
||||
break;
|
||||
case "text":
|
||||
setValidatePlaceholder("enter text");
|
||||
break;
|
||||
default:
|
||||
setValidatePlaceholder("enter text");
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
setDefaultdata();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
function setDefaultdata() {
|
||||
if (props.pos?.options?.hint) {
|
||||
setValidatePlaceholder(props.pos?.options.hint);
|
||||
} else if (props.pos?.options?.validation?.type) {
|
||||
checkRegularExpress(props.pos?.options?.validation?.type);
|
||||
}
|
||||
if (props.isNeedSign && props.pos.type === "date") {
|
||||
const updateDate = new Date();
|
||||
setStartDate(updateDate);
|
||||
}
|
||||
setTextValue(
|
||||
props.pos?.options?.defaultValue ? props.pos?.options?.defaultValue : ""
|
||||
);
|
||||
if (props.pos?.type && props.pos.type === "checkbox" && props.isNeedSign) {
|
||||
const isDefaultValue = props.pos.options?.defaultValue;
|
||||
setSelectedCheckbox(isDefaultValue);
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (props.pos?.type && props.pos.type === "date") {
|
||||
if (props.selectDate) {
|
||||
let updateDate;
|
||||
if (props.selectDate.format === "dd-MM-yyyy") {
|
||||
const [day, month, year] = props.saveDateFormat.split("-");
|
||||
updateDate = new Date(`${year}-${month}-${day}`);
|
||||
} else {
|
||||
updateDate = new Date();
|
||||
}
|
||||
// const updateDate = new Date(props.saveDateFormat);
|
||||
setStartDate(updateDate);
|
||||
const dateObj = {
|
||||
date: props.saveDateFormat,
|
||||
format: props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/YYYY"
|
||||
};
|
||||
props.setSelectDate(dateObj);
|
||||
}
|
||||
|
||||
onChangeInput(
|
||||
props.saveDateFormat,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
props.selectDate?.format
|
||||
? props.selectDate.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/YYYY"
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.saveDateFormat]);
|
||||
|
||||
const dateValue = (value) => {
|
||||
props.setSaveDateFormat(value);
|
||||
return <span>{value}</span>;
|
||||
};
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{dateValue(value)}
|
||||
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
|
||||
useEffect(() => {
|
||||
if (props.pos?.type) {
|
||||
const senderUser = localStorage.getItem(`Extand_Class`);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
|
||||
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
|
||||
onChangeInput(
|
||||
jsonSender && jsonSender[0],
|
||||
null,
|
||||
props.xyPostion,
|
||||
null,
|
||||
props.setXyPostion,
|
||||
props.data.Id,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [type]);
|
||||
|
||||
const calculateFontSize = () => {
|
||||
const fontSize = 10 + Math.min(props.pos.Width, props.pos.Height) * 0.1;
|
||||
const size = fontSize ? fontSize : 12;
|
||||
return size + "px";
|
||||
};
|
||||
//function for show checked checkbox
|
||||
const selectCheckbox = (ind) => {
|
||||
const res = props.pos.options?.response;
|
||||
const defaultCheck = props.pos.options?.defaultValue;
|
||||
if (res && res?.length > 0) {
|
||||
const isSelectIndex = res.indexOf(ind);
|
||||
if (isSelectIndex > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
} else if (defaultCheck) {
|
||||
const isSelectIndex = defaultCheck.indexOf(ind);
|
||||
if (isSelectIndex > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRadioCheck = (data) => {
|
||||
const defaultData = props.pos.options?.defaultValue;
|
||||
if (textValue === data) {
|
||||
return true;
|
||||
} else if (defaultData === data) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//function for set checked and unchecked value of checkbox
|
||||
const handleCheckboxValue = (isChecked, ind) => {
|
||||
let updateSelectedCheckbox = [],
|
||||
checkedList;
|
||||
let isDefaultValue, isDefaultEmpty;
|
||||
if (props.pos?.type && ["checkbox", "radio"].includes(props.pos.type)) {
|
||||
updateSelectedCheckbox = selectedCheckbox;
|
||||
if (isChecked) {
|
||||
updateSelectedCheckbox.push(ind);
|
||||
setSelectedCheckbox(updateSelectedCheckbox);
|
||||
} else {
|
||||
checkedList = selectedCheckbox.filter((data) => data !== ind);
|
||||
setSelectedCheckbox(checkedList);
|
||||
}
|
||||
if (props.isNeedSign) {
|
||||
isDefaultValue = props.pos.options?.defaultValue;
|
||||
}
|
||||
if (isDefaultValue && isDefaultValue.length > 0) {
|
||||
isDefaultEmpty = true;
|
||||
}
|
||||
onChangeInput(
|
||||
checkedList ? checkedList : updateSelectedCheckbox,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
isDefaultEmpty
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//function to handle select radio widget and set value seletced by user
|
||||
const handleCheckRadio = (isChecked, data) => {
|
||||
let isDefaultValue, isDefaultEmpty;
|
||||
if (props.isNeedSign) {
|
||||
isDefaultValue = props.pos.options?.defaultValue;
|
||||
}
|
||||
if (isDefaultValue) {
|
||||
isDefaultEmpty = true;
|
||||
}
|
||||
if (isChecked) {
|
||||
setTextValue(data);
|
||||
} else {
|
||||
setTextValue("");
|
||||
}
|
||||
onChangeInput(
|
||||
textValue,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
isDefaultEmpty
|
||||
);
|
||||
};
|
||||
switch (props.pos.type) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "stamp":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
return (
|
||||
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
||||
{props.pos.options?.values?.map((data, ind) => {
|
||||
return (
|
||||
<input
|
||||
key={ind}
|
||||
style={{
|
||||
width: props.pos.Width,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginBottom: "6px",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
onBlur={handleInputBlur}
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
props.pos.options?.isReadOnly &&
|
||||
props.pos.options?.isReadOnly
|
||||
}
|
||||
type="checkbox"
|
||||
checked={selectCheckbox(ind)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked && !props.isPlaceholder) {
|
||||
const maxRequired =
|
||||
props.pos.options?.validation?.maxRequiredCount;
|
||||
const maxCountInt = maxRequired && parseInt(maxRequired);
|
||||
if (maxCountInt > 0) {
|
||||
if (selectedCheckbox.length <= maxCountInt - 1) {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
} else {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
} else {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case "text":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
ref={inputRef}
|
||||
placeholder={validatePlaceholder}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
type="text"
|
||||
tabIndex="0"
|
||||
disabled={
|
||||
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
|
||||
? true
|
||||
: props.isPlaceholder
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
setTextValue(e.target.value);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{props.pos.type}</span>
|
||||
</div>
|
||||
);
|
||||
case "dropdown":
|
||||
return props.data?.signerObjId === props.signerObjId ? (
|
||||
<select
|
||||
className="inputPlaceholder"
|
||||
id="myDropdown"
|
||||
style={{ fontSize: "12px" }}
|
||||
value={selectOption}
|
||||
onChange={(e) => {
|
||||
setSelectOption(e.target.value);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
>
|
||||
{/* Default/Title option */}
|
||||
<option value="" disabled hidden>
|
||||
{props.pos.options.name}
|
||||
</option>
|
||||
|
||||
{props.pos?.options?.values.map((data, ind) => {
|
||||
return (
|
||||
<option key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
) : (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
>
|
||||
{props.pos?.options?.name ? props.pos.options.name : props.pos.type}
|
||||
</div>
|
||||
);
|
||||
case "initials":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "name":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
tabIndex="0"
|
||||
ref={inputRef}
|
||||
placeholder={"name"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{props.pos.type}</span>
|
||||
</div>
|
||||
);
|
||||
case "company":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"company"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{props.pos.type}</span>
|
||||
</div>
|
||||
);
|
||||
case "job title":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"job title"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{props.pos.type}</span>
|
||||
</div>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<div>
|
||||
<DatePicker
|
||||
disabled={
|
||||
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
closeOnScroll={true}
|
||||
className="inputPlaceholder"
|
||||
style={{ outlineColor: "#007bff" }}
|
||||
selected={
|
||||
startDate
|
||||
? startDate
|
||||
: props.pos.options?.response &&
|
||||
new Date(props.pos.options.response)
|
||||
}
|
||||
onChange={(date) => {
|
||||
setStartDate(date);
|
||||
}}
|
||||
popperPlacement="top-end"
|
||||
customInput={<ExampleCustomInput />}
|
||||
dateFormat={
|
||||
props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "dd MMMM, YYYY"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "image":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "email":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"email"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{props.pos.type}</span>
|
||||
</div>
|
||||
);
|
||||
case "radio":
|
||||
return (
|
||||
<div>
|
||||
{props.pos.options?.values.map((data, ind) => {
|
||||
return (
|
||||
<input
|
||||
key={ind}
|
||||
style={{
|
||||
width: props.pos.Width,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginBottom: "6px",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
type="radio"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
props.pos.options?.isReadOnly &&
|
||||
props.pos.options?.isReadOnly
|
||||
}
|
||||
// disabled={props.isPlaceholder}
|
||||
checked={handleRadioCheck(data)}
|
||||
// checked={isCheckedRadio.isChecked}
|
||||
onChange={(e) => {
|
||||
if (!props.isPlaceholder) {
|
||||
handleCheckRadio(e.target.checked, data);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case "label":
|
||||
return (
|
||||
<textarea
|
||||
rows={1}
|
||||
onChange={(e) => {
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
className="labelTextArea"
|
||||
cols="50"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return props.pos.SignUrl ? (
|
||||
<div style={{ pointerEvents: "none" }}>
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "black",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaceholderType;
|
||||
@@ -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,208 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import "../../styles/signerListPlace.css";
|
||||
import { darkenColor, getFirstLetter } from "../../constant/Utils";
|
||||
|
||||
const RecipientList = (props) => {
|
||||
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();
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
//function for onhover signer name change background color
|
||||
const inputRef = useRef(null);
|
||||
const onHoverStyle = (ind, blockColor) => {
|
||||
const style = {
|
||||
background: blockColor ? blockColor : color[ind % color.length],
|
||||
padding: "10px",
|
||||
marginTop: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
borderBottom: "1px solid #e3e1e1",
|
||||
alignItems: "center"
|
||||
};
|
||||
return style;
|
||||
};
|
||||
//function for onhover signer name remove background color
|
||||
const nonHoverStyle = () => {
|
||||
const style = {
|
||||
padding: "10px",
|
||||
marginTop: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
borderBottom: "1px solid #e3e1e1",
|
||||
alignItems: "center"
|
||||
};
|
||||
return style;
|
||||
};
|
||||
const isWidgetExist = (Id) => {
|
||||
return props.signerPos.some((x) => x.Id === Id);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.signersdata.length > 0 &&
|
||||
props.signersdata.map((obj, ind) => {
|
||||
return (
|
||||
<div
|
||||
data-tut="reactourFirst"
|
||||
onMouseEnter={() => setIsHover(ind)}
|
||||
onMouseLeave={() => setIsHover(null)}
|
||||
key={ind}
|
||||
style={
|
||||
isHover === ind || props.isSelectListId === ind
|
||||
? onHoverStyle(ind, obj.blockColor)
|
||||
: nonHoverStyle(ind)
|
||||
}
|
||||
onClick={() => {
|
||||
props.setSignerObjId(obj?.objectId || "");
|
||||
props.setIsSelectId(ind);
|
||||
props.setContractName(obj?.className || "");
|
||||
props.setUniqueId(obj.Id);
|
||||
props.setRoleName(obj.Role);
|
||||
if (props.handleModal) {
|
||||
props.handleModal();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="signerStyle"
|
||||
style={{
|
||||
background: obj.blockColor
|
||||
? darkenColor(obj.blockColor, 0.4)
|
||||
: nameColor[ind % nameColor.length],
|
||||
width: 30,
|
||||
height: 30,
|
||||
display: "flex",
|
||||
borderRadius: 30 / 2,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: "12px"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
color: "white",
|
||||
textTransform: "uppercase"
|
||||
}}
|
||||
>
|
||||
{isWidgetExist(obj.Id) ? (
|
||||
<i className="fa-solid fa-check"></i>
|
||||
) : (
|
||||
<>
|
||||
{obj.Name
|
||||
? getFirstLetter(obj.Name)
|
||||
: getFirstLetter(obj.Role)}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: obj.Name ? "column" : "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{obj.Name ? (
|
||||
<span className="userName" style={{ cursor: "default" }}>
|
||||
{obj.Name}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span
|
||||
className="userName"
|
||||
onClick={() => {
|
||||
setIsEdit({ [obj.Id]: true });
|
||||
props.setRoleName(obj.Role);
|
||||
}}
|
||||
>
|
||||
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
width: "inherit"
|
||||
}}
|
||||
value={obj.Role}
|
||||
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||
onBlur={() => {
|
||||
setIsEdit({});
|
||||
props.handleOnBlur(obj.Role, obj.Id);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
inputRef.current.blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
obj.Role
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{obj.Name && (
|
||||
<span className="useEmail" style={{ cursor: "default" }}>
|
||||
{obj.Role}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{props.handleDeleteUser && (
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleDeleteUser(obj.Id);
|
||||
}}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<i className="fa-regular fa-trash-can"></i>
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecipientList;
|
||||
@@ -0,0 +1,101 @@
|
||||
import React from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function RenderAllPdfPage({
|
||||
pdfUrl,
|
||||
signPdfUrl,
|
||||
allPages,
|
||||
setAllPages,
|
||||
setPageNumber,
|
||||
setSignBtnPosition,
|
||||
pageNumber
|
||||
}) {
|
||||
//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
|
||||
loading={"Loading 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:
|
||||
pageNumber - 1 === index
|
||||
? "2px solid red"
|
||||
: "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;
|
||||
@@ -0,0 +1,684 @@
|
||||
import React from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
handleImageResize,
|
||||
handleSignYourselfImageResize
|
||||
} from "../../constant/Utils";
|
||||
import EmailToast from "../../primitives/EmailToast";
|
||||
import Placeholder from "./Placeholder";
|
||||
|
||||
function RenderPdf({
|
||||
pageNumber,
|
||||
pdfOriginalWidth,
|
||||
pdfNewWidth,
|
||||
drop,
|
||||
signerPos,
|
||||
successEmail,
|
||||
handleTabDrag,
|
||||
handleStop,
|
||||
isDragging,
|
||||
setIsSignPad,
|
||||
setIsStamp,
|
||||
handleDeleteSign,
|
||||
setSignKey,
|
||||
pdfDetails,
|
||||
xyPostion,
|
||||
pdfUrl,
|
||||
numPages,
|
||||
pageDetails,
|
||||
pdfRequest,
|
||||
setCurrentSigner,
|
||||
signerObjectId,
|
||||
signedSigners,
|
||||
setPdfLoadFail,
|
||||
placeholder,
|
||||
pdfLoadFail,
|
||||
setSignerPos,
|
||||
setXyPostion,
|
||||
index,
|
||||
containerWH,
|
||||
setIsResize,
|
||||
handleLinkUser,
|
||||
setUniqueId,
|
||||
signersdata,
|
||||
setIsPageCopy,
|
||||
setSignerObjId,
|
||||
setShowDropdown,
|
||||
setIsInitial,
|
||||
setIsValidate,
|
||||
setWidgetType,
|
||||
setValidateAlert,
|
||||
setIsRadio,
|
||||
setCurrWidgetsDetails,
|
||||
setSelectWidgetId,
|
||||
selectWidgetId,
|
||||
unSignedWidgetId,
|
||||
setIsCheckbox,
|
||||
handleNameModal
|
||||
}) {
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const newWidth = containerWH.width;
|
||||
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
|
||||
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
// handle signature block width and height according to screen
|
||||
const posWidth = (pos, signYourself) => {
|
||||
const defaultWidth = defaultWidthHeight(pos.type).width;
|
||||
const posWidth = pos.Width ? pos.Width : defaultWidth;
|
||||
let width;
|
||||
if (signYourself) {
|
||||
width = posWidth;
|
||||
return width;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) / scale;
|
||||
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
width = posWidth;
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) * pos.scale;
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const posHeight = (pos, signYourself) => {
|
||||
let height;
|
||||
const posHeight = pos.Height;
|
||||
const defaultHeight = defaultWidthHeight(pos.type).height;
|
||||
|
||||
if (signYourself) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
|
||||
return height;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) / scale;
|
||||
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) * pos.scale;
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const xPos = (pos, signYourself) => {
|
||||
const resizePos = pos.xPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const yPos = (pos, signYourself) => {
|
||||
const resizePos = pos.yPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//function for render placeholder block over pdf document
|
||||
const checkSignedSignes = (data) => {
|
||||
const checkSign = signedSigners.filter(
|
||||
(sign) => sign.objectId === data.signerObjId
|
||||
);
|
||||
if (data.signerObjId === signerObjectId) {
|
||||
setCurrentSigner(true);
|
||||
}
|
||||
const handleAllUserName = (Id, Role, type) => {
|
||||
return pdfDetails[0].Signers.map((signerData, key) => {
|
||||
return (
|
||||
signerData.objectId === data.signerObjId && (
|
||||
<React.Fragment key={key}>
|
||||
{signerData?.Name && (
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{signerData.Name}
|
||||
</div>
|
||||
)}
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
{Role && (
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
checkSign.length === 0 &&
|
||||
data.placeHolder.map((placeData, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setSignKey={setSignKey}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setIsStamp={setIsStamp}
|
||||
handleSignYourselfImageResize={handleImageResize}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
isShowBorder={false}
|
||||
signerObjId={signerObjectId}
|
||||
isShowDropdown={true}
|
||||
isNeedSign={true}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
handleUserName={handleAllUserName}
|
||||
isDragging={false}
|
||||
pdfDetails={pdfDetails}
|
||||
setIsInitial={setIsInitial}
|
||||
setValidateAlert={setValidateAlert}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleUserName = (Id, Role, type) => {
|
||||
if (Id) {
|
||||
const checkSign = signersdata.find((sign) => sign.Id === Id);
|
||||
if (checkSign?.Name) {
|
||||
return (
|
||||
<>
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{checkSign?.Name}
|
||||
</div>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{Role}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{Role}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile && scale ? (
|
||||
<div
|
||||
style={{
|
||||
border: "0.1px solid #ebe8e8",
|
||||
marginTop: isGuestSigner && "30px"
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
<EmailToast isShow={successEmail} />
|
||||
{pdfLoadFail.status &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder // placeholder mobile
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
setPdfLoadFail(true);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
// ref={pdfRef}'
|
||||
onClick={() => {
|
||||
if (setSelectWidgetId) {
|
||||
setSelectWidgetId("");
|
||||
}
|
||||
}}
|
||||
file={
|
||||
pdfUrl
|
||||
? pdfUrl
|
||||
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||
? pdfDetails[0].SignedUrl
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
width={containerWH.width}
|
||||
height={containerWH.height}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<RSC
|
||||
style={{
|
||||
position: "relative",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
width:
|
||||
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
|
||||
height: window.innerHeight - 110 + "px"
|
||||
}}
|
||||
noScrollY={false}
|
||||
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
border: "0.1px solid #ebe8e8",
|
||||
width: pdfOriginalWidth
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
<EmailToast isShow={successEmail} />
|
||||
{pdfLoadFail.status &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={(event, dragElement) =>
|
||||
handleStop(event, dragElement, pos.type)
|
||||
}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
setPdfLoadFail(load);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
onClick={() => {
|
||||
if (setSelectWidgetId) {
|
||||
setSelectWidgetId("");
|
||||
}
|
||||
}}
|
||||
// ref={pdfRef}
|
||||
file={
|
||||
pdfUrl
|
||||
? pdfUrl
|
||||
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||
? pdfDetails[0].SignedUrl
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
</RSC>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default RenderPdf;
|
||||
@@ -0,0 +1,677 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import redPen from "../../assets/images/redPen.png";
|
||||
import bluePen from "../../assets/images/bluePen.png";
|
||||
import blackPen from "../../assets/images/blackPen.png";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function SignPad({
|
||||
isSignPad,
|
||||
isStamp,
|
||||
setIsImageSelect,
|
||||
onSaveSign,
|
||||
setIsSignPad,
|
||||
setImage,
|
||||
isImageSelect,
|
||||
imageRef,
|
||||
onImageChange,
|
||||
onSaveImage,
|
||||
image,
|
||||
defaultSign,
|
||||
setSignature,
|
||||
myInitial,
|
||||
isInitial,
|
||||
setIsInitial,
|
||||
setIsStamp,
|
||||
widgetType
|
||||
}) {
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const allColor = [bluePen, redPen, blackPen];
|
||||
const canvasRef = useRef(null);
|
||||
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
{ value: "Dancing Script" },
|
||||
{ value: "Cedarville Cursive" },
|
||||
{ value: "Delicious Handrawn" }
|
||||
// Add more font options as needed
|
||||
];
|
||||
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
|
||||
|
||||
const senderUser =
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
) &&
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
|
||||
useEffect(() => {
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setFontSelect("Fasthand");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
if (isTab === "draw") {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.clear();
|
||||
} else if (isStamp) {
|
||||
setImage("");
|
||||
}
|
||||
|
||||
setIsSignImg("");
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
}
|
||||
setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = () => {
|
||||
// canvasRef.current.backgroundColor = 'rgb(165, 26, 26)'
|
||||
setSignature(canvasRef.current.toDataURL());
|
||||
setIsSignImg(canvasRef.current.toDataURL());
|
||||
};
|
||||
|
||||
//save button component
|
||||
const SaveBtn = () => {
|
||||
return (
|
||||
<div style={{ marginTop: "2px" }}>
|
||||
{(isTab === "draw" || isTab === "uploadImage") && (
|
||||
<button
|
||||
style={{
|
||||
color: "black",
|
||||
border: "1px solid #ccc"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn saveBtn"
|
||||
onClick={() => handleClear()}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!image) {
|
||||
if (isTab === "mysignature") {
|
||||
setIsSignImg("");
|
||||
if (isInitial) {
|
||||
onSaveSign("initials");
|
||||
} else {
|
||||
onSaveSign("default");
|
||||
}
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(false, textWidth, textHeight);
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveSign();
|
||||
}
|
||||
}
|
||||
|
||||
setPenColor("blue");
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveImage();
|
||||
}
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
disabled={
|
||||
isSignImg || image || isDefaultSign || textWidth ? false : true
|
||||
}
|
||||
type="button"
|
||||
className={
|
||||
isSignImg || image || isDefaultSign || textWidth
|
||||
? "finishBtn saveBtn"
|
||||
: "disabledFinish saveBtn"
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadFont = async () => {
|
||||
try {
|
||||
await document.fonts.load(`20px ${fontSelect}`);
|
||||
const selectFontSTyle = fontOptions.find(
|
||||
(font) => font.value === fontSelect
|
||||
);
|
||||
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
|
||||
} catch (error) {
|
||||
console.error("Error loading font:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadFont();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fontSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
// Load the default signature after the component mounts
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
if (isTab === "type") {
|
||||
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
|
||||
//function for convert input text value in image
|
||||
const convertToImg = async (fontStyle, text) => {
|
||||
//get text content to convert in image
|
||||
const textContent = text;
|
||||
const fontfamily = fontStyle
|
||||
? fontStyle
|
||||
: fontSelect
|
||||
? fontSelect
|
||||
: "Fasthand";
|
||||
|
||||
//creating span for getting text content width
|
||||
const span = document.createElement("span");
|
||||
span.textContent = textContent;
|
||||
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
||||
span.style.display = "hidden";
|
||||
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
||||
|
||||
//create canvas to render text in canvas and convert in image
|
||||
const canvasElement = document.createElement("canvas");
|
||||
// Draw the text content on the canvas
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
const width = span.offsetWidth;
|
||||
const height = span.offsetHeight;
|
||||
setTextWidth(width);
|
||||
setTextHeight(height);
|
||||
const font = span.style["font"];
|
||||
// Set the canvas dimensions to match the span
|
||||
canvasElement.width = width * pixelRatio;
|
||||
canvasElement.height = height * pixelRatio;
|
||||
|
||||
// Render the content of the span onto the canvas
|
||||
// ctx.fillStyle = "white"; // Set the background color of the canvas
|
||||
// ctx.fillRect(0, 0, width*pixelRatio, height*pixelRatio);
|
||||
|
||||
// You can customize text styles if needed
|
||||
ctx.font = font;
|
||||
ctx.fillStyle = "black"; // Set the text color
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.scale(pixelRatio, pixelRatio);
|
||||
// Draw the content of the span onto the canvas
|
||||
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
|
||||
//remove span tag
|
||||
document.body.removeChild(span);
|
||||
// Convert the canvas to image data
|
||||
const dataUrl = canvasElement.toDataURL("image/png");
|
||||
setSignature(dataUrl);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isSignPad && (
|
||||
<div className="modaloverlay">
|
||||
<div className="modalcontainer">
|
||||
<div
|
||||
className="modalheader"
|
||||
style={{ padding: "0 13px", marginTop: "5px" }}
|
||||
>
|
||||
<div className="modaltitle">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
background: "white",
|
||||
marginTop: "3px"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-around",
|
||||
alignItems: "end",
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
{isStamp ? (
|
||||
<span style={{ color: themeColor }} className="signTab">
|
||||
{widgetType === "image"
|
||||
? "Upload image"
|
||||
: "Upload stamp image"}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color: isTab === "draw" ? themeColor : "#515252",
|
||||
|
||||
marginLeft: "2px"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
Draw
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "draw"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
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>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color: isTab === "type" ? themeColor : "#515252",
|
||||
|
||||
marginLeft: "2px"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
Type
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "type"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
{!isInitial && defaultSign ? (
|
||||
<div>
|
||||
<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>
|
||||
) : (
|
||||
isInitial &&
|
||||
myInitial && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
isTab === "mysignature"
|
||||
? themeColor
|
||||
: "#515252"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
My Initials
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "mysignature"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="closebtn"
|
||||
style={{ color: "black" }}
|
||||
onClick={() => {
|
||||
setPenColor("blue");
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "rgb(255, 255, 255)",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
src={isInitial ? myInitial : defaultSign}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : isImageSelect || isStamp ? (
|
||||
!image ? (
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
onClick={() => imageRef.current.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="uploadImg">Upload</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
marginBottom: 6,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
className={
|
||||
isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={imageRef}
|
||||
src={image.src}
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: "flex", justifyContent: "flex-end" }}
|
||||
>
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<span className="signatureText">
|
||||
{isInitial ? "Initials" : "Signature"}:
|
||||
</span>
|
||||
|
||||
<input
|
||||
maxLength={isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect }}
|
||||
type="text"
|
||||
className="signatureInput"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="fontOptionContainer">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value && "rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px"
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "10px"
|
||||
}}
|
||||
>
|
||||
<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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignPad;
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import "../../styles/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", background: "white" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
padding: "10px",
|
||||
|
||||
background: "#93a3db"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="signerStyle"
|
||||
style={{
|
||||
background: "#abd1d0",
|
||||
width: 20,
|
||||
height: 20,
|
||||
display: "flex",
|
||||
borderRadius: 30 / 2,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: "20px",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "8px",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold"
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
|
||||
</span>
|
||||
</div>
|
||||
<div 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,36 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import "../../styles/signerListPlace.css";
|
||||
import RecipientList from "./RecipientList";
|
||||
|
||||
function SignerListPlace(props) {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
padding: "5px"
|
||||
}}
|
||||
>
|
||||
<span className="signedStyle">
|
||||
{props.title ? props.title : "Recipients"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="signerList">
|
||||
<RecipientList {...props} />
|
||||
</div>
|
||||
{props.handleAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="addSignerBtn"
|
||||
onClick={() => props.handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignerListPlace;
|
||||
@@ -0,0 +1,420 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import RecipientList from "./RecipientList";
|
||||
import { useDrag } from "react-dnd";
|
||||
import WidgetList from "./WidgetList";
|
||||
import { widgets } from "../../constant/Utils";
|
||||
|
||||
function WidgetComponent({
|
||||
dragSignature,
|
||||
signRef,
|
||||
handleDivClick,
|
||||
handleMouseLeave,
|
||||
themeColor,
|
||||
dragStamp,
|
||||
isSignYourself,
|
||||
addPositionOfSignature,
|
||||
signersdata,
|
||||
isSelectListId,
|
||||
setSignerObjId,
|
||||
setIsSelectId,
|
||||
setContractName,
|
||||
isSigners,
|
||||
dataTut,
|
||||
isMailSend,
|
||||
handleAddSigner,
|
||||
setUniqueId,
|
||||
setRoleName,
|
||||
handleDeleteUser,
|
||||
signerPos,
|
||||
handleRoleChange,
|
||||
handleOnBlur,
|
||||
title,
|
||||
|
||||
isTemplateFlow
|
||||
}) {
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 5,
|
||||
text: "dropdown"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDropdown: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, checkbox] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 6,
|
||||
text: "checkbox"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCheck: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, text] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 7,
|
||||
text: "text"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragText: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, initials] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 8,
|
||||
text: "initials"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragInitial: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, name] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 9,
|
||||
text: "name"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragName: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, company] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 10,
|
||||
text: "company"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCompany: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, jobTitle] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 11,
|
||||
text: "job title"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragJobtitle: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, date] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 12,
|
||||
text: "date"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDate: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, image] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 13,
|
||||
text: "image"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragImage: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, email] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 14,
|
||||
text: "email"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragEmail: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, radio] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 15,
|
||||
text: "radio"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragRadio: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, label] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 16,
|
||||
text: "label"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragLabel: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [widget, setWidget] = useState([]);
|
||||
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const handleModal = () => {
|
||||
setIsSignersModal(!isSignersModal);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const widgetRef = [
|
||||
dragSignature,
|
||||
dragStamp,
|
||||
initials,
|
||||
label,
|
||||
text,
|
||||
checkbox,
|
||||
dropdown,
|
||||
radio,
|
||||
image,
|
||||
date,
|
||||
name,
|
||||
email,
|
||||
company,
|
||||
jobTitle
|
||||
];
|
||||
const getWidgetArray = widgets;
|
||||
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
||||
return { ...obj, ref: widgetRef[ind] };
|
||||
});
|
||||
|
||||
setWidget(newUpdateSigner);
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const filterWidgets = widget.filter(
|
||||
(data) =>
|
||||
data.type !== "dropdown" && data.type !== "radio" && data.type !== "label"
|
||||
);
|
||||
const labelWidget = widget.filter((data) => data.type !== "label");
|
||||
const updateWidgets = isSignYourself
|
||||
? filterWidgets
|
||||
: isTemplateFlow
|
||||
? labelWidget
|
||||
: widget;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
!isMailSend && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
id="navbar"
|
||||
className="stickyfooter"
|
||||
style={{ width: window.innerWidth + "px" }}
|
||||
>
|
||||
{isSigners && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
style={{
|
||||
background: isSelectListId
|
||||
? color[isSelectListId % color.length]
|
||||
: color[0],
|
||||
padding: "10px 20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
onClick={() => {
|
||||
// if (signersdata?.length) {
|
||||
handleModal();
|
||||
// }
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "700",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{title ? title : "Recipient"}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "700",
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{signersdata[isSelectListId]?.Role && (
|
||||
<div>
|
||||
{signersdata[isSelectListId]?.Name ? (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Name?.length > 12
|
||||
? `${signersdata[isSelectListId].Name.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Name}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Role?.length > 12
|
||||
? `${signersdata[isSelectListId].Role.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Role}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginLeft: 6, fontSize: 16 }}>
|
||||
<i className="fa-solid fa-angle-down"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{handleAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
style={{
|
||||
margin: "5px 0 5px 0",
|
||||
backgroundColor: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="addSignerBtn"
|
||||
onClick={() => handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
style={{
|
||||
background: "white",
|
||||
borderTop: `2px solid ${themeColor}`
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
overflowX: "scroll",
|
||||
whiteSpace: "nowrap",
|
||||
padding: "10px"
|
||||
}}
|
||||
>
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
signRef={signRef}
|
||||
marginLeft={5}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div data-tut={dataTut} className="signerComponent">
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
padding: "5px"
|
||||
}}
|
||||
>
|
||||
<span className="signedStyle">Fields</span>
|
||||
</div>
|
||||
|
||||
<div className="signLayoutContainer1">
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
signRef={signRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isSignersModal && (
|
||||
<ModalUi
|
||||
title={"Roles"}
|
||||
isOpen={isSignersModal}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
{signersdata.length > 0 ? (
|
||||
<RecipientList
|
||||
signerPos={signerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
setSignerObjId={setSignerObjId}
|
||||
setIsSelectId={setIsSelectId}
|
||||
setContractName={setContractName}
|
||||
setUniqueId={setUniqueId}
|
||||
setRoleName={setRoleName}
|
||||
handleDeleteUser={handleDeleteUser}
|
||||
handleRoleChange={handleRoleChange}
|
||||
handleOnBlur={handleOnBlur}
|
||||
handleModal={handleModal}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
fontSize: 15,
|
||||
fontWeight: "500",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
Please add a role
|
||||
</div>
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default WidgetComponent;
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import { getWidgetType } from "../../constant/Utils";
|
||||
|
||||
function WidgetList(props) {
|
||||
return props.updateWidgets.map((item, ind) => {
|
||||
return (
|
||||
<div key={ind} style={{ marginBottom: "10px" }}>
|
||||
<div
|
||||
className="widgets"
|
||||
onClick={() => {
|
||||
props.addPositionOfSignature &&
|
||||
props.addPositionOfSignature("onclick", item);
|
||||
}}
|
||||
ref={(element) => {
|
||||
if (!props.isMobile) {
|
||||
item.ref(element);
|
||||
if (element) {
|
||||
if (props?.signRef) {
|
||||
props.signRef.current = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
onMouseMove={props?.handleDivClick}
|
||||
onMouseDown={() => {
|
||||
props?.handleMouseLeave();
|
||||
}}
|
||||
>
|
||||
{item.ref && getWidgetType(item, props?.marginLeft)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default WidgetList;
|
||||
@@ -0,0 +1,284 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
status: "required",
|
||||
hint: "",
|
||||
textvalidate: "text"
|
||||
});
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const statusArr = ["Required", "Optional"];
|
||||
const inputOpt = ["email", "number", "text"];
|
||||
|
||||
useEffect(() => {
|
||||
if (props.defaultdata) {
|
||||
setFormdata({
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || "",
|
||||
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||
status: props.defaultdata?.options?.status || "required",
|
||||
hint: props.defaultdata?.options?.hint || "",
|
||||
textvalidate:
|
||||
props.defaultdata?.options?.validation?.type === "regex"
|
||||
? props.defaultdata?.options?.validation?.pattern
|
||||
: props.defaultdata?.options?.validation?.type || ""
|
||||
});
|
||||
} else {
|
||||
setFormdata({
|
||||
...formdata,
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || ""
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.defaultdata, props.widgetName]);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
props.handleData(formdata);
|
||||
setFormdata({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
status: "required",
|
||||
hint: "",
|
||||
textvalidate: "text"
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handledefaultChange = (e) => {
|
||||
if (formdata.textvalidate) {
|
||||
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
|
||||
const isValidate = regexObject?.test(e.target.value);
|
||||
setIsValid(isValidate);
|
||||
} else {
|
||||
setIsValid(true);
|
||||
}
|
||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
function handleValidation(type) {
|
||||
switch (type) {
|
||||
case "email":
|
||||
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||
case "number":
|
||||
return "/^\\d+$/";
|
||||
case "text":
|
||||
return "/^[a-zA-Zs]+$/";
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function handleBlurRegex() {
|
||||
if (!formdata.textvalidate) {
|
||||
setFormdata({ ...formdata, textvalidate: "text" });
|
||||
} else {
|
||||
if (formdata.defaultValue) {
|
||||
const regexObject = RegexParser(
|
||||
handleValidation(formdata.textvalidate)
|
||||
);
|
||||
const isValidate = regexObject?.test(formdata.defaultValue);
|
||||
if (isValidate === false) {
|
||||
setFormdata({ ...formdata, defaultValue: "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
handleClose={props.handleClose && props.handleClose}
|
||||
title={"Widget info"}
|
||||
>
|
||||
<form onSubmit={handleSubmit} style={{ padding: 20 }}>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{(props.defaultdata?.type === "text" ||
|
||||
props.widgetName === "text") && (
|
||||
<>
|
||||
<div className="form-section">
|
||||
<label htmlFor="textvalidate" style={{ fontSize: 13 }}>
|
||||
Validation
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginBottom: "0.5rem"
|
||||
}}
|
||||
>
|
||||
<div style={{ width: "100%", position: "relative" }}>
|
||||
<input
|
||||
style={{
|
||||
padding: "0.5rem 0.75rem",
|
||||
width: "92%",
|
||||
borderWidth: "1px",
|
||||
borderColor: "#d1d5db",
|
||||
borderTopLeftRadius: "0.375rem",
|
||||
borderBottomLeftRadius: "0.375rem",
|
||||
outline: "none",
|
||||
fontSize: "0.75rem",
|
||||
position: "relative",
|
||||
zIndex: 2
|
||||
}}
|
||||
name="textvalidate"
|
||||
placeholder="Enter custom expression"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onBlur={() => handleBlurRegex()}
|
||||
/>
|
||||
<select
|
||||
style={{ position: "absolute", left: 0, zIndex: 1 }}
|
||||
className="addUserInput"
|
||||
name="textvalidate"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onBlur={() => handleBlurRegex()}
|
||||
>
|
||||
<option disabled style={{ fontSize: "13px" }}>
|
||||
Select...
|
||||
</option>
|
||||
{inputOpt.map((data, ind) => {
|
||||
return (
|
||||
<option
|
||||
style={{ fontSize: "13px" }}
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Default value
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="defaultValue"
|
||||
value={formdata.defaultValue}
|
||||
onChange={(e) => handledefaultChange(e)}
|
||||
onBlur={() =>
|
||||
isValid === false &&
|
||||
setFormdata({ ...formdata, defaultValue: "" })
|
||||
}
|
||||
/>
|
||||
{isValid === false ? (
|
||||
<p style={{ color: "Red", fontSize: 8 }}>
|
||||
invalid default value
|
||||
</p>
|
||||
) : (
|
||||
<p
|
||||
style={{
|
||||
color: "transparent",
|
||||
fontSize: 10,
|
||||
margin: "3px 8px"
|
||||
}}
|
||||
>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="form-section">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginBottom: "0.5rem"
|
||||
}}
|
||||
>
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 5,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<input
|
||||
style={{ accentColor: "red", marginRight: "10px" }}
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
||||
{data}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{(props.defaultdata?.type === "text" ||
|
||||
props?.widgetName === "text") && (
|
||||
<div className="form-section">
|
||||
<label htmlFor="hint" style={{ fontSize: 13 }}>
|
||||
Hint
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="hint"
|
||||
value={formdata.hint}
|
||||
onChange={(e) => handleChange(e)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: "#b7b3b3",
|
||||
width: "100%",
|
||||
marginBottom: "16px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
color: "white",
|
||||
padding: "5px 20px",
|
||||
backgroundColor: "#32a3ac"
|
||||
}}
|
||||
type="submit"
|
||||
className="finishBtn"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetNameModal;
|
||||
@@ -2,20 +2,24 @@ import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||
return (
|
||||
<li key={item.title} role="none">
|
||||
<NavLink
|
||||
to={`/${item.pageType}/${item.objectId}`}
|
||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
|
||||
onClick={closeSidebar}
|
||||
tabIndex={isOpen ? 0 : -1}
|
||||
role="menuitem"
|
||||
>
|
||||
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<span className="ml-3 lg:ml-4">{item.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<li key={item.title} role="none">
|
||||
<NavLink
|
||||
to={
|
||||
item.pageType
|
||||
? `/${item.pageType}/${item.objectId}`
|
||||
: `/${item.objectId}`
|
||||
}
|
||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
|
||||
onClick={closeSidebar}
|
||||
tabIndex={isOpen ? 0 : -1}
|
||||
role="menuitem"
|
||||
>
|
||||
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<span className="ml-3 lg:ml-4">{item.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
export default Menu;
|
||||
|
||||
@@ -22,8 +22,9 @@ const Submenu = ({ item, closeSidebar }) => {
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<span className="ml-3 lg:ml-4">{title}</span>
|
||||
<i
|
||||
className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
||||
}`}
|
||||
className={`${
|
||||
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
||||
}`}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
@@ -33,13 +34,20 @@ const Submenu = ({ item, closeSidebar }) => {
|
||||
{children.map((childItem) => (
|
||||
<li key={childItem.title} role="none">
|
||||
<NavLink
|
||||
to={`/${childItem.pageType}/${childItem.objectId}`}
|
||||
to={
|
||||
childItem.pageType
|
||||
? `/${childItem.pageType}/${childItem.objectId}`
|
||||
: `/${childItem.objectId}`
|
||||
}
|
||||
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
|
||||
onClick={closeSidebar}
|
||||
role="menuitem"
|
||||
tabIndex={submenuOpen ? 0 : -1}
|
||||
>
|
||||
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<i
|
||||
className={`${childItem.icon} text-[18px]`}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="ml-3 lg:ml-4">{childItem.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
@@ -3,7 +3,9 @@ export const appInfo = {
|
||||
appTitle: "contracts",
|
||||
applogo: logo,
|
||||
appname: "contracts",
|
||||
baseurl: process.env.REACT_APP_SERVERURL,
|
||||
baseurl: process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app",
|
||||
defaultRole: "contracts_User",
|
||||
fbAppId: process.env.REACT_APP_FBAPPID
|
||||
? `${process.env.REACT_APP_FBAPPID}`
|
||||
|
||||
@@ -7,3 +7,5 @@ export const submitBtn =
|
||||
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
|
||||
export const modalSubmitBtnColor = "#17a2b8";
|
||||
export const modalCancelBtnColor = "white";
|
||||
export const themeColor = "#47a3ad";
|
||||
export const iconColor = "#686968";
|
||||
|
||||
@@ -1 +1,62 @@
|
||||
import("./Bootstrap");
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
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 "./components/pdf/DragElement";
|
||||
import TagManager from "react-gtm-module";
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.REACT_APP_GTM) {
|
||||
const tagManagerArgs = {
|
||||
gtmId: process.env.REACT_APP_GTM
|
||||
};
|
||||
TagManager.initialize(tagManagerArgs);
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -16,8 +16,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -32,8 +31,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -48,8 +46,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -64,8 +61,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -80,8 +76,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -96,8 +91,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -112,8 +106,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -128,8 +121,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -144,8 +136,7 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -175,16 +166,14 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/placeHolderSign"
|
||||
redirectUrl: "placeHolderSign"
|
||||
},
|
||||
{
|
||||
btnLabel: "Edit",
|
||||
btnColor: "#00c9d5",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/template"
|
||||
redirectUrl: "template"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "./themeColor";
|
||||
|
||||
function DragElement(item) {
|
||||
const getWidgets = [
|
||||
{
|
||||
type: "signature",
|
||||
icon: "fa-solid fa-pen-nib",
|
||||
iconSize: "20px"
|
||||
},
|
||||
{
|
||||
type: "stamp",
|
||||
icon: "fa-solid fa-stamp",
|
||||
iconSize: "19px"
|
||||
},
|
||||
{
|
||||
type: "dropdown",
|
||||
icon: "fa-solid fa-circle-chevron-down",
|
||||
iconSize: "19px"
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
icon: "fa-solid fa-square-check",
|
||||
iconSize: "22px"
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
icon: "fa-solid fa-font",
|
||||
iconSize: "21px"
|
||||
},
|
||||
{
|
||||
type: "initials",
|
||||
icon: "fa-solid fa-signature",
|
||||
iconSize: "15px"
|
||||
},
|
||||
{
|
||||
type: "name",
|
||||
icon: "fa-solid fa-user",
|
||||
iconSize: "21px"
|
||||
},
|
||||
{
|
||||
type: "company",
|
||||
icon: "fa-solid fa-building",
|
||||
iconSize: "24px"
|
||||
},
|
||||
{
|
||||
type: "job title",
|
||||
icon: "fa-solid fa-address-card",
|
||||
iconSize: "16px"
|
||||
},
|
||||
{
|
||||
type: "date",
|
||||
icon: "fa-solid fa-calendar-days",
|
||||
iconSize: "19px"
|
||||
}
|
||||
];
|
||||
|
||||
const filterWidgetPreview = getWidgets.filter(
|
||||
(data) => data.type === item?.text
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="signatureBtn"
|
||||
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)",
|
||||
marginLeft: "5px"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginLeft: "5px"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-sharp fa-solid fa-grip-vertical"
|
||||
style={{ color: "#908d8d", fontSize: "13px" }}
|
||||
></i>
|
||||
<span
|
||||
style={{
|
||||
fontWeight: "400",
|
||||
fontSize: "15px",
|
||||
// padding: "3px 20px 0px 20px",
|
||||
color: "black",
|
||||
marginLeft: "5px"
|
||||
}}
|
||||
>
|
||||
{filterWidgetPreview[0].type}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: themeColor(),
|
||||
padding: "0 5px",
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
style={{ color: "white", fontSize: filterWidgetPreview[0].iconSize }}
|
||||
className={filterWidgetPreview[0].icon}
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DragElement;
|
||||
@@ -1,3 +0,0 @@
|
||||
export const themeColor = () => {
|
||||
return "#47a3ad";
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import React from "react";
|
||||
import ModalUi from "./ModalUi";
|
||||
|
||||
const AddRoleModal = (props) => {
|
||||
return (
|
||||
<ModalUi
|
||||
title={"Add Role"}
|
||||
isOpen={props.isModalRole}
|
||||
handleClose={props.handleCloseRoleModal}
|
||||
>
|
||||
<div className="addusercontainer">
|
||||
<form
|
||||
style={{ display: "flex", flexDirection: "column" }}
|
||||
onSubmit={props.handleAddRole}
|
||||
>
|
||||
<input
|
||||
value={props.roleName}
|
||||
onChange={(e) => props.setRoleName(e.target.value)}
|
||||
placeholder={
|
||||
props.signersdata.length > 0
|
||||
? "User " + (props.signersdata.length + 1)
|
||||
: "User 1"
|
||||
}
|
||||
className="addUserInput"
|
||||
/>
|
||||
<p
|
||||
style={{
|
||||
color: "grey",
|
||||
fontSize: 11,
|
||||
margin: "10px 0 10px 5px"
|
||||
}}
|
||||
>
|
||||
e.g: Hr, Director, Manager, New joinee, Accountant, etc...
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
background: "#00a2b7"
|
||||
}}
|
||||
className="finishBtn"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
onClick={props.handleCloseRoleModal}
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddRoleModal;
|
||||
@@ -0,0 +1,293 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import "../styles/AddUser.css";
|
||||
const AddUser = (props) => {
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [addYourself, setAddYourself] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
}, []);
|
||||
// Load user details from localStorage when the component mounts
|
||||
useEffect(() => {
|
||||
const savedUserDetails = JSON.parse(
|
||||
localStorage.getItem("UserInformation")
|
||||
);
|
||||
if (savedUserDetails && addYourself) {
|
||||
setName(savedUserDetails.name);
|
||||
setPhone(savedUserDetails.phone);
|
||||
setEmail(savedUserDetails.email);
|
||||
}
|
||||
}, [addYourself]);
|
||||
|
||||
const checkUserExist = async () => {
|
||||
const user = Parse.User.current();
|
||||
try {
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", user.getEmail());
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
setIsUserExist(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
contactQuery.set("Phone", phone);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("phone", phone);
|
||||
_user.set("password", phone);
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_Guest",
|
||||
userId: user.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
props.details(parseData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_Guest",
|
||||
userId: userRes.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details(parseData);
|
||||
}
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log("err", err);
|
||||
setIsLoader(false);
|
||||
alert("something went wrong!");
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleAddYourselfChange = () => {
|
||||
if (addYourself) {
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
} else {
|
||||
setAddYourself(true);
|
||||
}
|
||||
};
|
||||
const handleReset = () => {
|
||||
setAddYourself(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addusercontainer">
|
||||
{isLoader && (
|
||||
<div className="loaderdiv">
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-wrapper">
|
||||
<div style={{ fontSize: 14, fontWeight: "700" }}>Add User</div>
|
||||
|
||||
{isUserExist && (
|
||||
<div className="form-section">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="addYourself"
|
||||
checked={addYourself}
|
||||
onChange={handleAddYourselfChange}
|
||||
className="form-checkbox"
|
||||
/>
|
||||
<label htmlFor="addYourself" className="checkbox-label ">
|
||||
Add Yourself
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="email" style={{ fontSize: 13 }}>
|
||||
Email
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="phone" style={{ fontSize: 13 }}>
|
||||
Phone
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="buttoncontainer">
|
||||
<button type="submit" className="submitbutton">
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="resetbutton"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUser;
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import "../styles/signature.css";
|
||||
|
||||
function EmailToast({ isShow }) {
|
||||
return (
|
||||
<>
|
||||
{isShow && (
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<div
|
||||
className="alert alert-success successBox"
|
||||
style={{ zIndex: "1051" }}
|
||||
>
|
||||
Email sent successfully!
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmailToast;
|
||||
@@ -67,7 +67,7 @@ const ReportTable = ({
|
||||
const handlemicroapp = async (item, url, btnLabel) => {
|
||||
if (ReportName === "Templates") {
|
||||
if (btnLabel === "Edit") {
|
||||
navigate(`/asmf/${url}/${item.objectId}`);
|
||||
navigate(`/${url}/${item.objectId}`);
|
||||
} else {
|
||||
setActLoader({ [`${item.objectId}_${btnLabel}`]: true });
|
||||
try {
|
||||
@@ -149,7 +149,7 @@ const ReportTable = ({
|
||||
if (res.data && res.data.objectId) {
|
||||
setActLoader({});
|
||||
setIsAlert(true);
|
||||
navigate(`/asmf/${url}/${res.data.objectId}`, {
|
||||
navigate(`/${url}/${res.data.objectId}`, {
|
||||
state: { title: "Use Template" }
|
||||
});
|
||||
}
|
||||
@@ -177,7 +177,7 @@ const ReportTable = ({
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem("rowlevel");
|
||||
navigate("/rpmf/" + url);
|
||||
navigate(`/${url}`);
|
||||
localStorage.setItem("rowlevel", JSON.stringify(item));
|
||||
}
|
||||
|
||||
@@ -211,8 +211,10 @@ const ReportTable = ({
|
||||
setIsDeleteModal({});
|
||||
setActLoader({ [item.objectId]: true });
|
||||
try {
|
||||
const url =
|
||||
process.env.REACT_APP_SERVERURL + "/classes/contracts_Contactbook/";
|
||||
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
const url = serverUrl + "/classes/contracts_Contactbook/";
|
||||
const body = { IsDeleted: true };
|
||||
const res = await axios.put(url + item.objectId, body, {
|
||||
headers: {
|
||||
|
||||
@@ -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,47 @@
|
||||
import React from "react";
|
||||
import SelectSigners from "./SelectSigners";
|
||||
import AddUser from "./AddUser";
|
||||
import ModalUi from "./ModalUi";
|
||||
|
||||
const LinkUserModal = (props) => {
|
||||
return (
|
||||
<ModalUi
|
||||
title={"Add/Choose Signer"}
|
||||
isOpen={props.isAddUser[props.uniqueId]}
|
||||
handleClose={props.closePopup}
|
||||
>
|
||||
<SelectSigners
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 5,
|
||||
margin: "0 30%",
|
||||
color: "#808080"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
height: 1,
|
||||
width: "100%",
|
||||
backgroundColor: "#ccc"
|
||||
}}
|
||||
></span>
|
||||
<span>or</span>
|
||||
<span
|
||||
style={{
|
||||
height: 1,
|
||||
width: "100%",
|
||||
backgroundColor: "#ccc"
|
||||
}}
|
||||
></span>
|
||||
</div>
|
||||
<AddUser details={props.handleAddUser} closePopup={props.closePopup} />
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinkUserModal;
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
const ModalUi = ({
|
||||
children,
|
||||
title,
|
||||
@@ -6,13 +7,20 @@ const ModalUi = ({
|
||||
headColor,
|
||||
handleClose,
|
||||
showHeader = true,
|
||||
showClose = true
|
||||
showClose = true,
|
||||
styleClass
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{isOpen && (
|
||||
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
||||
<div className="fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] md:min-w-[500px] overflow-y-auto hide-scrollbar">
|
||||
<div
|
||||
className={
|
||||
styleClass
|
||||
? styleClass
|
||||
: "fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] md:min-w-[500px] overflow-y-auto hide-scrollbar"
|
||||
}
|
||||
>
|
||||
{showHeader && (
|
||||
<div
|
||||
className="flex justify-between rounded-t items-center py-[15px] px-[20px] text-white"
|
||||
|
||||
@@ -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,65 @@
|
||||
import React from "react";
|
||||
import "../styles/customModal.css";
|
||||
import "../styles/signature.css";
|
||||
|
||||
function CustomModal({
|
||||
show,
|
||||
headMsg,
|
||||
bodyMssg,
|
||||
footerMessage,
|
||||
declineDoc,
|
||||
setIsDecline,
|
||||
containerWH
|
||||
}) {
|
||||
const isMobile = window.innerWidth < 767;
|
||||
return (
|
||||
show && (
|
||||
<div
|
||||
className="parentDiv"
|
||||
style={{
|
||||
width: containerWH && containerWH.width,
|
||||
height: isMobile ? "100%" : containerWH && containerWH.height
|
||||
}}
|
||||
>
|
||||
<div className="childDiv">
|
||||
<div className="modalHeadDiv bg-danger">{headMsg && headMsg}</div>
|
||||
<div className="modalBodyDIv">
|
||||
<p className="pTagBody">{bodyMssg && bodyMssg}</p>
|
||||
</div>
|
||||
{footerMessage && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%"
|
||||
}}
|
||||
></div>
|
||||
<div className="modalFooterDiv">
|
||||
<button
|
||||
style={{
|
||||
background: "#de4337"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn"
|
||||
onClick={() => declineDoc()}
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => setIsDecline({ isDeclined: false })}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomModal;
|
||||
@@ -0,0 +1,108 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import "../styles/AddUser.css";
|
||||
import AsyncSelect from "react-select/async";
|
||||
|
||||
const customStyles = {
|
||||
control: (provided) => ({
|
||||
...provided,
|
||||
fontSize: "13px" // Font size for the control
|
||||
}),
|
||||
option: (provided) => ({
|
||||
...provided,
|
||||
fontSize: "13px" // Font size for the options
|
||||
})
|
||||
};
|
||||
const SelectSigners = (props) => {
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [selected, setSelected] = useState();
|
||||
const [userData, setUserData] = useState({});
|
||||
const [isError, setIsError] = useState(false);
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
|
||||
// `handleOptions` is used to set just save from quick form to selected option in dropdown
|
||||
const handleOptions = (item) => {
|
||||
setSelected(item);
|
||||
const userData = userList.find((x) => x.objectId === item.value);
|
||||
if (userData) {
|
||||
setUserData(userData);
|
||||
}
|
||||
};
|
||||
const handleAdd = () => {
|
||||
if (userData && userData.objectId) {
|
||||
props.details(userData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
} else {
|
||||
setIsError(true);
|
||||
setTimeout(() => setIsError(false), 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (inputValue.length > 1) {
|
||||
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||
}
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
// console.log("userList ", res);
|
||||
setUserList(res);
|
||||
return await res.map((item) => ({
|
||||
label: item.Email,
|
||||
value: item.objectId
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addusercontainer">
|
||||
<div className="form-wrapper">
|
||||
<div className="form-section" style={{ marginBottom: 0 }}>
|
||||
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
|
||||
<AsyncSelect
|
||||
cacheOptions
|
||||
defaultOptions
|
||||
value={selected}
|
||||
loadingMessage={() => "Loading..."}
|
||||
noOptionsMessage={() => "User not Found"}
|
||||
loadOptions={loadOptions}
|
||||
onChange={handleOptions}
|
||||
styles={customStyles}
|
||||
/>
|
||||
</div>
|
||||
{isError ? (
|
||||
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
|
||||
Please select signer
|
||||
</p>
|
||||
) : (
|
||||
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
<button className="submitbutton" onClick={() => handleAdd()}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectSigners;
|
||||
@@ -0,0 +1,34 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default function TourContentWithBtn({ message, isChecked }) {
|
||||
const [isCheck, setIsCheck] = useState(false);
|
||||
|
||||
const handleCheck = () => {
|
||||
setIsCheck(!isCheck);
|
||||
if (isChecked) {
|
||||
isChecked(!isCheck);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<p>{message}</p>
|
||||
<label
|
||||
style={{
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{ marginRight: 3 }}
|
||||
checked={isCheck}
|
||||
onChange={handleCheck}
|
||||
/>{" "}
|
||||
<span style={{ color: "#787878", fontSize: 12 }}>
|
||||
Don't show this again
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import "../styles/signature.css";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import RenderDebugPdf from "../components/RenderDebugPdf";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Alert from "../primitives/Alert";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
|
||||
function processDimensions(x, y, width, height) {
|
||||
if (width < 0) {
|
||||
x -= Math.abs(width);
|
||||
width = Math.abs(width);
|
||||
}
|
||||
|
||||
if (height < 0) {
|
||||
y -= Math.abs(height);
|
||||
height = Math.abs(height);
|
||||
}
|
||||
|
||||
return {
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
width: Math.floor(width),
|
||||
height: Math.floor(height)
|
||||
};
|
||||
}
|
||||
const DebugPdf = () => {
|
||||
const { width } = useWindowSize();
|
||||
const [pdf, setPdf] = useState("");
|
||||
const [isModal, setIsModal] = useState(true);
|
||||
const [allPages, setAllPages] = useState(null);
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||
status: false,
|
||||
type: "load"
|
||||
});
|
||||
const [pdfDetails, setPdfDetails] = useState({
|
||||
name: "",
|
||||
pdftype: "",
|
||||
totalPages: "",
|
||||
currentPage: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
base64: ""
|
||||
});
|
||||
const [hoverCoordinates, setHoverCoordinates] = useState({ x: 0, y: 0 });
|
||||
const [pdfDimension, setPdfDimension] = useState({ width: 0, height: 0 });
|
||||
const [annotations, setAnnotations] = useState([]);
|
||||
const [newAnnotation, setNewAnnotation] = useState([]);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (pdf && pdf.name) {
|
||||
const fetchPdfMetadata = async () => {
|
||||
try {
|
||||
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
|
||||
console.log("pdfDataURL ", pdfDataURL);
|
||||
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
|
||||
const pdfType = await inferPdfType(pdfInfo);
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
pdftype: pdfType
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error fetching PDF metadata:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPdfMetadata();
|
||||
}
|
||||
}, [pdf]);
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(1);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
|
||||
// Assuming a standard DPI of 72, you can adjust this value if needed
|
||||
const dpi = 72;
|
||||
|
||||
const widthInInches = width / dpi;
|
||||
const heightInInches = height / dpi;
|
||||
|
||||
const isA1 = widthInInches > 23.39 && heightInInches > 16.54;
|
||||
const isA2 = widthInInches > 16.54 && heightInInches > 11.69;
|
||||
const isA3 = widthInInches > 11.69 && heightInInches > 8.27;
|
||||
const isA4 = widthInInches > 8.27 && heightInInches > 5.83;
|
||||
const isLegal = widthInInches > 8.5 && heightInInches > 14;
|
||||
const isLetter = widthInInches > 8.5 && heightInInches > 11;
|
||||
const isLedger = widthInInches > 11 && heightInInches > 17;
|
||||
|
||||
if (isA1) return "A1";
|
||||
if (isA2) return "A2";
|
||||
if (isA3) return "A3";
|
||||
if (isA4) return "A4";
|
||||
if (isLegal) return "Legal";
|
||||
if (isLetter) return "Letter";
|
||||
if (isLedger) return "Ledger";
|
||||
|
||||
return "Unknown";
|
||||
} catch (error) {
|
||||
console.error("Error getting page dimensions:", error);
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
setIsModal(false);
|
||||
setPdfDetails((prevData) => ({ ...prevData, name: pdf?.name }));
|
||||
};
|
||||
|
||||
//function for get pdf page details
|
||||
const pageDetails = async ({ numPages }) => {
|
||||
const load = {
|
||||
status: true
|
||||
};
|
||||
setPdfDetails((prevDetails) => ({ ...prevDetails, totalPages: numPages }));
|
||||
setPdfLoadFail(load);
|
||||
};
|
||||
|
||||
const handleMouseMoveDiv = (event) => {
|
||||
setHoverCoordinates({
|
||||
x: event.nativeEvent.offsetX,
|
||||
y: event.nativeEvent.offsetY
|
||||
});
|
||||
};
|
||||
const handleMouseDown = (event) => {
|
||||
if (newAnnotation.length === 0) {
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
setNewAnnotation([
|
||||
{ x, y, width: 0, height: 0, key: "0", page: pageNumber }
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (event) => {
|
||||
if (newAnnotation.length === 1) {
|
||||
const sx = newAnnotation[0].x;
|
||||
const sy = newAnnotation[0].y;
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
const result = processDimensions(sx, sy, x - sx, y - sy);
|
||||
const annotationToAdd = {
|
||||
...result,
|
||||
key: annotations.length + 1,
|
||||
page: pageNumber
|
||||
};
|
||||
annotations.push(annotationToAdd);
|
||||
setNewAnnotation([]);
|
||||
setAnnotations(annotations);
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
x: result.x,
|
||||
y: result.y
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
if (newAnnotation.length === 1) {
|
||||
const sx = newAnnotation[0].x;
|
||||
const sy = newAnnotation[0].y;
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
setNewAnnotation([
|
||||
{
|
||||
x: sx,
|
||||
y: sy,
|
||||
width: x - sx,
|
||||
height: y - sy,
|
||||
page: pageNumber,
|
||||
key: "0"
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const annotationsToDraw = [...annotations, ...newAnnotation];
|
||||
|
||||
const handlePageLoadSuccess = (page) => {
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
currentPage: page.pageNumber
|
||||
}));
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleFileChange = (files) => {
|
||||
const file = files[0];
|
||||
setPdf(file);
|
||||
|
||||
if (file && file.type === "application/pdf") {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onloadend = () => {
|
||||
const base64String = reader.result.split(",")[1];
|
||||
setPdfDetails((prevdata) => ({ ...prevdata, base64: base64String }));
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
const handleDelete = (key) => {
|
||||
const updateAnnotations = annotations.filter((x) => x.key !== key);
|
||||
setAnnotations(updateAnnotations);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
<Title title={"Debug Pdf"} />
|
||||
{width < 800 ? (
|
||||
<HandleError handleError={"Debug PDF only availble for PC"} />
|
||||
) : (
|
||||
<>
|
||||
{!isModal && (
|
||||
<div className="signatureContainer">
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdf}
|
||||
allPages={allPages}
|
||||
setAllPages={setAllPages}
|
||||
setPageNumber={setPageNumber}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
{/* pdf render view */}
|
||||
<div>
|
||||
<div data-tut="reactourThird">
|
||||
<RenderDebugPdf
|
||||
pdfUrl={pdf}
|
||||
pageDetails={pageDetails}
|
||||
pageNumber={pageNumber}
|
||||
setPdfLoadFail={setPdfLoadFail}
|
||||
pdfLoadFail={pdfLoadFail}
|
||||
handlePageLoadSuccess={handlePageLoadSuccess}
|
||||
handleMouseMove={handleMouseMove}
|
||||
handleMouseUp={handleMouseUp}
|
||||
handleMouseDown={handleMouseDown}
|
||||
hoverCoordinates={hoverCoordinates}
|
||||
annotations={annotationsToDraw}
|
||||
pdfDimension={pdfDimension}
|
||||
handleMouseMoveDiv={handleMouseMoveDiv}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ backgroundColor: "white", width: 220 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
PDF details
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Name: {pdfDetails?.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Pdf type: {pdfDetails?.pdftype}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Total Pages: {pdfDetails?.totalPages}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Current Page: {pdfDetails?.currentPage}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Base64 : {pdfDetails?.base64.slice(0, 10)}...
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => copytoclipboard(pdfDetails?.base64)}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
Last click
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
x co-ordinate: {pdfDetails?.x}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
y co-ordinate: {pdfDetails?.y}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey",
|
||||
borderTop: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
Annotation
|
||||
</div>
|
||||
<ul
|
||||
style={{
|
||||
listStyle: "none",
|
||||
padding: 10,
|
||||
height: 500,
|
||||
overflowY: "auto",
|
||||
scrollbarWidth: 5
|
||||
}}
|
||||
>
|
||||
{annotations.map((coord, index) => (
|
||||
<li key={index}>
|
||||
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
|
||||
index + 1
|
||||
}:`}</span>
|
||||
<code
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: "black",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
|
||||
</code>
|
||||
<div>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() =>
|
||||
copytoclipboard(
|
||||
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => handleDelete(coord.key)}
|
||||
>
|
||||
<i className="fa-solid fa-trash-can"></i>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi title={"Select PDF"} isOpen={isModal}>
|
||||
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => handleFileChange(e.target.files)}
|
||||
style={{
|
||||
width: "100%",
|
||||
border: "1px solid grey",
|
||||
borderRadius: "4px",
|
||||
padding: "5px",
|
||||
fontSize: 12
|
||||
}}
|
||||
accept=".pdf"
|
||||
required
|
||||
/>
|
||||
<div
|
||||
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
background: "#32a3ac",
|
||||
borderRadius: "2px",
|
||||
boxShadow:
|
||||
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
|
||||
border: "none",
|
||||
textTransform: "uppercase",
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
padding: "0.375rem 0.75rem",
|
||||
textAlign: "center",
|
||||
color: "#ffffff",
|
||||
outline: "none"
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DebugPdf;
|
||||
@@ -74,8 +74,11 @@ const Forms = (props) => {
|
||||
};
|
||||
|
||||
const handleFileUpload = async (file) => {
|
||||
Parse.serverURL = process.env.REACT_APP_SERVERURL;
|
||||
Parse.initialize(process.env.REACT_APP_APPID);
|
||||
const url = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
Parse.serverURL = url;
|
||||
Parse.initialize("opensign");
|
||||
setfileload(true);
|
||||
const fileName = file.name;
|
||||
const name = sanitizeFileName(fileName);
|
||||
@@ -195,9 +198,7 @@ const Forms = (props) => {
|
||||
});
|
||||
setFileUpload([]);
|
||||
setpercentage(0);
|
||||
navigate(
|
||||
`/asmf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/${props?.redirectRoute}/${res.id}`
|
||||
);
|
||||
navigate(`/${props?.redirectRoute}/${res.id}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
@@ -280,7 +281,8 @@ const Forms = (props) => {
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {fileupload?.split("/")?.pop()?.split("_")[1]}
|
||||
file selected :{" "}
|
||||
{fileupload?.split("/")?.pop()?.split("_")[1]}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import "../styles/loginPage.css";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import axios from "axios";
|
||||
import { themeColor } from "../constant/const";
|
||||
|
||||
function GuestLogin() {
|
||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
||||
let navigate = useNavigate();
|
||||
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();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||
const handleServerUrl = () => {
|
||||
//split url in array from '&'
|
||||
localStorage.clear();
|
||||
const checkSplit = serverUrl.split("&");
|
||||
const server = checkSplit[0];
|
||||
const parseId = checkSplit[1];
|
||||
const appName = checkSplit[2];
|
||||
|
||||
const newServer = server.replaceAll("%2F", "/");
|
||||
localStorage.setItem("baseUrl", newServer);
|
||||
localStorage.setItem("parseAppId", parseId);
|
||||
localStorage.setItem("_appName", appName);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const { value } = event.target;
|
||||
setOTP(value);
|
||||
};
|
||||
|
||||
//send email OTP function
|
||||
const SendOtp = async (e) => {
|
||||
const serverUrl =
|
||||
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
|
||||
const parseId =
|
||||
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
|
||||
if (serverUrl && localStorage) {
|
||||
setLoading(true);
|
||||
e.preventDefault();
|
||||
setEmail(email);
|
||||
|
||||
try {
|
||||
let url = `${serverUrl}functions/SendOTPMailV1/`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email.toString()
|
||||
};
|
||||
let Otp = await axios.post(url, body, { headers: headers });
|
||||
|
||||
if (Otp) {
|
||||
setLoading(false);
|
||||
setEnterOtp(true);
|
||||
}
|
||||
} catch (error) {
|
||||
alert("something went wrong!");
|
||||
}
|
||||
} else {
|
||||
alert("something went wrong!");
|
||||
}
|
||||
};
|
||||
|
||||
//verify OTP send on via email
|
||||
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 {
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail/`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email,
|
||||
otp: OTP
|
||||
};
|
||||
let user = await axios.post(url, body, { headers: headers });
|
||||
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;
|
||||
const parseId = localStorage.getItem("parseAppId");
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem(
|
||||
`Parse/${parseId}/currentUser`,
|
||||
JSON.stringify(_user)
|
||||
);
|
||||
localStorage.setItem("username", _user.name);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||
localStorage.setItem("isGuestSigner", true);
|
||||
setLoading(false);
|
||||
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
} else {
|
||||
alert("Please Enter OTP!");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
{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"
|
||||
}}
|
||||
>
|
||||
<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 ? (
|
||||
<div className="row">
|
||||
<div className="col-sm-6 KLO">
|
||||
<span className="welcomeText">Welcome Back !</span>
|
||||
<br />
|
||||
<span className="KNLO">
|
||||
Verification code is sent to your email
|
||||
</span>
|
||||
<div className="card card-box" style={{ borderRadius: "0px" }}>
|
||||
<div className="card-body">
|
||||
<input
|
||||
type="email"
|
||||
name="mobile"
|
||||
value={email}
|
||||
disabled
|
||||
className="loginInput"
|
||||
/>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div className="btnContainer">
|
||||
{loading ? (
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
disabled
|
||||
>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm "
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
Loading...
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="verifyBtn"
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
marginLeft: "0px !important"
|
||||
}}
|
||||
onClick={(e) => SendOtp(e)}
|
||||
>
|
||||
Send OTP
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="row">
|
||||
<div className="col-sm-6 KLO">
|
||||
<span className="welcomeText">Welcome Back !</span>
|
||||
<br />
|
||||
<span className="KNLO">You will get a OTP via Email</span>
|
||||
<div className="card card-box">
|
||||
<div className="card-body">
|
||||
<label>Enter Verification Code</label>
|
||||
<input
|
||||
type="number"
|
||||
className="loginInput"
|
||||
name="OTP"
|
||||
value={OTP}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{loading ? (
|
||||
<button
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
type="button"
|
||||
disabled
|
||||
>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm "
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
Loading...
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => VerifyOTP(e)}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
>
|
||||
Verify
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GuestLogin;
|
||||
@@ -1,84 +0,0 @@
|
||||
import { Suspense } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useFederatedComponent from "mf-cra";
|
||||
import React from "react";
|
||||
import Title from "../components/Title";
|
||||
function RemoteApp({ app }) {
|
||||
// console.log("app ", app);
|
||||
const { Component: RemoteComponent } = useFederatedComponent(app);
|
||||
// console.log("RemoteComponent ", RemoteComponent);
|
||||
return (
|
||||
<>
|
||||
<Title title={app.remoteName} />
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)",
|
||||
backgroundColor: "#ffffff",
|
||||
width: "100%",
|
||||
borderRadius: 3,
|
||||
height: "100%",
|
||||
minHeight: "100vh"
|
||||
}}
|
||||
>
|
||||
{RemoteComponent && <RemoteComponent />}
|
||||
</div>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
function checkObjectProperties(obj) {
|
||||
// Check if all three properties exist in the object
|
||||
if (
|
||||
obj &&
|
||||
obj.remoteName !== undefined &&
|
||||
obj.remoteUrl !== undefined &&
|
||||
obj.moduleToLoad !== undefined
|
||||
) {
|
||||
return true; // All properties are present
|
||||
} else {
|
||||
return false; // At least one property is missing
|
||||
}
|
||||
}
|
||||
export default function RemoteAppContainer() {
|
||||
const { remoteApp } = useParams();
|
||||
|
||||
const obj = {
|
||||
remoteUrl: window.location.origin + "/mfbuild/remoteEntry.js",
|
||||
moduleToLoad: "./AppRoutes",
|
||||
remoteName: remoteApp
|
||||
};
|
||||
// console.log("obj ", obj);
|
||||
const isMf = checkObjectProperties(obj);
|
||||
if (isMf) {
|
||||
const appToLoad = obj;
|
||||
return <RemoteApp app={appToLoad} />;
|
||||
} else {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
|
||||
404
|
||||
</h1>
|
||||
<p className="text-[30px] lg:text-[50px] text-black">
|
||||
Page Not Found
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,690 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import redPen from "../assets/images/redPen.png";
|
||||
import bluePen from "../assets/images/bluePen.png";
|
||||
import blackPen from "../assets/images/blackPen.png";
|
||||
import "../styles/managesign.css";
|
||||
import "../styles/signature.css";
|
||||
import axios from "axios";
|
||||
import { toDataUrl } from "../constant/Utils";
|
||||
|
||||
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();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
//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);
|
||||
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);
|
||||
} 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")
|
||||
}
|
||||
};
|
||||
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={{
|
||||
backgroundColor: "white",
|
||||
maxHeight: "500px",
|
||||
overflow: "auto"
|
||||
}}
|
||||
>
|
||||
{isSuccess && (
|
||||
<div
|
||||
className="alert alert-success successBox"
|
||||
role="alert"
|
||||
onAnimationEnd={() => setIsSuccess(false)}
|
||||
>
|
||||
Signature saved successfully!
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="mainDiv"
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingRight: "10px"
|
||||
}}
|
||||
>
|
||||
<div style={{ margin: 20 }}>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: "700",
|
||||
fontSize: 15,
|
||||
color: "#000",
|
||||
paddingBottom: 8
|
||||
}}
|
||||
>
|
||||
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="penContainerDefault"
|
||||
>
|
||||
<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="intialSignature"
|
||||
>
|
||||
<img
|
||||
alt="preview image"
|
||||
src={Initials}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<SignatureCanvas
|
||||
ref={initailsRef}
|
||||
penColor={initialPen}
|
||||
canvasProps={{
|
||||
className: "intialSignature"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleInitialsChange(initailsRef.current.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
className="penContainerInitial"
|
||||
>
|
||||
<div>
|
||||
{!isInitials && (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{allColor.map((data, key) => {
|
||||
return (
|
||||
<img
|
||||
alt="pen"
|
||||
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;
|
||||
@@ -0,0 +1,757 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import "../styles/opensigndrive.css";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import DriveBody from "../components/opensigndrive/DriveBody";
|
||||
import { themeColor, iconColor } from "../constant/const";
|
||||
import { getDrive } from "../constant/Utils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
|
||||
function Opensigndrive() {
|
||||
const navigate = useNavigate();
|
||||
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 [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 [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||||
const [isNewFol, setIsNewFol] = useState(false);
|
||||
const currentUser =
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
) &&
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonCurrentUser = JSON.parse(currentUser);
|
||||
|
||||
useEffect(() => {
|
||||
if (docId) {
|
||||
getPdfFolderDocumentList();
|
||||
} else {
|
||||
getPdfDocumentList();
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [docId]);
|
||||
|
||||
//function for get all pdf document list
|
||||
const getPdfDocumentList = async () => {
|
||||
const load = {
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
};
|
||||
let driveDetails;
|
||||
setIsLoading(load);
|
||||
try {
|
||||
driveDetails = await getDrive();
|
||||
if (driveDetails) {
|
||||
if (driveDetails.length > 0) {
|
||||
setPdfData(driveDetails);
|
||||
sortApps(null, null, driveDetails);
|
||||
}
|
||||
const data = [
|
||||
{
|
||||
name: "OpenSign™ Drive",
|
||||
objectId: ""
|
||||
}
|
||||
];
|
||||
setFolderName(data);
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
} else if (driveDetails === "Error: Something went wrong!") {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading(loadObj);
|
||||
}
|
||||
} catch (e) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
};
|
||||
//function for get parent folder document list
|
||||
const getPdfFolderDocumentList = async () => {
|
||||
const load = {
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
};
|
||||
setIsLoading(load);
|
||||
let driveDetails;
|
||||
try {
|
||||
driveDetails = await getDrive(docId);
|
||||
|
||||
if (driveDetails) {
|
||||
if (driveDetails.length > 0) {
|
||||
setPdfData(driveDetails);
|
||||
sortApps(null, null, driveDetails);
|
||||
} else {
|
||||
setPdfData([]);
|
||||
}
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
} else if (driveDetails === "Error: Something went wrong!") {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading(loadObj);
|
||||
}
|
||||
} catch (e) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong!"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//function for get all pdf document list
|
||||
const getParentFolder = async () => {
|
||||
setIsFolder(true);
|
||||
};
|
||||
//function for handle folder name path
|
||||
const handleRoute = (index) => {
|
||||
const updateFolderName = folderName.filter((x, i) => {
|
||||
if (i <= index) {
|
||||
return x;
|
||||
}
|
||||
});
|
||||
|
||||
setFolderName(updateFolderName);
|
||||
const getLastId = updateFolderName[updateFolderName.length - 1];
|
||||
setDocId(getLastId.objectId);
|
||||
};
|
||||
|
||||
//function for add new folder name
|
||||
const handleFolderName = (e) => {
|
||||
setError();
|
||||
const value = e.target.value;
|
||||
setNewFolderName(value);
|
||||
};
|
||||
//function for create folder
|
||||
const handleAddFolder = async () => {
|
||||
if (newFolderName) {
|
||||
setIsFolderLoader(true);
|
||||
const getParentObjId = folderName[folderName.length - 1];
|
||||
const parentId = getParentObjId && getParentObjId.objectId;
|
||||
const foldercls = `${localStorage.getItem("_appName")}_Document`;
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: foldercls,
|
||||
objectId: parentId
|
||||
};
|
||||
const CreatedBy = {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: jsonCurrentUser.objectId
|
||||
};
|
||||
|
||||
try {
|
||||
const exsitQuery = new Parse.Query(foldercls);
|
||||
exsitQuery.equalTo("Name", newFolderName);
|
||||
exsitQuery.equalTo("Type", "Folder");
|
||||
if (parentId) {
|
||||
exsitQuery.equalTo("Folder", folderPtr);
|
||||
}
|
||||
const templExist = await exsitQuery.first();
|
||||
if (templExist) {
|
||||
setError("Folder already exist!");
|
||||
setIsFolderLoader(false);
|
||||
} else {
|
||||
const template = new Parse.Object(foldercls);
|
||||
template.set("Name", newFolderName);
|
||||
template.set("Type", "Folder");
|
||||
|
||||
if (parentId) {
|
||||
template.set("Folder", folderPtr);
|
||||
}
|
||||
template.set("CreatedBy", CreatedBy);
|
||||
const res = await template.save();
|
||||
if (res) {
|
||||
const result = JSON.parse(JSON.stringify(res));
|
||||
|
||||
setPdfData((prev) => [...prev, result]);
|
||||
setNewFolderName();
|
||||
setIsFolderLoader(false);
|
||||
setIsFolder(false);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "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 = (type, order, driveDetails) => {
|
||||
const selectedSortType = type ? type : selectedSort ? selectedSort : "Date";
|
||||
const sortOrder = order ? order : sortingOrder ? sortingOrder : "Decending";
|
||||
|
||||
let sortingData = driveDetails;
|
||||
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(!isShowSort);
|
||||
} else if (isNewFol && !e.target.closest("#folder-menu")) {
|
||||
setIsNewFol(!isNewFol);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isShowSort, isNewFol]);
|
||||
|
||||
const handleFolderTab = (folderData) => {
|
||||
return folderData.map((data, id) => {
|
||||
return (
|
||||
<React.Fragment key={id}>
|
||||
<span
|
||||
onClick={() => handleRoute(id)}
|
||||
style={{
|
||||
color: "#a64b4e",
|
||||
fontWeight: "400",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
{data.name}
|
||||
<span
|
||||
style={{
|
||||
color: "#a64b4e",
|
||||
fontWeight: "200",
|
||||
cursor: "pointer",
|
||||
margin: "0 4px"
|
||||
}}
|
||||
>
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
};
|
||||
const oncloseFolder = () => {
|
||||
setIsFolder(false);
|
||||
setNewFolderName("");
|
||||
setError("");
|
||||
};
|
||||
return (
|
||||
<div style={{ backgroundColor: "white" }} className="folderComponent">
|
||||
<Title title={"OpenSign™ Drive"} drive={true} />
|
||||
<div>
|
||||
<ModalUi
|
||||
headerColor={"#dc3545"}
|
||||
isOpen={isAlert.isShow}
|
||||
title={"Alert"}
|
||||
handleClose={() => {
|
||||
setIsAlert({
|
||||
isShow: false,
|
||||
alertMessage: ""
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<p>{isAlert.alertMessage}</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
onClick={() =>
|
||||
setIsAlert({
|
||||
isShow: false,
|
||||
alertMessage: ""
|
||||
})
|
||||
}
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isFolder}
|
||||
title={"Add New Folder"}
|
||||
handleClose={oncloseFolder}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{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
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleAddFolder();
|
||||
}}
|
||||
style={{ display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<label
|
||||
style={{
|
||||
margin: "10px 0px 10px 0px",
|
||||
fontSize: "15px",
|
||||
fontWeight: "400"
|
||||
}}
|
||||
>
|
||||
Name
|
||||
<span style={{ color: "red" }}>*</span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
required
|
||||
className="form-control inputStyle"
|
||||
type="text"
|
||||
value={newFolderName}
|
||||
onChange={(e) => handleFolderName(e)}
|
||||
// className="addFolderInput"
|
||||
/>
|
||||
<span
|
||||
style={{ color: "red", fontSize: "12px", marginTop: "6px" }}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<button
|
||||
style={{ background: themeColor }}
|
||||
type="submit"
|
||||
className="finishBtn"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={oncloseFolder}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
{isLoading.isLoad ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100vh",
|
||||
width: "100%",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="loader img"
|
||||
src={loader}
|
||||
style={{ width: "80px", height: "80px" }}
|
||||
/>
|
||||
<span style={{ fontSize: "13px", color: "gray" }}>
|
||||
{isLoading.message}
|
||||
</span>
|
||||
</div>
|
||||
) : handleError ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100vh",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: "20px", color: "gray" }}>
|
||||
{handleError}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="folderContainer">
|
||||
<div
|
||||
onMouseEnter={(e) => handleMouseEnter(e)}
|
||||
ref={scrollRef}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
className="folderPath"
|
||||
>
|
||||
{handleFolderTab(folderName)}
|
||||
</div>
|
||||
<div className="dropMenuBD">
|
||||
<div
|
||||
id="folder-menu"
|
||||
className={
|
||||
isNewFol ? "dropdown show dropDownStyle" : "dropdown"
|
||||
}
|
||||
onClick={() => setIsNewFol(!isNewFol)}
|
||||
>
|
||||
<div className="sort">
|
||||
<i
|
||||
className="fa fa-plus-square"
|
||||
aria-hidden="true"
|
||||
style={{ fontSize: "25px", color: `${iconColor}` }}
|
||||
></i>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isNewFol ? "dropdown-menu show" : "dropdown-menu"
|
||||
}
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isNewFol ? "true" : "false"}
|
||||
>
|
||||
{" "}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
onClick={() => getParentFolder()}
|
||||
>
|
||||
<i
|
||||
style={{ marginRight: "5px" }}
|
||||
className="fa fa-plus"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Create folder
|
||||
</span>
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
onClick={() => navigate("/form/sHAnZphf69")}
|
||||
>
|
||||
<i
|
||||
style={{ marginRight: "5px" }}
|
||||
className="fas fa-pen-nib"
|
||||
></i>
|
||||
Sign Yourself
|
||||
</span>
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
onClick={() => navigate("/form/8mZzFxbG1z")}
|
||||
>
|
||||
{" "}
|
||||
<i
|
||||
style={{ marginRight: "5px" }}
|
||||
className="fa fa-file-signature"
|
||||
></i>
|
||||
Request Signatures{" "}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="menu-container"
|
||||
className={isShowSort ? "dropdown show" : "dropdown"}
|
||||
onClick={() => setIsShowSort(!isShowSort)}
|
||||
>
|
||||
<div 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("Name", null, pdfData);
|
||||
}}
|
||||
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("Date", null, pdfData);
|
||||
}}
|
||||
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(null, "Accending", pdfData);
|
||||
}}
|
||||
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(null, "Decending", pdfData);
|
||||
}}
|
||||
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>
|
||||
</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>
|
||||
) : (
|
||||
<DriveBody
|
||||
pdfData={pdfData}
|
||||
setFolderName={setFolderName}
|
||||
setIsLoading={setIsLoading}
|
||||
setDocId={setDocId}
|
||||
getPdfFolderDocumentList={getPdfFolderDocumentList}
|
||||
getPdfDocumentList={getPdfDocumentList}
|
||||
isDocId={docId}
|
||||
setPdfData={setPdfData}
|
||||
isList={isList}
|
||||
setIsAlert={setIsAlert}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Opensigndrive;
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Suspense } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useFederatedComponent from "mf-cra";
|
||||
import React from "react";
|
||||
import Title from "../components/Title";
|
||||
function RemoteApp({ app }) {
|
||||
// console.log("app ", app);
|
||||
const { Component: RemoteComponent } = useFederatedComponent(app);
|
||||
// console.log("RemoteComponent ", RemoteComponent);
|
||||
return (
|
||||
<>
|
||||
<Title title={app.remoteName} />
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)",
|
||||
backgroundColor: "#ffffff",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
borderRadius: 3,
|
||||
minHeight: "70vh"
|
||||
}}
|
||||
>
|
||||
{RemoteComponent && <RemoteComponent />}
|
||||
</div>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
function checkObjectProperties(obj) {
|
||||
// Check if all three properties exist in the object
|
||||
if (
|
||||
obj &&
|
||||
obj.remoteName !== undefined &&
|
||||
obj.remoteUrl !== undefined &&
|
||||
obj.moduleToLoad !== undefined
|
||||
) {
|
||||
return true; // All properties are present
|
||||
} else {
|
||||
return false; // At least one property is missing
|
||||
}
|
||||
}
|
||||
export default function RemoteAppContainer() {
|
||||
const { remoteApp } = useParams();
|
||||
// console.log("remoteApp ", remoteApp);
|
||||
|
||||
let paramString = remoteApp;
|
||||
let queryString = new URLSearchParams(paramString);
|
||||
let obj = {};
|
||||
for (const [key, value] of queryString.entries()) {
|
||||
// console.log("value ", key + ":" + value);
|
||||
|
||||
if (key === "remoteUrl") {
|
||||
const url = window.location.origin + "/mfbuild/remoteEntry.js";
|
||||
// console.log("atob(value) ", atob(value))
|
||||
obj = { ...obj, [key]: url };
|
||||
} else {
|
||||
if (key === "moduleToLoad") {
|
||||
obj = { ...obj, [key]: "./" + value };
|
||||
} else {
|
||||
obj = { ...obj, [key]: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log("obj ", obj);
|
||||
const isMf = checkObjectProperties(obj);
|
||||
if (isMf) {
|
||||
const appToLoad = obj;
|
||||
return <RemoteApp app={appToLoad} />;
|
||||
} else {
|
||||
return <div>App not found</div>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
.addusercontainer {
|
||||
height: 100%;
|
||||
padding: 10px 20px 10px 20px;
|
||||
}
|
||||
|
||||
.loaderdiv {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
}
|
||||
|
||||
.form-wrapper {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
margin-left: 0.5rem;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.addUserInput {
|
||||
padding: 0.5rem 0.75rem;
|
||||
width: 100%;
|
||||
border-width: 1px;
|
||||
border-color: #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
outline: none;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
.buttoncontainer {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.submitbutton {
|
||||
background: #1ab6ce;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 0.375rem 0.75rem;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
outline: none;
|
||||
}
|
||||
/* For classes: bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none */
|
||||
.resetbutton {
|
||||
background-color: #188ae2;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 0.375rem 0.75rem;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
outline: none;
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.containerBox {
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.certificateBox {
|
||||
border: 1px solid rgb(177, 174, 174);
|
||||
padding: 20px;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
.parentDiv {
|
||||
background: gray;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.childDiv {
|
||||
width: 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
background-color: #fff;
|
||||
margin: 30px 20px;
|
||||
border: 0px 1px 1px 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0.3rem;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.modalHeadDiv {
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
border-top-left-radius: 0.3rem;
|
||||
border-top-right-radius: 0.3rem;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.modalBodyDIv {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.spanTagHead {
|
||||
color: white !important;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pTagBody {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
.modalFooterDiv {
|
||||
margin: 15px 15px 15px 5px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.childDiv {
|
||||
margin: 50px 7px;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
.loginInput{
|
||||
padding: 10px;
|
||||
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
font-size: 0.75rem;
|
||||
border: 1px solid #e2dddd;;
|
||||
background-color: #e0e5f5;
|
||||
color: black;
|
||||
|
||||
padding-left: 1rem!important;
|
||||
padding-right: 1rem!important;
|
||||
padding-top: 0.5rem!important;
|
||||
|
||||
font-family: inherit;
|
||||
font-feature-settings: inherit;
|
||||
font-variation-settings: inherit;
|
||||
font-weight: inherit;
|
||||
overflow: visible;
|
||||
|
||||
}
|
||||
.loginInput:focus{
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
.verifyBtn{
|
||||
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;
|
||||
}
|
||||
.verifyBtn:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
.verifyBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
color: white;
|
||||
}
|
||||
.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;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
margin-left: 17px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.welcomeText{
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@media screen and (max-width:366px) {
|
||||
|
||||
.main-logo {
|
||||
width: 150px;
|
||||
height: 46px;
|
||||
display: inline-block;
|
||||
}
|
||||
.welcomeText{
|
||||
font-size: 20px;
|
||||
}
|
||||
.KNLO {
|
||||
color: #878787;
|
||||
font-size: 10px;
|
||||
}
|
||||
.btnContainer{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,540 @@
|
||||
@import "@radix-ui/colors/black-alpha.css";
|
||||
@import "@radix-ui/colors/mauve.css";
|
||||
@import "@radix-ui/colors/violet.css";
|
||||
|
||||
.ContextMenuContent,
|
||||
.ContextMenuSubContent {
|
||||
min-width: 135px;
|
||||
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);
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.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: 15px;
|
||||
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;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: #dad9db;
|
||||
color: var(--violet-11);
|
||||
}
|
||||
|
||||
.itemColor {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
.folderComponent {
|
||||
padding: 30px;
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.inputStyle {
|
||||
border: 1.6px solid #ccc !important;
|
||||
}
|
||||
.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, 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 */
|
||||
}
|
||||
|
||||
.docDeleteBtn {
|
||||
/* box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18); */
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
|
||||
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
padding: 7px 17px;
|
||||
color: white;
|
||||
font-weight: 600 !important;
|
||||
font-size: 12px !important;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.docDeleteBtn:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.docDeleteBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 766px) {
|
||||
.itemColor {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@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,16 @@
|
||||
.addSignerBtn{
|
||||
padding: 10px;
|
||||
margin-top: 2px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #47a3ad;
|
||||
margin: 1px;
|
||||
color: #47a3ad;
|
||||
cursor: pointer
|
||||
}
|
||||
.addSignerBtn:hover{
|
||||
background: #47a3ad;
|
||||
color: #ffffff;
|
||||
}
|
||||
@@ -218,7 +218,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
|
||||
const objectId = contactMail[i].contactPtr.objectId;
|
||||
|
||||
const hostUrl = baseUrl.origin + '/loadmf/signmicroapp';
|
||||
const hostUrl = baseUrl.origin;
|
||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
|
||||
@@ -319,7 +319,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
objectId: res.id,
|
||||
signurl: contact.map(x => ({
|
||||
email: x.email,
|
||||
url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
})),
|
||||
message: 'Document sent successfully!',
|
||||
});
|
||||
|
||||
@@ -268,8 +268,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
};
|
||||
|
||||
const objectId = contactMail[i].contactPtr.objectId;
|
||||
|
||||
const hostUrl = baseUrl.origin + '/loadmf/signmicroapp';
|
||||
const hostUrl = baseUrl.origin;
|
||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
||||
@@ -354,7 +353,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
objectId: res.id,
|
||||
signurl: contact.map(x => ({
|
||||
email: x.email,
|
||||
url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
})),
|
||||
message: 'Document sent successfully!',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,696 @@
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.up = async Parse => {
|
||||
const className = 'w_menu';
|
||||
const userMenu = new Parse.Query(className);
|
||||
const updateUserMenu = await userMenu.get('H9vRfEYKhT');
|
||||
updateUserMenu.set('menuItems', [
|
||||
{
|
||||
icon: 'fas fa-tachometer-alt',
|
||||
title: 'Dashboard',
|
||||
target: '',
|
||||
pageType: 'dashboard',
|
||||
description: '',
|
||||
objectId: '35KBoSgoAK',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-newspaper',
|
||||
title: 'New Document',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: null,
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-nib',
|
||||
title: 'Sign yourself',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'sHAnZphf69',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-paper-plane',
|
||||
title: 'Request signatures',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: '8mZzFxbG1z',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-file-signature',
|
||||
title: 'New template',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'template',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-file-contract',
|
||||
title: 'Templates',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '6TeaPr321t',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
title: 'OpenSign™ Drive',
|
||||
target: '_self',
|
||||
pageType: '',
|
||||
description: '',
|
||||
objectId: 'opensigndrive',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-address-card',
|
||||
title: 'Reports',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-signature',
|
||||
title: 'Need your sign',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '4Hhwbp482K',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tasks',
|
||||
title: 'In Progress',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '1MwEuxLEkF',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-check-circle',
|
||||
title: 'Completed',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'kQUoW4hUXz',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
title: 'Drafts',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'ByHuevtCFY',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-times-circle',
|
||||
title: 'Declined',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'UPr2Fm5WY3',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-hourglass-end',
|
||||
title: 'Expired',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'zNqBHXHsYH',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-address-book',
|
||||
title: 'Contactbook',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '5KhaPr482K',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-cog',
|
||||
title: 'Settings',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-fancy',
|
||||
title: 'My Signature',
|
||||
target: '_self',
|
||||
pageType: '',
|
||||
description: '',
|
||||
objectId: 'managesign',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-key',
|
||||
title: 'API Token',
|
||||
target: '_self',
|
||||
pageType: 'generatetoken',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-globe',
|
||||
title: 'Webhook',
|
||||
target: '_self',
|
||||
pageType: 'webhook',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const AdminMenu = new Parse.Query(className);
|
||||
const updateAdminMenu = await AdminMenu.get('VPh91h0ZHk');
|
||||
updateAdminMenu.set('menuItems', [
|
||||
{
|
||||
icon: 'fas fa-tachometer-alt',
|
||||
title: 'Dashboard',
|
||||
target: '',
|
||||
pageType: 'dashboard',
|
||||
description: '',
|
||||
objectId: '35KBoSgoAK',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-newspaper',
|
||||
title: 'New Document',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: null,
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-nib',
|
||||
title: 'Sign yourself',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'sHAnZphf69',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-paper-plane',
|
||||
title: 'Request signatures',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: '8mZzFxbG1z',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-file-signature',
|
||||
title: 'New template',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'template',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-file-contract',
|
||||
title: 'Templates',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '6TeaPr321t',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
title: 'OpenSign™ Drive',
|
||||
target: '_self',
|
||||
pageType: '',
|
||||
description: '',
|
||||
objectId: 'opensigndrive',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-address-card',
|
||||
title: 'Reports',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-signature',
|
||||
title: 'Need your sign',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '4Hhwbp482K',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tasks',
|
||||
title: 'In Progress',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '1MwEuxLEkF',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-check-circle',
|
||||
title: 'Completed',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'kQUoW4hUXz',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
title: 'Drafts',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'ByHuevtCFY',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-times-circle',
|
||||
title: 'Declined',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'UPr2Fm5WY3',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-hourglass-end',
|
||||
title: 'Expired',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'zNqBHXHsYH',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-address-book',
|
||||
title: 'Contactbook',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '5KhaPr482K',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-cog',
|
||||
title: 'Settings',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-fancy',
|
||||
title: 'My Signature',
|
||||
target: '_self',
|
||||
pageType: '',
|
||||
description: '',
|
||||
objectId: 'managesign',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-user',
|
||||
title: 'Add User',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'lM0xRnM3iE',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-key',
|
||||
title: 'API Token',
|
||||
target: '_self',
|
||||
pageType: 'generatetoken',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-globe',
|
||||
title: 'Webhook',
|
||||
target: '_self',
|
||||
pageType: 'webhook',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
// TODO: Set the schema here
|
||||
// Example:
|
||||
// schema.addString('name').addNumber('cash');
|
||||
const batch = [updateUserMenu, updateAdminMenu];
|
||||
return Parse.Object.saveAll(batch, { useMasterKey: true });
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.down = async Parse => {
|
||||
// TODO: set className here
|
||||
const className = 'w_menu';
|
||||
const userMenu = new Parse.Query(className);
|
||||
const revertUserMenu = await userMenu.get('H9vRfEYKhT');
|
||||
revertUserMenu.set('menuItems', [
|
||||
{
|
||||
icon: 'fas fa-tachometer-alt',
|
||||
title: 'Dashboard',
|
||||
target: '',
|
||||
pageType: 'dashboard',
|
||||
description: '',
|
||||
objectId: '35KBoSgoAK',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-newspaper',
|
||||
title: 'New Document',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: null,
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-nib',
|
||||
title: 'Sign yourself',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'sHAnZphf69',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-paper-plane',
|
||||
title: 'Request signatures',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: '8mZzFxbG1z',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-file-signature',
|
||||
title: 'New template',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'template',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-file-contract',
|
||||
title: 'Templates',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '6TeaPr321t',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
title: 'OpenSign™ Drive',
|
||||
target: '_self',
|
||||
pageType: 'mf',
|
||||
description: '',
|
||||
objectId:
|
||||
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-address-card',
|
||||
title: 'Reports',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-signature',
|
||||
title: 'Need your sign',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '4Hhwbp482K',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tasks',
|
||||
title: 'In Progress',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '1MwEuxLEkF',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-check-circle',
|
||||
title: 'Completed',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'kQUoW4hUXz',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
title: 'Drafts',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'ByHuevtCFY',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-times-circle',
|
||||
title: 'Declined',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'UPr2Fm5WY3',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-hourglass-end',
|
||||
title: 'Expired',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'zNqBHXHsYH',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-address-book',
|
||||
title: 'Contactbook',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '5KhaPr482K',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-cog',
|
||||
title: 'Settings',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-fancy',
|
||||
title: 'My Signature',
|
||||
target: '_self',
|
||||
pageType: 'mf',
|
||||
description: '',
|
||||
objectId:
|
||||
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-key',
|
||||
title: 'API Token',
|
||||
target: '_self',
|
||||
pageType: 'generatetoken',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-globe',
|
||||
title: 'Webhook',
|
||||
target: '_self',
|
||||
pageType: 'webhook',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const adminMenu = new Parse.Query(className);
|
||||
const revertAdminMenu = await adminMenu.get('VPh91h0ZHk');
|
||||
revertAdminMenu.set('menuItems', [
|
||||
{
|
||||
icon: 'fas fa-tachometer-alt',
|
||||
title: 'Dashboard',
|
||||
target: '',
|
||||
pageType: 'dashboard',
|
||||
description: '',
|
||||
objectId: '35KBoSgoAK',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-newspaper',
|
||||
title: 'New Document',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: null,
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-nib',
|
||||
title: 'Sign yourself',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'sHAnZphf69',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-paper-plane',
|
||||
title: 'Request signatures',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: '8mZzFxbG1z',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-file-signature',
|
||||
title: 'New template',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'template',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-file-contract',
|
||||
title: 'Templates',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '6TeaPr321t',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-folder',
|
||||
title: 'OpenSign™ Drive',
|
||||
target: '_self',
|
||||
pageType: 'mf',
|
||||
description: '',
|
||||
objectId:
|
||||
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-address-card',
|
||||
title: 'Reports',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-signature',
|
||||
title: 'Need your sign',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '4Hhwbp482K',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-tasks',
|
||||
title: 'In Progress',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '1MwEuxLEkF',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-check-circle',
|
||||
title: 'Completed',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'kQUoW4hUXz',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
title: 'Drafts',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'ByHuevtCFY',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-times-circle',
|
||||
title: 'Declined',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'UPr2Fm5WY3',
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-hourglass-end',
|
||||
title: 'Expired',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: 'zNqBHXHsYH',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-address-book',
|
||||
title: 'Contactbook',
|
||||
target: '_self',
|
||||
pageType: 'report',
|
||||
description: '',
|
||||
objectId: '5KhaPr482K',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: 'fas fa-cog',
|
||||
title: 'Settings',
|
||||
target: '_self',
|
||||
pageType: null,
|
||||
description: '',
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: 'fas fa-pen-fancy',
|
||||
title: 'My Signature',
|
||||
target: '_self',
|
||||
pageType: 'mf',
|
||||
description: '',
|
||||
objectId:
|
||||
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
|
||||
},
|
||||
{
|
||||
icon: 'far fa-user',
|
||||
title: 'Add User',
|
||||
target: '_self',
|
||||
pageType: 'form',
|
||||
description: '',
|
||||
objectId: 'lM0xRnM3iE',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-key',
|
||||
title: 'API Token',
|
||||
target: '_self',
|
||||
pageType: 'generatetoken',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
{
|
||||
icon: 'fa-solid fa-globe',
|
||||
title: 'Webhook',
|
||||
target: '_self',
|
||||
pageType: 'webhook',
|
||||
description: '',
|
||||
objectId: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
// TODO: Set the schema here
|
||||
// Example:
|
||||
// schema.addString('name').addNumber('cash');
|
||||
const batch = [revertUserMenu, revertAdminMenu];
|
||||
return Parse.Object.saveAll(batch, { useMasterKey: true });
|
||||
};
|
||||
@@ -2,8 +2,6 @@ import React from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import { Stage, Layer, Rect } from "react-konva";
|
||||
const RenderDebugPdf = (props) => {
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
|
||||
@@ -488,7 +488,7 @@ function PlaceHolderSign() {
|
||||
setIsCheckbox(true);
|
||||
} else if (dragTypeValue === "radio") {
|
||||
setIsRadio(true);
|
||||
} else if(dragTypeValue !== 'label' && dragTypeValue !== 'signature') {
|
||||
} else if (dragTypeValue !== "label" && dragTypeValue !== "signature") {
|
||||
setIsNameModal(true);
|
||||
}
|
||||
setWidgetType(dragTypeValue);
|
||||
@@ -704,10 +704,9 @@ function PlaceHolderSign() {
|
||||
flag,
|
||||
containerWH
|
||||
);
|
||||
|
||||
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
Parse.initialize(parseAppId);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const fileName = sanitizeFileName(pdfDetails[0].Name) + ".pdf";
|
||||
@@ -746,7 +745,7 @@ function PlaceHolderSign() {
|
||||
setIsSendAlert(alert);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const sendEmailToSigners = async () => {
|
||||
setIsUiLoading(true);
|
||||
const pdfUrl = await embedPrefilllData();
|
||||
@@ -842,7 +841,6 @@ function PlaceHolderSign() {
|
||||
//filter label widgets after add label widgets data on pdf
|
||||
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
|
||||
|
||||
|
||||
try {
|
||||
if (updateExpiryDate) {
|
||||
data = {
|
||||
@@ -1212,7 +1210,7 @@ function PlaceHolderSign() {
|
||||
|
||||
const handleNameModal = () => {
|
||||
setIsNameModal(!isNameModal);
|
||||
setCurrWidgetsDetails({})
|
||||
setCurrWidgetsDetails({});
|
||||
};
|
||||
//function for update TourStatus
|
||||
const closeTour = async () => {
|
||||
|
||||