Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into api-v1-beta

This commit is contained in:
prafull-opensignlabs
2024-01-11 22:27:09 +05:30
106 changed files with 11025 additions and 4332 deletions
+9
View File
@@ -196,6 +196,15 @@
"contributions": [
"code"
]
},
{
"login": "VikramNagwal",
"name": "Vikram",
"avatar_url": "https://avatars.githubusercontent.com/u/123088024?v=4",
"profile": "https://github.com/VikramNagwal",
"contributions": [
"code"
]
}
]
}
+5 -5
View File
@@ -45,14 +45,14 @@ DO_REGION=us-west
USE_LOCAL=TRUE
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
MAILGUN_API_KEY=XXXXX
MAILGUN_API_KEY=
MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com
SMTP_ENABLE=
SMTP_HOST=
SMTP_PORT=
SMTP_USER_EMAIL=
SMTP_PASS=
SMTP_HOST=smtp.yourhost.com
SMTP_PORT=443
SMTP_USER_EMAIL=mailer@yourdomain.com
SMTP_PASS=password
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
+22
View File
@@ -0,0 +1,22 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# maintain dependencies for frontend
- package-ecosystem: "npm" # See documentation for possible values
directory: "/apps/OpenSign" # Location of package manifests
schedule:
interval: "weekly"
# maintain dependencies for server
- package-ecosystem: "npm" # See documentation for possible values
directory: "/apps/OpenSignServer" # Location of package manifests
schedule:
interval: "weekly"
# maintain dependencies for microfrontends
- package-ecosystem: "npm" # See documentation for possible values
directory: "microfrontends/SignDocuments" # Location of package manifests
schedule:
interval: "weekly"
+3
View File
@@ -131,6 +131,9 @@ We would like to thank all our contributors and users for their support and feed
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Govinda04"><img src="https://avatars.githubusercontent.com/u/50038172?v=4?s=100" width="100px;" alt="Govinda Kocharekar"/><br /><sub><b>Govinda Kocharekar</b></sub></a><br /><a href="#code-Govinda04" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://bilal.cc"><img src="https://avatars.githubusercontent.com/u/55330484?v=4?s=100" width="100px;" alt="Bilal Ahmad Bhat"/><br /><sub><b>Bilal Ahmad Bhat</b></sub></a><br /><a href="#code-crediblebilal" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
</tr>
</tbody>
</table>
+2 -2
View File
@@ -31,7 +31,7 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<title>Open sign</title>
<title>OpenSign</title>
</head>
<body>
@@ -50,4 +50,4 @@
-->
</body>
</html>
</html>
-1
View File
@@ -1 +0,0 @@
v1.0.5-beta
+19 -2
View File
@@ -16,6 +16,7 @@ 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";
function App() {
const [isloading, setIsLoading] = useState(true);
@@ -59,8 +60,24 @@ function App() {
) : (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} />
<Route
exact
path="/"
element={
<ValidateRoute>
<Login />
</ValidateRoute>
}
/>
<Route
exact
path="/signup"
element={
<ValidateRoute>
<Signup />
</ValidateRoute>
}
/>
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
@@ -32,6 +32,7 @@ const AppendFormInForm = (props) => {
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);
@@ -45,6 +46,7 @@ const AppendFormInForm = (props) => {
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
@@ -104,13 +106,18 @@ const AppendFormInForm = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
@@ -157,13 +164,18 @@ const AppendFormInForm = (props) => {
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
@@ -86,7 +86,7 @@ const LoginFacebook = ({
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
role: "contracts_Admin",
role: "contracts_User",
company: userDetails.Company
}
};
+1 -1
View File
@@ -148,7 +148,7 @@ const GoogleSignInBtn = ({
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
role: "contracts_Admin",
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
+8 -6
View File
@@ -5,7 +5,7 @@ import Parse from "parse";
import axios from "axios";
import "../styles/spinner.css";
import TreeFormComponent from "./TreeFormComponent";
import TreeEditForm from "./TreeEditForm";
// import TreeEditForm from "./TreeEditForm";
import "../styles/modal.css";
import Modal from "react-modal";
@@ -22,7 +22,7 @@ const TreeWidget = (props) => {
const [schemaState, setSchemaState] = useState({});
const [TabURL, setTabURL] = useState("");
const [editable, setEditable] = useState(false);
const [editId, setEditId] = useState("");
// const [editId, setEditId] = useState("");
const [defaultState, setDefaultState] = useState(false);
const [isShowModal, setIsShowModal] = useState(false);
const selectFolderHandle = async () => {
@@ -472,6 +472,8 @@ const TreeWidget = (props) => {
props.schema.data.FolderTypeField,
props.schema.data.FolderTypeValue
);
const currentUser = Parse.User.current();
folder.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (tabList.length > 0) {
let len = tabList.length - 1;
folder.set(props.schema.data.ParentFolderField, {
@@ -698,7 +700,7 @@ const TreeWidget = (props) => {
))}
<hr />
</div>
{editable && (
{/* {editable && (
<TreeEditForm
FormId={props.schema.data.FormId}
objectId={editId}
@@ -707,7 +709,7 @@ const TreeWidget = (props) => {
selectFolderHandle();
}}
/>
)}
)} */}
{isAddField && !loader && !editable && (
<TreeFormComponent
Id={props.schema.data.FormId}
@@ -795,7 +797,7 @@ const TreeWidget = (props) => {
</a>
</div>
{fldr[props.schema.data.FolderTypeField] ===
{/* {fldr[props.schema.data.FolderTypeField] ===
props.schema.data.FolderTypeValue && (
<a
className="float-right"
@@ -815,7 +817,7 @@ const TreeWidget = (props) => {
aria-hidden="true"
></i>
</a>
)}
)} */}
</li>
)
)}
@@ -0,0 +1,147 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../primitives/Alert";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: parentFolderId
};
const [name, setName] = useState("");
const [folderList, setFolderList] = useState([]);
const [isAlert, setIsAlert] = useState(false);
const [selectedParent, setSelectedParent] = useState();
const [alert, setAlert] = useState({ type: "info", message: "" });
useEffect(() => {
fetchFolder();
// eslint-disable-next-line
}, []);
const fetchFolder = async () => {
try {
const FolderQuery = new Parse.Query(folderCls);
if (parentFolderId) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
}
}
} catch (error) {
console.log("Err ", error);
}
};
const handleCreateFolder = async (event) => {
event.preventDefault();
if (name) {
const currentUser = Parse.User.current();
const exsitQuery = new Parse.Query(folderCls);
exsitQuery.equalTo("Name", name);
exsitQuery.equalTo("Type", "Folder");
if (parentFolderId) {
exsitQuery.equalTo("Folder", folderPtr);
}
const templExist = await exsitQuery.first();
if (templExist) {
setAlert({ type: "dange", message: "Folder already exist!" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
} else {
const template = new Parse.Object(folderCls);
template.set("Name", name);
template.set("Type", "Folder");
if (selectedParent) {
template.set("Folder", {
__type: "Pointer",
className: folderCls,
objectId: selectedParent
});
} else if (parentFolderId) {
template.set("Folder", folderPtr);
}
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
const res = await template.save();
if (res) {
setAlert({
type: "success",
message: "Folder created successfully!"
});
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
if (onSuccess) {
onSuccess(res);
}
}
}
} else {
setAlert({ type: "info", message: "Please fill folder name" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
}
};
const handleOptions = (e) => {
setSelectedParent(e.target.value);
};
return (
<div>
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder">
<h1 className="text-base font-semibold">Create Folder</h1>
<div className="text-xs mt-2">
<label className="block">
Name<span className="text-red-500 text-[13px]">*</span>
</label>
<input
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Parent Folder</label>
<select
value={selectedParent}
onChange={handleOptions}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
>
<option>select</option>
{folderList.length > 0 &&
folderList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
<div>
<button
onClick={handleCreateFolder}
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
>
<i className="fa-solid fa-plus mr-1"></i>
<span>Create</span>
</button>
</div>
</div>
</div>
);
};
export default CreateFolder;
@@ -0,0 +1,60 @@
import React, { useCallback, useMemo } from "react";
import { useDropScript } from "../../hook/useScript";
const DROPBOX_APP_KEY = process.env.REACT_APP_DROPBOX_API_KEY; // App key
const DROPBOX_SDK_URL = "https://www.dropbox.com/static/api/2/dropins.js";
const DROPBOX_SCRIPT_ID = "dropboxjs";
export default function DropboxChooser({ children, onSuccess, onCancel }) {
useDropScript(DROPBOX_SDK_URL, {
attrs: {
id: DROPBOX_SCRIPT_ID,
"data-app-key": DROPBOX_APP_KEY
}
});
const options = useMemo(
() => ({
// Required. Called when a user selects an item in the Chooser.
success: (files) => {
// console.log("success", files);
onSuccess && onSuccess(files);
},
// Optional. Called when the user closes the dialog without selecting a file
// and does not include any parameters.
cancel: () => {
console.log("cancel");
onCancel && onCancel();
},
// Optional. "preview" (default) is a preview link to the document for sharing,
// "direct" is an expiring link to download the contents of the file. For more
linkType: "direct", // or "preview"
multiselect: false, // 是否支持多选
extensions: [".pdf"],
// Optional. A value of false (default) limits selection to files,
// while true allows the user to select both folders and files.
// You cannot specify `linkType: "direct"` when using `folderselect: true`.
folderselect: false // or true
}),
[onSuccess, onCancel]
);
const handleChoose = useCallback((e) => {
e.preventDefault()
if (window.Dropbox) {
window.Dropbox.choose(options);
}
}, [options]);
return (
<div onClick={handleChoose}>
{children || (
<button className="px-2 py-[2px] text-2xl rounded border-[1px] text-blue-400 border-gray-300 w-full">
<i className="fa-brands fa-dropbox"></i>
</button>
)}
</div>
);
}
@@ -2,14 +2,12 @@ import React, { useState, useEffect } from "react";
import { SaveFileSize } from "../../constant/saveFileSize";
import Parse from "parse";
import sanitizeFileName from "../../primitives/sanitizeFileName";
import DropboxChooser from "./DropboxChoose";
const FileUpload = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [_fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
const [localValue, setLocalValue] = useState("");
const [Message] = useState(false);
const [percentage, setpercentage] = useState(0);
@@ -25,7 +23,6 @@ const FileUpload = (props) => {
const onChange = (e) => {
try {
let files = e.target.files;
setLocalValue(e.target.files);
if (typeof files[0] !== "undefined") {
if (props.schema.filetypes && props.schema.filetypes.length > 0) {
var fileName = files[0].name;
@@ -106,6 +103,56 @@ const FileUpload = (props) => {
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
// console.log("file ", files);
setfileload(true);
const file = files[0];
const url = file.link;
const size = file.bytes;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than 10 MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
});
// console.log("response.url() ", response.url());
setFileUpload(response.url());
props.onChange(response.url());
setfileload(false);
if (response.url()) {
SaveFileSize(size, response.url());
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {
console.log("cancel clicked ");
};
let fileView =
props.formData &&
props.schema.uploadtype === "s3viajw" ? null : props.formData &&
@@ -207,55 +254,55 @@ const FileUpload = (props) => {
</div>
<>
{localValue ? (
<input
type="file"
id="hashfile"
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
accept="application/pdf,application/vnd.ms-excel"
onChange={onChange}
/>
) : props.formData ? (
<div
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
>
file selected : {props.formData.split("/")[3]}
{props.formData ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-[3px] w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {props.formData?.split("/")[3]?.split("_")[1]}
</div>
<div
onClick={() => {
console.log("clicked");
setFileUpload([]);
props.onChange(undefined);
}}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
) : (
<input
type="file"
id="hashfile"
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
accept="application/pdf,application/vnd.ms-excel"
onChange={onChange}
/>
<div className="flex gap-2 justify-center items-center">
<input
type="file"
id="hashfile"
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
accept="application/pdf,application/vnd.ms-excel"
onChange={onChange}
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
)}
</>
</React.Fragment>
@@ -0,0 +1,261 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../primitives/ModalUi";
const SelectFolder = ({ required, onSuccess, folderCls }) => {
const [isOpen, SetIsOpen] = useState(false);
const [clickFolder, setClickFolder] = useState("");
const [selectFolder, setSelectedFolder] = useState({});
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [folderPath, setFolderPath] = useState("");
const [isAdd, setIsAdd] = useState(false);
useEffect(() => {
if (isOpen) {
setIsAdd(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
fetchFolder();
}
// eslint-disable-next-line
}, [isOpen]);
const fetchFolder = async (folderPtr) => {
setIsLoader(true);
try {
const FolderQuery = new Parse.Query(folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
setIsLoader(false);
}
setIsLoader(false);
}
} catch (error) {
setIsLoader(false);
}
};
const handleSelect = (item) => {
setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
if (tabList.length > 0) {
const tab = tabList.some((x) => x.objectId === item.objectId);
if (!tab) {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
} else {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
};
const handleSubmit = () => {
let url = "Root";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
setFolderPath(url);
setSelectedFolder(clickFolder);
if (onSuccess) {
onSuccess(clickFolder);
}
SetIsOpen(false);
};
const handleCancel = () => {
SetIsOpen(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
};
const removeTabListItem = async (e, i) => {
e.preventDefault();
// setEditable(false);
if (!isAdd) {
setIsLoader(true);
let folderPtr;
if (i) {
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
let _len = list.length - 1;
folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: list[_len].objectId
};
setTabList(list);
} else {
setClickFolder({});
setSelectedFolder({});
setFolderList([]);
setTabList([]);
}
fetchFolder(folderPtr);
}
};
const handleCreate = () => {
setIsAdd(!isAdd);
};
const handleAddFolder = () => {
setFolderList([]);
if (clickFolder && clickFolder.ObjectId) {
fetchFolder({
__type: "Pointer",
className: folderCls,
objectId: clickFolder.ObjectId
});
} else {
fetchFolder();
}
handleCreate();
};
return (
<div className="text-xs mt-2 ">
<div>
<label className="block">
Select Folder
{required && <span className="text-red-500 text-[13px]">*</span>}
</label>
</div>
<div className="rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
<div>
<i
className="far fa-folder-open text-[40px] text-[#33bbff]"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name ? selectFolder.Name : "Root"}
</p>
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
<i
className="fa fa-pencil"
title="Select Folder"
aria-hidden="true"
></i>
</div>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p>
</div>
</div>
<ModalUi
title={"Select Folder"}
isOpen={isOpen}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="Root"
onClick={(e) => removeTabListItem(e)}
>
Root /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<span
className="cursor-pointer"
title={tab.Name}
onClick={(e) => removeTabListItem(e, i)}
>
{tab.Name}
</span>
{" / "}
</React.Fragment>
))}
<hr />
</div>
<div className="mt-2 mb-3">
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer"
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div>
))}
{isAdd && (
<CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId}
folderCls={folderCls}
onSuccess={handleAddFolder}
/>
)}
{isLoader && (
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
</div>
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
</div>
);
};
export default SelectFolder;
@@ -0,0 +1,190 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AppendFormInForm from "../AppendFormInForm";
import Modal from "react-modal";
import Parse from "parse";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
* code changes required
*/
const SignersInput = (props) => {
Modal.setAppElement("body");
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = React.useState([]);
const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions);
const [modalIsOpen, setModalIsOpen] = useState(false);
const onSortEnd = ({ oldIndex, newIndex }) => {
const newValue = arrayMove(selected, oldIndex, newIndex);
setSelected(newValue);
};
const GetSelectListData = async () => {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
contactbook.equalTo(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactbook.notEqualTo("IsDeleted", true);
const contactRes = await contactbook.find();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
let list = [];
// let _selected = [];
res.forEach((x) => {
let obj = {
label: x.Name,
value: x.objectId,
isChecked: true
};
list.push(obj);
});
setState(list);
}
} catch (error) {
console.log("err", error);
}
};
useEffect(() => {
GetSelectListData();
}, []);
useEffect(() => {
if (selected && selected.length) {
let newData = [];
selected.forEach((x) => {
newData.push(x.value);
});
if (props.onChange) {
props.onChange(newData);
}
}
// eslint-disable-next-line
}, [selected]);
const handleModalCloseClick = () => {
setIsModel(false);
setModalIsOpen(false);
};
const openModal = () => {
setModalIsOpen(true);
};
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
const handleNewDetails = (data) => {
setState([...state, data]);
if (selected.length > 0) {
setSelected([...selected, data]);
} else {
setSelected([data]);
}
};
return (
<div className="text-xs mt-2 ">
<label className="block">
Signers
{props.required && <span className="text-red-500 text-[13px]">*</span>}
</label>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<Select
onSortEnd={onSortEnd}
distance={4}
isMulti
options={state || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
/>
</div>
<div
onClick={() => {
setIsModel(true);
openModal();
}}
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<i className="fas fa-plus"></i>
</div>
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
id="modal1"
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
type="button"
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
>
<div className=" text-black text-xl font-semibold pl-3">
Add Signer
</div>
<button onClick={handleModalCloseClick}>
<i
style={{ fontSize: 25 }}
className="fa fa-times-circle"
aria-hidden="true"
></i>
</button>
</div>
{isModal && (
<AppendFormInForm
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
</div>
</div>
);
};
export default SignersInput;
+2
View File
@@ -0,0 +1,2 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
+17
View File
@@ -15,3 +15,20 @@ export const useScript = (url, onload) => {
};
}, [url, onload]);
};
/**`useScript` hook is generated scripte for google sign in button */
export const useDropScript = (url, onload) => {
useEffect(() => {
const script = document.createElement("script");
//add url parameter to the script src, for load and it will remove after load in return
script.src = url;
script.async = true;
script.defer = true;
script.id = "dropboxjs";
script.setAttribute("data-app-key", "8k0thg9r1t7asqg");
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, [url, onload]);
};
+12 -1
View File
@@ -4,4 +4,15 @@
body{
background-color: white;
}
}
/* Custom CSS to hide scrollbar */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+3 -4
View File
@@ -14,7 +14,7 @@ export const formJson = (id) => {
type: "string",
title: "Select Document",
filetypes: [],
maxfilesizeKB: "5000",
maxfilesizeKB: "10000",
uploadtype: "regular",
helpbody: "",
helplink: ""
@@ -113,7 +113,7 @@ export const formJson = (id) => {
});
formData = {
jsonSchema: {
title: "New Document",
title: "Request signatures",
description: "",
type: "object",
required: ["URL", "Name", "Note", "TimeToCompleteDays", "Signers"],
@@ -147,7 +147,7 @@ export const formJson = (id) => {
class: "contracts_Contactbook",
displayKey: "Name",
valueKey: "objectId",
query: `where={"CreatedBy":${userPtr}}&keys=Name`,
query: `where={"CreatedBy":${userPtr},"IsDeleted":{"$ne":true}}&keys=Name`,
isPointer: true,
helpbody: "",
helplink: "",
@@ -582,4 +582,3 @@ export const formJson = (id) => {
return formData;
}
};
+32 -5
View File
@@ -1,7 +1,9 @@
export default function reportJson(id) {
// console.log("json ", json);
const head = ["Sr.No", "Name", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
switch (id) {
// draft documents report
case "ByHuevtCFY":
@@ -103,7 +105,7 @@ export default function reportJson(id) {
case "d9k3UfYHBc":
return {
reportName: "Recently sent for signatures",
heading: head,
heading: dashboardReportHead,
actions: [
{
btnLabel: "View",
@@ -119,7 +121,7 @@ export default function reportJson(id) {
case "5Go51Q7T8r":
return {
reportName: "Recent signature requests",
heading: head,
heading: dashboardReportHead,
actions: [
{
btnLabel: "Sign",
@@ -135,7 +137,7 @@ export default function reportJson(id) {
case "kC5mfynCi4":
return {
reportName: "Drafts",
heading: head,
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
actions: [
{
btnLabel: "sign",
@@ -159,6 +161,31 @@ export default function reportJson(id) {
textColor: "white",
btnIcon: "fa-solid fa-trash"
}
],
form: "ContactBook"
};
// template report
case "6TeaPr321t":
return {
reportName: "Templates",
heading: head,
actions: [
{
btnLabel: "Use",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/placeHolderSign"
},
{
btnLabel: "Edit",
btnColor: "#00c9d5",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/template"
}
]
};
default:
+1 -1
View File
@@ -52,7 +52,7 @@
"subtitle": "Customization available Priority support.",
"btnText": "Contact us",
"url": "https://www.opensignlabs.com/contact-us",
"target": "_self",
"target": "_blank",
"benefits": [
"All features",
"Custom domain",
+73 -23
View File
@@ -7,10 +7,35 @@ import Tour from "reactour";
import axios from "axios";
import { useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate } from "react-router-dom";
const HomeLayout = ({ children }) => {
const navigate = useNavigate();
const { width } = useWindowSize();
const [isOpen, setIsOpen] = useState(true);
const arr = useSelector((state) => state.TourSteps);
const [isUserValid, setIsUserValid] = useState(true);
useEffect(() => {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
setIsUserValid(true);
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
}, []);
// reactour state
const [isCloseBtn, setIsCloseBtn] = useState(true);
@@ -19,7 +44,7 @@ const HomeLayout = ({ children }) => {
const [tourConfigs, setTourConfigs] = useState([]);
const showSidebar = () => {
setIsOpen(value => !value);
setIsOpen((value) => !value);
};
useEffect(() => {
if (width && width <= 768) {
@@ -28,9 +53,7 @@ const HomeLayout = ({ children }) => {
}, [width]);
useEffect(() => {
if (localStorage.getItem("domain") === "sign" && arr && arr.length > 0) {
handleDynamicSteps();
} else if (
if (
localStorage.getItem("domain") === "contracts" &&
arr &&
arr.length > 0
@@ -153,32 +176,59 @@ const HomeLayout = ({ children }) => {
setIsOpen(false);
}
};
const handleLoginBtn = () => {
try {
Parse.User.logOut();
} catch (err) {
console.log("err ", err);
} finally {
localStorage.removeItem("accesstoken");
navigate("/", { replace: true });
}
};
return (
<div>
<div className="sticky top-0 z-50">
<Header showSidebar={showSidebar} />
</div>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
{isUserValid ? (
<>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{children}</div>
<div className="z-30">
<Footer />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{children}</div>
<div className="z-30">
<Footer />
</div>
</div>
</div>
</div>
</div>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
</>
) : (
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-normal">Your session has expired.</p>
<button
onClick={handleLoginBtn}
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
>
Login
</button>
</div>
</ModalUi>
)}
</div>
);
};
+30
View File
@@ -0,0 +1,30 @@
import React from "react";
const Alert = ({ children, type }) => {
const textcolor = type ? theme(type) : theme();
function theme(color) {
switch (color) {
case "success":
return "border-[#c3e6cb] bg-[#d4edda] text-[#155724]";
case "info":
return "border-[#adcdeb] bg-[#c1daf0] text-[#153756]";
case "danger":
return "border-[#f0a8a8] bg-[#f4bebe] text-[#c42121]";
default:
return "border-[#d6d6d6] bg-[#d9d9d9] text-[#575757]";
}
}
return (
<>
{children && (
<div
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] `}
>
{children}
</div>
)}
</>
);
};
export default Alert;
+258 -44
View File
@@ -3,6 +3,8 @@ import pad from "../assets/images/pad.svg";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import "../styles/report.css";
import ModalUi from "./ModalUi";
import AppendFormInForm from "../components/AppendFormInForm";
const ReportTable = ({
ReportName,
List,
@@ -11,13 +13,19 @@ const ReportTable = ({
heading,
setIsNextRecord,
isMoreDocs,
docPerPage
docPerPage,
form
}) => {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const [actLoader, setActLoader] = useState({});
const [isAlert, setIsAlert] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isDocErr, setIsDocErr] = useState(false);
const [isContactform, setIsContactform] = useState(false);
const [isDeleteModal, setIsDeleteModal] = useState({});
const startIndex = (currentPage - 1) * docPerPage;
// For loop is used to calculate page numbers visible below table
// Initialize pageNumbers using useMemo to avoid unnecessary re-creation
const pageNumbers = useMemo(() => {
@@ -55,39 +63,128 @@ const ReportTable = ({
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
// `handlemicroapp` is used to open microapp
const handlemicroapp = (item, url) => {
localStorage.removeItem("rowlevel");
navigate("/rpmf/" + url);
localStorage.setItem("rowlevel", JSON.stringify(item));
const handlemicroapp = async (item, url, btnLabel) => {
if (ReportName === "Templates") {
if (btnLabel === "Edit") {
navigate(`/asmf/${url}/${item.objectId}`);
} else {
setActLoader({ [`${item.objectId}_${btnLabel}`]: true });
try {
const params = {
templateId: item.objectId
};
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
);
// console.log("templateDeatils.data ", templateDeatils.data);
const templateData =
templateDeatils.data && templateDeatils.data.result;
if (!templateData.error) {
const Doc = templateData;
let signers = [];
if (Doc.Signers?.length > 0) {
Doc.Signers?.forEach((x) => {
if (x.objectId) {
const obj = {
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x.objectId
};
signers.push(obj);
}
});
}
let placeholdersArr = [];
if (Doc.Placeholders?.length > 0) {
placeholdersArr = Doc.Placeholders;
const data = {
Name: Doc.Name,
URL: Doc.URL,
SignedUrl: Doc.SignedUrl,
Description: Doc.Description,
Note: Doc.Note,
Placeholders: placeholdersArr,
ExtUserPtr: {
__type: "Pointer",
className: "contracts_Users",
objectId: Doc.ExtUserPtr.objectId
},
CreatedBy: {
__type: "Pointer",
className: "_User",
objectId: Doc.CreatedBy.objectId
},
Signers: signers
};
try {
const res = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}classes/${localStorage.getItem("_appName")}_Document`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
"X-Parse-Session-Token":
localStorage.getItem("accesstoken")
}
}
);
// console.log("Res ", res.data);
if (res.data && res.data.objectId) {
setActLoader({});
setIsAlert(true);
navigate(`/asmf/${url}/${res.data.objectId}`, {
state: { title: "Use Template" }
});
}
} catch (err) {
console.log("Err", err);
setIsAlert(true);
setIsErr(true);
setActLoader({});
}
} else {
setIsDocErr(true);
setActLoader({});
}
} else {
setIsAlert(true);
setIsErr(true);
setActLoader({});
}
} catch (err) {
console.log("err", err);
setIsAlert(true);
setIsErr(true);
setActLoader({});
}
}
} else {
localStorage.removeItem("rowlevel");
navigate("/rpmf/" + url);
localStorage.setItem("rowlevel", JSON.stringify(item));
}
// localStorage.setItem("rowlevelMicro");
};
const handlebtn = async (item) => {
if (ReportName === "Contactbook") {
setActLoader({ [item.objectId]: true });
try {
const url =
process.env.REACT_APP_SERVERURL + "/classes/contracts_Contactbook/";
const body = { IsDeleted: true };
const res = await axios.put(url + item.objectId, body, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
});
// console.log("Res ", res.data);
if (res.data && res.data.updatedAt) {
setActLoader({});
setIsAlert(true);
const upldatedList = List.filter((x) => x.objectId !== item.objectId);
setList(upldatedList);
}
} catch (err) {
console.log("err", err);
setIsAlert(true);
setIsErr(true);
setActLoader({});
}
setIsDeleteModal({ [item.objectId]: true });
}
};
@@ -101,6 +198,44 @@ const ReportTable = ({
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
const handleContactFormModal = () => {
setIsContactform(!isContactform);
};
const handleUserData = (data) => {
setList((prevData) => [data, ...prevData]);
};
const handleDelete = async (item) => {
setIsDeleteModal({});
setActLoader({ [item.objectId]: true });
try {
const url =
process.env.REACT_APP_SERVERURL + "/classes/contracts_Contactbook/";
const body = { IsDeleted: true };
const res = await axios.put(url + item.objectId, body, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
});
// console.log("Res ", res.data);
if (res.data && res.data.updatedAt) {
setActLoader({});
setIsAlert(true);
const upldatedList = List.filter((x) => x.objectId !== item.objectId);
setList(upldatedList);
}
} catch (err) {
console.log("err", err);
setIsAlert(true);
setIsErr(true);
setActLoader({});
}
};
const handleCloseDeleteModal = () => setIsDeleteModal({});
return (
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
{isAlert && (
@@ -115,7 +250,23 @@ const ReportTable = ({
</div>
)}
<h2 className="text-[23px] font-light my-2">{ReportName}</h2>
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">{ReportName}</div>
{ReportName === "Templates" && (
<i
onClick={() => navigate("/form/template")}
className="fa-solid fa-square-plus text-sky-400 text-[25px]"
></i>
)}
{form && (
<div
className="cursor-pointer"
onClick={() => handleContactFormModal()}
>
<i className="fa-solid fa-square-plus text-sky-400 text-[25px]"></i>
</div>
)}
</div>
<table className="table-auto w-full border-collapse">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
@@ -135,11 +286,13 @@ const ReportTable = ({
{currentLists.map((item, index) =>
ReportName === "Contactbook" ? (
<tr className="border-y-[1px]" key={index}>
<td className="px-4 py-2">{index + 1}</td>
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Email || "-"}</td>
<td className="px-4 py-2">{item?.Phone || "-"}</td>
<td className="px-4 py-2 flex flex-col justify-center items-center gap-2 text-white">
<td className="px-3 py-2 text-white">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
@@ -149,7 +302,7 @@ const ReportTable = ({
? handlemicroapp(item, act.redirectUrl)
: handlebtn(item)
}
className={`flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
className={`mb-1 flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
style={{
backgroundColor: act.btnColor
? act.btnColor
@@ -175,16 +328,52 @@ const ReportTable = ({
)}
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete Contact"}
handleClose={handleCloseDeleteModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this contact?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="bg-[#1ab6ce] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-3 focus:outline-none"
>
Yes
</button>
<button
onClick={handleCloseDeleteModal}
className="bg-[#188ae2] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-3 text-center ml-[2px] focus:outline-none"
>
No
</button>
</div>
</div>
</ModalUi>
)}
</td>
</tr>
) : (
<tr className="border-y-[1px]" key={index}>
<td className="px-4 py-2">{index + 1}</td>
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Note || "-"}</td>
<td className="px-4 py-2">
{item?.Folder?.Name || "OpenSignDrive"}
{heading.includes("Sr.No") && (
<td className="px-4 py-2">{startIndex + index + 1}</td>
)}
<td className="px-4 py-2 font-semibold w-56">
{item?.Name}{" "}
</td>
{heading.includes("Note") && (
<td className="px-4 py-2">{item?.Note || "-"}</td>
)}
{heading.includes("Folder") && (
<td className="px-4 py-2">
{item?.Folder?.Name || "OpenSignDrive"}
</td>
)}
<td className="px-4 py-2">
<a
target="_blank"
@@ -202,17 +391,21 @@ const ReportTable = ({
<td className="px-4 py-2">
{item?.Signers ? formatRow(item?.Signers) : "-"}
</td>
<td className="px-4 py-2 flex flex-col justify-center items-center gap-2 text-white">
<td className="px-3 py-2 text-white">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() =>
act?.redirectUrl
? handlemicroapp(item, act.redirectUrl)
? handlemicroapp(
item,
act.redirectUrl,
act.btnLabel
)
: handlebtn(item)
}
className={`flex justify-center items-center w-full gap-1 px-2 py-1 rounded shadow`}
className={`mb-1 flex justify-center items-center gap-1 px-2 py-1 rounded shadow`}
style={{
backgroundColor: act.btnColor
? act.btnColor
@@ -224,7 +417,9 @@ const ReportTable = ({
{act?.btnIcon && (
<i
className={
actLoader
actLoader[
`${item.objectId}_${act.btnLabel}`
]
? "fa-solid fa-spinner fa-spin-pulse"
: act.btnIcon
}
@@ -235,7 +430,6 @@ const ReportTable = ({
{act?.btnLabel ? act.btnLabel : "view"}
</span>
</button>
// )
))}
</td>
</tr>
@@ -293,6 +487,26 @@ const ReportTable = ({
<div className="text-sm font-semibold">No Data Available</div>
</div>
)}
<ModalUi
title={"Add Contact"}
isOpen={isContactform}
handleClose={handleContactFormModal}
>
<AppendFormInForm
handleUserData={handleUserData}
closePopup={handleContactFormModal}
/>
</ModalUi>
<ModalUi
headColor={"#dc3545"}
isOpen={isDocErr}
title={"Receipent required"}
handleClose={() => setIsDocErr(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add receipent in template!</p>
</div>
</ModalUi>
</div>
);
};
+28
View File
@@ -0,0 +1,28 @@
import React, { useState } from "react";
const Modal = ({ children, Title }) => {
const [isOpen, SetIsOpen] = useState(false);
return (
<>
{children && (
<div
className={`fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm bg-white rounded `}
>
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem] ">
<div className="font-semibold text-xl text-black">{Title}</div>
<div
onClick={() => SetIsOpen()}
className="px-2 py-1 bg-gray-400 rounded cursor-pointer"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
<hr />
{isOpen && <div>{children}</div>}
</div>
)}
</>
);
};
export default Modal;
+40
View File
@@ -0,0 +1,40 @@
import React from "react";
const ModalUi = ({
children,
title,
isOpen,
headColor,
handleClose,
showHeader = true,
showClose = true
}) => {
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">
{showHeader && (
<div
className="flex justify-between rounded-t items-center py-[15px] px-[20px] text-white"
style={{ background: headColor ? headColor : "#32a3ac" }}
>
<div className="text-[1.2rem] font-normal">{title}</div>
{showClose && (
<div
className="text-[1.5rem] cursor-pointer"
onClick={() => handleClose && handleClose()}
>
&times;
</div>
)}
</div>
)}
<div>{children}</div>
</div>
</div>
)}
</>
);
};
export default ModalUi;
@@ -0,0 +1,322 @@
import React, { useState } from "react";
import sanitizeFileName from "./sanitizeFileName";
import Parse from "parse";
import DropboxChooser from "../components/fields/DropboxChoose";
import Alert from "./Alert";
import SelectFolder from "../components/fields/SelectFolder";
// import SignersInput from "../components/fields/SignersInput";
import Title from "../components/Title";
import { useNavigate } from "react-router-dom";
import { templateCls } from "../constant/const";
const TemplateForm = () => {
const navigate = useNavigate();
const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: "",
Description: "",
Note: "Please review and sign this document"
});
const [fileupload, setFileUpload] = useState([]);
const [fileload, setfileload] = useState(false);
const [percentage, setpercentage] = useState(0);
const [isAlert, setIsAlert] = useState(false);
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleFileInput = (e) => {
setpercentage(0);
try {
let files = e.target.files;
if (typeof files[0] !== "undefined") {
const mb = Math.round(files[0].bytes / Math.pow(1024, 2));
if (mb > 10) {
alert(
`The selected file size is too large. Please select a file less than ${Math.round(
10
)} MB`
);
return;
}
handleFileUpload(files[0]);
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const handleFileUpload = async (file) => {
Parse.serverURL = process.env.REACT_APP_SERVERURL;
Parse.initialize(process.env.REACT_APP_APPID);
setfileload(true);
const fileName = file.name;
const name = sanitizeFileName(fileName);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
setfileload(true);
const file = files[0];
const url = file.link;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than 10 MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const currentUser = Parse.User.current();
const template = new Parse.Object(templateCls);
Object.entries(formData).forEach((item) =>
template.set(item[0], item[1])
);
template.set("URL", fileupload);
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (folder && folder.ObjectId) {
template.set("Folder", {
__type: "Pointer",
className: templateCls,
objectId: folder.ObjectId
});
}
if (signers && signers.length > 0) {
template.set("Signers", signers);
}
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
template.set("ExtUserPtr", {
__type: "Pointer",
className: "contracts_Users",
objectId: ExtCls[0].objectId
});
const res = await template.save();
if (res) {
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note: ""
});
setFileUpload([]);
setpercentage(0);
navigate(
"/asmf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/template/" +
res.id
);
}
} catch (err) {
console.log("err ", err);
setIsAlert(true);
}
};
const handleFolder = (data) => {
setFolder(data);
};
// const handleSigners = (data) => {
// if (data && data.length > 0) {
// const updateSigners = data.map((x) => ({
// __type: "Pointer",
// className: "contracts_Contactbook",
// objectId: x
// }));
// setSigners(updateSigners);
// }
// };
const handleReset = () => {
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note: ""
});
setFileUpload([]);
setpercentage(0);
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title="New Template" />
{isAlert && <Alert type="success">Template created successfully!</Alert>}
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">New Template</h1>
{fileload && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
)}
<div className="text-xs">
<label className="block">
File<span className="text-red-500 text-[13px]">*</span>
</label>
{fileupload.length > 0 ? (
<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("/")[3]?.split("_")[1]}
</div>
<div
onClick={() => {
setFileUpload([]);
}}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center">
<input
type="file"
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
onChange={(e) => handleFileInput(e)}
accept="application/pdf,application/vnd.ms-excel"
required
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
)}
</div>
<div className="text-xs mt-2">
<label className="block">
Title<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Name"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">
Note<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Note"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Description</label>
<input
name="Description"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
/>
</div>
{/* <SignersInput onChange={handleSigners} /> */}
<SelectFolder onSuccess={handleFolder} folderCls={templateCls} />
<div className="flex items-center mt-3 gap-2 text-white">
<button
className="bg-[#1ab6ce] rounded-sm shadow-md text-[14px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
type="submit"
>
Submit
</button>
<div
className="bg-[#188ae2] rounded-sm shadow-md text-[14px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
onClick={() => handleReset()}
>
Reset
</div>
</div>
</form>
</div>
);
};
export default TemplateForm;
@@ -0,0 +1,34 @@
import React, { useEffect } from "react";
import Parse from "parse";
const ValidateRoute = ({ children }) => {
useEffect(() => {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (!user) {
handlelogout();
}
} catch (error) {
handlelogout();
}
})();
}, []);
const handlelogout = async () => {
try {
Parse.User.logOut();
localStorage.removeItem("accesstoken");
} catch (err) {
console.log("err ", err);
} finally {
localStorage.removeItem("accesstoken");
}
};
return <div>{children}</div>;
};
export default ValidateRoute;
+14 -9
View File
@@ -25,6 +25,7 @@ import TreeWidget from "../components/TreeWidget";
import parse from "html-react-parser";
import Title from "../components/Title";
import { formJson } from "../json/FormJson";
import TemplateForm from "../primitives/TemplateForm";
const widget = {
TimeWidget: TimeWidget
};
@@ -41,15 +42,19 @@ const fields = () => {
function FormBuilderFn(props) {
const { id } = useParams();
const navigate = useNavigate();
return (
<FormBuilder
removeState={props.removeState}
removeLevel2State={props.removeLevel2State}
removeLevel3State={props.removeLevel3State}
id={id}
navigate={navigate}
/>
);
if (id === "template") {
return <TemplateForm />;
} else {
return (
<FormBuilder
removeState={props.removeState}
removeLevel2State={props.removeLevel2State}
removeLevel3State={props.removeLevel3State}
id={id}
navigate={navigate}
/>
);
}
}
class FormBuilder extends Component {
state = {
+2 -2
View File
@@ -74,8 +74,8 @@ const PgSignUp = (props) => {
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("res", res);
if (res) {
const checkUser = new Parse.Query(extClass);
const updateQuery = await checkUser.get(res.id);
const updateQuery = new Parse.Object(extClass);
updateQuery.id = res.id;
updateQuery.set(
"Next_billing_date",
new Date(zohoRes.data.result.nextBillingDate)
@@ -136,8 +136,13 @@ const PlanSubscriptions = () => {
<p>{item.subtitle}</p>
</div>
</div>
<NavLink
to={item.url + details}
to={
item.btnText === "Subscribe"
? item.url + details
: item.url
}
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white"
target={item.target}
>
+3
View File
@@ -15,6 +15,7 @@ const Report = () => {
const [heading, setHeading] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true);
const [form, setForm]= useState("")
const abortController = new AbortController();
const docPerPage = 10;
@@ -50,6 +51,7 @@ const Report = () => {
setActions(json.actions);
setHeading(json.heading);
setReportName(json.reportName);
setForm(json.form)
Parse.serverURL = localStorage.getItem("BaseUrl12");
Parse.initialize(localStorage.getItem("AppID12"));
const currentUser = Parse.User.current().id;
@@ -154,6 +156,7 @@ const Report = () => {
setIsNextRecord={setIsNextRecord}
isMoreDocs={isMoreDocs}
docPerPage={docPerPage}
form={form}
/>
) : (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
+111 -109
View File
@@ -158,124 +158,126 @@ function UserProfile() {
></div>
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
<img
className="object-contain w-full h-full"
src={Image === "" ? dp : Image}
alt="dp"
/>
</div>
{editmode && (
<input
type="file"
className="max-w-[270px] text-sm py-1 px-2 mt-4 border-[1px] border-[#15b4e9] text-black rounded"
accept="image/png, image/gif, image/jpeg"
onChange={(e) => {
let files = e.target.files;
fileUpload(files[0]);
}}
/>
)}
{percentage !== 0 && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
<div className="flex justify-center items-center w-full">
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
<img
className="object-contain w-full h-full"
src={Image === "" ? dp : Image}
alt="dp"
/>
</div>
{editmode && (
<input
type="file"
className="max-w-[270px] text-sm py-1 px-2 mt-4 border-[1px] border-[#15b4e9] text-black rounded"
accept="image/png, image/gif, image/jpeg"
onChange={(e) => {
let files = e.target.files;
fileUpload(files[0]);
}}
/>
)}
{percentage !== 0 && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
)}
<div className="text-base font-semibold pt-4">
{localStorage.getItem("_user_role")}
</div>
)}
<div className="text-base font-semibold pt-4">
{localStorage.getItem("_user_role")}
</div>
</div>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
}`}
>
<span>Name:</span>{" "}
{editmode ? (
<input
type="text"
value={name}
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetName(e.target.value)}
/>
) : (
<span>{localStorage.getItem("username")}</span>
)}
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
}`}
>
<span>Phone:</span>{" "}
{editmode ? (
<input
type="text"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetPhone(e.target.value)}
value={Phone}
/>
) : (
<span>{UserProfile && UserProfile.phone}</span>
)}
</li>
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span>Email:</span>{" "}
<span>{UserProfile && UserProfile.email}</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<span>Is Email verified:</span>{" "}
<span>
{UserProfile && UserProfile.emailVerified
? "Verified"
: "Not verified"}
</span>
</li>
</ul>
<div className="flex justify-center pb-4">
{editmode ? (
<button
type="button"
onClick={handleSubmit}
className="rounded bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-2 mr-4"
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
}`}
>
Save
</button>
) : (
<span className="font-semibold">Name:</span>{" "}
{editmode ? (
<input
type="text"
value={name}
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetName(e.target.value)}
/>
) : (
<span>{localStorage.getItem("username")}</span>
)}
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
}`}
>
<span className="font-semibold">Phone:</span>{" "}
{editmode ? (
<input
type="text"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
onChange={(e) => SetPhone(e.target.value)}
value={Phone}
/>
) : (
<span>{UserProfile && UserProfile.phone}</span>
)}
</li>
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Email:</span>{" "}
<span>{UserProfile && UserProfile.email}</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Is Email verified:</span>{" "}
<span>
{UserProfile && UserProfile.emailVerified
? "Verified"
: "Not verified"}
</span>
</li>
</ul>
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
{editmode ? (
<button
type="button"
onClick={handleSubmit}
className="rounded shadow focus:outline-none border-[2px] border-[#15b4e9] bg-white text-[#15b4e9] px-4 py-2 mr-4"
>
Save
</button>
) : (
<button
type="button"
onClick={() => {
setEditMode(true);
}}
className="rounded shadow focus:outline-none text-white bg-[#e7505a] px-4 py-2 mr-4"
>
Edit
</button>
)}
<button
type="button"
onClick={() => {
setEditMode(true);
if (editmode) {
setEditMode(false);
} else {
navigate("/changepassword");
}
}}
className="rounded shadow text-white bg-[#e7505a] px-4 py-2 mr-4"
className={`rounded shadow focus:outline-none text-white bg-[#3598dc] ${
editmode ? "px-4 py-2 " : "p-2"
}`}
>
Edit
{editmode ? "Cancel" : "Change Password"}
</button>
)}
<button
type="button"
onClick={() => {
if (editmode) {
setEditMode(false);
} else {
navigate("/changepassword");
}
}}
className={`rounded shadow text-white bg-[#3598dc] ${
editmode ? "px-4 py-2 " : "p-2"
}`}
>
{editmode ? "Cancel" : "Change Password"}
</button>
</div>
</div>
</div>
)}
+2 -4
View File
@@ -2211,7 +2211,5 @@
background-position: -2em 0;
}
}
.box:nth-of-type(n + 43) {
display: none;
}
+9 -3
View File
@@ -19,6 +19,8 @@ import getDrive from './parsefunction/getDrive.js';
import getReport from './parsefunction/getReport.js';
import generateApiToken from './parsefunction/generateApiToken.js';
import getapitoken from './parsefunction/getapitoken.js';
import TemplateAfterSave from './parsefunction/TemplateAfterSave.js';
import GetTemplate from './parsefunction/GetTemplate.js';
Parse.Cloud.define('AddUserToRole', addUserToGroups);
Parse.Cloud.define('UserGroups', getUserGroups);
@@ -28,9 +30,6 @@ Parse.Cloud.define('googlesign', GoogleSign);
Parse.Cloud.define('zohodetails', ZohoDetails);
Parse.Cloud.define('usersignup', usersignup);
Parse.Cloud.define('facebooksign', FacebookSign);
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.define('SendOTPMailV1', sendMailOTPv1);
Parse.Cloud.define('sendmail', SendMailv1);
Parse.Cloud.define('AuthLoginAsMail', AuthLoginAsMail);
@@ -41,3 +40,10 @@ Parse.Cloud.define('getDrive', getDrive);
Parse.Cloud.define('getReport', getReport);
Parse.Cloud.define('generateapitoken', generateApiToken);
Parse.Cloud.define('getapitoken', getapitoken);
Parse.Cloud.define('getDrive', getDrive)
Parse.Cloud.define('getReport', getReport)
Parse.Cloud.define("getTemplate", GetTemplate)
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave("contracts_Template", TemplateAfterSave)
@@ -16,6 +16,7 @@ async function ContactbookAftersave(request) {
acl.setWriteAccess(object.get('UserId'), true);
object.setACL(acl);
object.set('IsDeleted', false)
// Continue saving the object
return object.save(null, { useMasterKey: true });
}
@@ -0,0 +1,53 @@
import axios from 'axios';
export default async function GetTemplate(request) {
const serverUrl = process.env.SERVER_URL;
const templateId = request.params.templateId;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
// console.log("templateId ", templateId)
// console.log("userId ",userId)
if (templateId && userId) {
try {
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreateBy');
const res = await template.first({ useMasterKey: true });
// console.log("res ", res)
if (res) {
// console.log("res ",res)
const acl = res.getACL();
console.log("acl", acl.getReadAccess(userId))
if (acl && acl.getReadAccess(userId)) {
return res;
} else {
return { error: "You don't have access of this document!" };
}
} else {
return { error: "You don't have access of this document!" };
}
} catch (err) {
console.log('err', err);
return err;
}
} else {
return { error: 'Please pass required parameters!' };
}
} catch (err) {
console.log('err', err);
if (err.code == 209) {
return { error: 'Invalid session token' };
} else {
return { error: "You don't have access of this document!" };
}
}
}
@@ -0,0 +1,83 @@
export default async function TemplateAfterSave(request) {
try {
if (!request.original) {
console.log('new entry is insert in contracts_Template');
// update acl of New Document If There are signers present in array
const signers = request.object.get('Signers');
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
}
} else {
if (request.user) {
const signers = request.object.get('Signers');
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
}
}
}
} catch (err) {
console.log('err in aftersave of contracts_Template');
console.log(err);
}
async function updateAclDoc(objId) {
// console.log("In side updateAclDoc func")
// console.log(objId)
const Query = new Parse.Query('contracts_Template');
Query.include('Signers');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
// console.log(JSON.stringify(res));
const UsersPtr = res.Signers.map(item => item.UserId);
if (res.Signers[0].ExtUserPtr) {
const ExtUserSigners = res.Signers.map(item => {
return {
__type: 'Pointer',
className: 'contracts_Users',
objectId: item.ExtUserPtr.objectId,
};
});
updateACL.set('Signers', ExtUserSigners);
}
// console.log("UsersPtr")
// console.log(JSON.stringify(UsersPtr))
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
UsersPtr.forEach(x => {
newACL.setReadAccess(x.objectId, true);
newACL.setWriteAccess(x.objectId, true);
});
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
async function updateSelfDoc(objId) {
// console.log("Inside updateSelfDoc func")
const Query = new Parse.Query('contracts_Template');
const updateACL = await Query.get(objId, { useMasterKey: true });
// const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
// console.log(JSON.stringify(res));
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
}
@@ -21,6 +21,7 @@ export default async function getDocument(request) {
query.include('Signers');
query.include('AuditTrail.UserPtr');
query.include('Placeholders');
query.notEqualTo('IsArchive', true)
const res = await query.first({ useMasterKey: true });
if (res) {
const acl = res.getACL();
@@ -40,7 +41,7 @@ export default async function getDocument(request) {
return { error: 'Please pass required parameters!' };
}
} catch (err) {
console.log('err');
console.log('err', err);
if (err.code == 209) {
return { error: 'Invalid session token' };
} else {
@@ -16,9 +16,9 @@ export default async function getDrive(request) {
if (userId) {
let url;
if (docId) {
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"$or":[{"CreatedBy":{"$exists":false}},{"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"}}]}&include=ExtUserPtr,Signers,Folder`;
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers,Folder`;
} else {
url = `${classUrl}?where={"Folder":{"$exists":false},"$or":[{"CreatedBy":{"$exists":false}},{"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"}}]}&include=ExtUserPtr,Signers`;
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers`;
}
try {
const res = await axios.get(url, {
@@ -19,7 +19,7 @@ export default async function getReport(request) {
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const json = reportId && reportJson(reportId, userId);
const clsName = reportId === '5KhaPr482K' ? 'contracts_Contactbook' : 'contracts_Document';
const clsName = json?.reportClass ? json.reportClass : 'contracts_Document';
if (json) {
const { params, keys } = json;
const orderBy = '-updatedAt';
+110 -90
View File
@@ -2,7 +2,7 @@ import SignPDF from './SignPDF.min.cjs';
import fs from 'node:fs';
import axios from 'axios';
import FormData from 'form-data';
import plainplaceholder from './customSignPdf/plainplaceholder.min.js';
// import plainplaceholder from './customSignPdf/plainplaceholder.min.js';
import { plainAddPlaceholder } from 'node-signpdf/dist/helpers/index.js';
const serverUrl = process.env.SERVER_URL,
APPID = process.env.APP_ID,
@@ -19,20 +19,20 @@ async function uploadFile(a) {
console.log('err ', e), fs.unlinkSync(a);
}
}
async function updateDoc(t, s, r, i, o, n) {
async function updateDoc(t, s, r, i, n, o) {
try {
var d = {
UserPtr: { __type: 'Pointer', className: n, objectId: r },
UserPtr: { __type: 'Pointer', className: o, objectId: r },
SignedUrl: s,
Activity: 'Signed',
ipAddress: i,
};
let e;
var l = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, d] : [d]).filter(
var l = (e = n.AuditTrail && 0 < n.AuditTrail.length ? [...n.AuditTrail, d] : [d]).filter(
e => 'Signed' === e.Activity
);
let a = !1;
!((o.Signers && 0 < o.Signers.length && l.length !== o.Signers.length) || !(a = !0));
!((n.Signers && 0 < n.Signers.length && l.length !== n.Signers.length) || !(a = !0));
var p = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
await axios.put(serverUrl + '/classes/contracts_Document/' + t, p, {
headers: {
@@ -61,7 +61,7 @@ async function sendMail(e) {
s +
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
t.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p></div></div></body></html>',
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>'
};
await axios.post(serverUrl + '/functions/sendmailv3', a, {
headers: {
@@ -86,7 +86,7 @@ async function sendCompletedMail(e) {
s +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
t.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p></div></div></body></html>',
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
};
await axios.post(serverUrl + '/functions/sendmailv3', a, {
headers: {
@@ -96,115 +96,135 @@ async function sendCompletedMail(e) {
},
});
}
async function PDF(s, r) {
async function PDF(i, n) {
try {
var i = s.params.sign,
e = s.params.docId,
o = s.params.userId,
n = await axios.get(serverUrl + '/classes/contracts_Document/' + e + '?include=ExtUserPtr', {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}),
d = await axios.get(serverUrl + '/users/me', {
i.params.sign;
var e = i.params.docId,
a = i.params.userId,
o = await axios.get(
serverUrl + '/classes/contracts_Document/' + e + '?include=ExtUserPtr,Signers',
{
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}
),
t = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': s.headers.sessiontoken,
'X-Parse-Session-Token': i.headers.sessiontoken,
},
});
if (!d.data || !d.data.objectId) return { status: 'error', message: 'this user not allowed!' };
if (!t.data || !t.data.objectId) return { status: 'error', message: 'this user not allowed!' };
{
var l,
var d,
l,
p,
c,
m = JSON.stringify({ objectId: o });
let a, t;
t = o
? (l = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + m, {
c = JSON.stringify({ objectId: a });
let s, r;
r = a
? (d = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': s.headers.sessiontoken,
'X-Parse-Session-Token': i.headers.sessiontoken,
},
})).data && 0 < l.data.results.length
? ((a = l), 'contracts_Contactbook')
: ((a = await axios.get(serverUrl + '/classes/contracts_Users?where=' + m, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
})).data && 0 < d.data.results.length
? ((s = d), 'contracts_Contactbook')
: ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})),
'contracts_Users')
: ((p = JSON.stringify({
UserId: { __type: 'Pointer', className: '_User', objectId: d.data.objectId },
: ((l = JSON.stringify({
UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId },
})),
(c = await axios.get(serverUrl + '/classes/contracts_Users?where=' + p, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
})).data && 0 < c.data.results.length
? ((a = c), 'contracts_Users')
: ((a = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + p, {
(p = await axios.get(serverUrl + '/classes/contracts_Users?where=' + l, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})).data && 0 < p.data.results.length
? ((s = p), 'contracts_Users')
: ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + l, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': s.headers.sessiontoken,
'X-Parse-Session-Token': i.headers.sessiontoken,
},
})),
'contracts_Contactbook'));
var g = a.data.results[0].Name,
h = a.data.results[0].Email;
if (!s.params.pdfFile) return { status: 'error', message: 'pdf file not present!' };
var m = s.data.results[0].Name,
g = s.data.results[0].Email;
if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' };
{
let e = Buffer.from(s.params.pdfFile, 'base64');
let e = Buffer.from(i.params.pdfFile, 'base64');
var u = process.env.PFX_BASE64,
f = Buffer.from(u, 'base64');
e = i
? plainplaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + g + ' <' + h + '>',
location: 'test location',
signatureLength: 1e4,
sign: i,
})
: plainAddPlaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + g + ' <' + h + '>',
location: 'test location',
signatureLength: 1e4,
});
var y = await new SignPDF(e, f).signPDF(),
v = `./exports/exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
P = (fs.writeFileSync(v, y), await uploadFile(v));
if (P && P.imageUrl) {
const r = await updateDoc(
s.params.docId,
P.imageUrl,
a.data.results[0].objectId,
s.headers['x-real-ip'],
n.data,
t
h = Buffer.from(u, 'base64'),
f = {
UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId },
SignedUrl: '',
Activity: 'Signed',
ipAddress: i.headers['x-real-ip'],
};
let a;
var y = (a =
o.data.AuditTrail && 0 < o.data.AuditTrail.length
? [...o.data.AuditTrail, f]
: [f]).filter(e => 'Signed' === e.Activity);
let t = !1;
!(
(o.data.Signers && 0 < o.data.Signers.length && y.length !== o.data.Signers.length) ||
!(t = !0)
);
var v,
P,
x = `./exports/exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
A =
(t
? ((v = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
(e =
v && 0 < v.length
? plainAddPlaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + v?.join(', '),
location: 'location',
signatureLength: 1e4,
})
: plainAddPlaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + m + ' <' + g + '>',
location: 'location',
signatureLength: 1e4,
})),
(P = await new SignPDF(e, h).signPDF()),
fs.writeFileSync(x, P))
: fs.writeFileSync(x, e),
await uploadFile(x));
if (A && A.imageUrl) {
const n = await updateDoc(
i.params.docId,
A.imageUrl,
s.data.results[0].objectId,
i.headers['x-real-ip'],
o.data,
r
);
return (
sendMail({
url: P.imageUrl,
sender: { Mail: n.data.ExtUserPtr.Email, Name: n.data.ExtUserPtr.Name },
pdfName: n.data.Name,
receiver: h,
url: A.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name },
pdfName: o.data.Name,
receiver: g,
}),
r &&
r.isCompleted &&
n &&
n.isCompleted &&
sendCompletedMail({
url: P.imageUrl,
sender: { Mail: n.data.ExtUserPtr.Email, Name: 'Open sign' },
pdfName: n.data.Name,
receiver: n.data.ExtUserPtr.Email,
url: A.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: 'Open sign' },
pdfName: o.data.Name,
receiver: o.data.ExtUserPtr.Email,
}),
fs.unlinkSync(v),
console.log('New Signed PDF created called: ' + v),
'success' === r.message
? { status: 'success', data: P.imageUrl }
fs.unlinkSync(x),
console.log('New Signed PDF created called: ' + x),
'success' === n.message
? { status: 'success', data: A.imageUrl }
: { status: 'error', message: 'please provide required parameters!' }
);
}
@@ -7,11 +7,11 @@ export default function reportJson(id, userId) {
return {
reportName: 'Draft Documents',
params: {
Type: null,
$or: [
{ Signers: null, SignedUrl: null },
{ Signers: { $exists: true }, Placeholders: null },
],
Type: { $ne: 'Folder' },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [{Signers:{$eq:[]}}, { Signers: null }, { Signers: { $exists: true }, Placeholders: null }],
CreatedBy: {
__type: 'Pointer',
className: '_User',
@@ -28,10 +28,23 @@ export default function reportJson(id, userId) {
Type: { $ne: 'Folder' },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
Placeholders: { $ne: null },
Signers: {
$inQuery: {
where: {
UserId: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
className: 'contracts_Contactbook',
},
},
},
keys: [
'Name',
@@ -50,10 +63,11 @@ export default function reportJson(id, userId) {
reportName: 'In-progress documents',
params: {
Type: { $ne: 'Folder' },
Signers: { $ne: null },
Signers: { $exists: true, $ne: [] },
Placeholders: { $ne: null },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
@@ -70,7 +84,7 @@ export default function reportJson(id, userId) {
return {
reportName: 'Completed Documents',
params: {
Type: null,
Type: { $ne: 'Folder' },
IsCompleted: true,
CreatedBy: {
__type: 'Pointer',
@@ -78,6 +92,7 @@ export default function reportJson(id, userId) {
objectId: currentUserId,
},
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
},
keys: [
'Name',
@@ -96,6 +111,7 @@ export default function reportJson(id, userId) {
reportName: 'Declined Documents',
params: {
Type: null,
IsArchive: { $ne: true },
IsDeclined: true,
CreatedBy: {
__type: 'Pointer',
@@ -113,6 +129,7 @@ export default function reportJson(id, userId) {
params: {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
Type: { $ne: 'Folder' },
$and: [
{
@@ -141,10 +158,11 @@ export default function reportJson(id, userId) {
reportName: 'Recently sent for signatures',
params: {
Type: { $ne: 'Folder' },
Signers: { $ne: null },
Signers: { $exists: true, $ne: [] },
Placeholders: { $ne: null },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
@@ -154,7 +172,7 @@ export default function reportJson(id, userId) {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: ['Name', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
};
// Recent signature requests report show on dashboard
case '5Go51Q7T8r':
@@ -164,32 +182,36 @@ export default function reportJson(id, userId) {
Type: { $ne: 'Folder' },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
Placeholders: { $ne: null },
Signers: {
$inQuery: {
where: {
UserId: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
className: 'contracts_Contactbook',
},
},
},
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.UserId',
'AuditTrail',
],
keys: ['Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name', 'Signers.UserId', 'AuditTrail'],
};
// Drafts report show on dashboard
case 'kC5mfynCi4':
return {
reportName: 'Drafts',
params: {
Type: null,
$or: [
{ Signers: null, SignedUrl: null },
{ Signers: { $exists: true }, Placeholders: null },
],
Type: { $ne: 'Folder' },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [{Signers:{$eq:[]}}, { Signers: null }, { Signers: { $exists: true }, Placeholders: null }],
CreatedBy: {
__type: 'Pointer',
className: '_User',
@@ -202,6 +224,7 @@ export default function reportJson(id, userId) {
case '5KhaPr482K':
return {
reportName: 'Contactbook',
reportClass: 'contracts_Contactbook',
params: {
CreatedBy: {
__type: 'Pointer',
@@ -212,6 +235,22 @@ export default function reportJson(id, userId) {
},
keys: ['Name', 'Email', 'Phone'],
};
// Templates report
case '6TeaPr321t':
return {
reportName: 'Templates',
reportClass: 'contracts_Template',
params: {
Type: { $ne: 'Folder' },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
};
default:
return null;
}
@@ -38,7 +38,7 @@ exports.up = async Parse => {
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
}
],
},
{
@@ -0,0 +1,588 @@
/**
*
* @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: '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',
},
],
},
]);
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: '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',
},
],
},
]);
// 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: 'fas fa-file-signature',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
],
},
{
icon: 'fas fa-folder',
title: 'OpenSignDrive™',
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: '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',
},
],
},
]);
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: 'fas fa-file-signature',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
],
},
{
icon: 'fas fa-folder',
title: 'OpenSignDrive™',
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: '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',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [revertUserMenu, revertAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
@@ -0,0 +1,40 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const className = 'contracts_Template';
const schema = new Parse.Schema(className);
schema.addString('Name');
schema.addString('URL');
schema.addString('Note');
schema.addString('Description');
schema.addArray('Signers');
schema.addBoolean('IsArchive');
schema.addArray('Placeholders');
schema.addPointer('Folder', 'contracts_Template');
schema.addString('Type');
schema.addPointer('CreatedBy', '_User');
schema.addPointer('ExtUserPtr', 'contracts_Users');
schema.addBoolean('EnablePhoneOTP')
schema.addBoolean('EnableEmailOTP')
schema.addBoolean('SendinOrder')
schema.addBoolean('SentToOthers')
schema.addBoolean('AutomaticReminders')
return schema.save();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const className = 'contracts_Template';
const schema = new Parse.Schema(className);
return schema.purge().then(() => schema.delete());
};
@@ -0,0 +1,23 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
// TODO: set className here
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.addBoolean('IsArchive');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.deleteField('IsArchive');
return schema.update();
};
Binary file not shown.
+3 -7
View File
@@ -70,7 +70,9 @@ if (process.env.SMTP_ENABLE) {
export const config = {
databaseURI:
process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev',
cloud: process.env.CLOUD || __dirname + '/cloud/main.js',
cloud: function () {
import('./cloud/main.js');
},
appId: process.env.APP_ID || 'myAppId',
masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
masterKeyIps: ['0.0.0.0/0', '::1'], // '::1'
@@ -174,12 +176,6 @@ app.get('/', function (req, res) {
res.status(200).send('open-sign-server is running !!!');
});
// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function (req, res) {
res.sendFile(path.join(__dirname, '/public/test.html'));
});
if (!process.env.TESTING) {
const port = process.env.PORT || 8080;
const httpServer = http.createServer(app);
+200 -2
View File
@@ -29,11 +29,13 @@
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-helmet": "^6.1.0",
"react-pdf": "^7.4.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6",
"reactour": "^1.19.1",
"select-dom": "^9.0.0",
@@ -2310,6 +2312,65 @@
"postcss-selector-parser": "^6.0.10"
}
},
"node_modules/@emotion/babel-plugin": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0",
"source-map": "^0.5.7",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@emotion/cache": {
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
"integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
"dependencies": {
"@emotion/memoize": "^0.8.1",
"@emotion/sheet": "^1.2.2",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"stylis": "4.2.0"
}
},
"node_modules/@emotion/hash": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
},
"node_modules/@emotion/is-prop-valid": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
@@ -2322,8 +2383,52 @@
"node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
"peer": true
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/react": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz",
"integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
"hoist-non-react-statics": "^3.3.1"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"dependencies": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.1",
"csstype": "^3.0.2"
}
},
"node_modules/@emotion/serialize/node_modules/@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
},
"node_modules/@emotion/sheet": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
"integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
},
"node_modules/@emotion/stylis": {
"version": "0.8.5",
@@ -2337,6 +2442,24 @@
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
"peer": true
},
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@emotion/utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
},
"node_modules/@emotion/weak-memoize": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -10608,6 +10731,11 @@
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -14810,6 +14938,11 @@
"node": ">= 4.0.0"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -17748,6 +17881,25 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-helmet": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
"integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
"dependencies": {
"object-assign": "^4.1.1",
"prop-types": "^15.7.2",
"react-fast-compare": "^3.1.1",
"react-side-effect": "^2.1.0"
},
"peerDependencies": {
"react": ">=16.3.0"
}
},
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -18003,6 +18155,34 @@
"react": ">=16.0.0"
}
},
"node_modules/react-select": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz",
"integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==",
"dependencies": {
"@babel/runtime": "^7.12.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.8.1",
"@floating-ui/dom": "^1.0.1",
"@types/react-transition-group": "^4.4.0",
"memoize-one": "^6.0.0",
"prop-types": "^15.6.0",
"react-transition-group": "^4.3.0",
"use-isomorphic-layout-effect": "^1.1.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-side-effect": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
"integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
"peerDependencies": {
"react": "^16.3.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-signature-canvas": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/react-signature-canvas/-/react-signature-canvas-1.0.6.tgz",
@@ -19527,6 +19707,11 @@
"postcss": "^8.2.15"
}
},
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"node_modules/sucrase": {
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
@@ -20443,6 +20628,19 @@
}
}
},
"node_modules/use-isomorphic-layout-effect": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
@@ -25,11 +25,13 @@
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-helmet": "^6.1.0",
"react-pdf": "^7.4.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6",
"reactour": "^1.19.1",
"select-dom": "^9.0.0",
@@ -47,7 +47,7 @@
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"
/>
<title>OpenSign App</title>
<title>OpenSign</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
@@ -47,65 +47,34 @@ function DraftDocument() {
//check document type and render on signyour self and placeholder route
const handleDraftDoc = () => {
const data = pdfDetails[0];
const checkSignerExist =
pdfDetails[0] && pdfDetails[0].Signers && pdfDetails[0].Signers;
const isPlaceholder =
pdfDetails[0].Placeholders && pdfDetails[0].Placeholders;
const isDecline = data.IsDeclined && data.IsDeclined;
const signUrl = data.SignedUrl && data.SignedUrl;
const expireDate =
pdfDetails[0] &&
pdfDetails[0].ExpiryDate.iso &&
pdfDetails[0].ExpiryDate.iso;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
const hostUrl = getHostUrl();
const signerExist = data.Signers && data.Signers;
const isDecline = data.IsDeclined && data.IsDeclined;
const isPlaceholder = data.Placeholders && data.Placeholders;
//checking document is completed and signer exist then navigate to pdfRequestFiles file
if (data.IsCompleted && checkSignerExist) {
navigate(`${hostUrl}pdfRequestFiles`);
//checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
}
//checking document is completed and signer does not exist then navigate to recipientSignPdf file
else if (data.IsCompleted && !checkSignerExist) {
navigate(`${hostUrl}signaturePdf`);
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
}
//checking document is declined by someone then navigate to pdfRequestFiles file
//checking if document has declined by someone
else if (isDecline) {
navigate(`${hostUrl}pdfRequestFiles`);
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
//checking draft type document
}
//checking document has expired and signers exist and placeholder does not set yet then navigate to pdfRequestFiles file
//draft type request sign document
//Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
} //placeholder draft document
else if (
currDate > expireUpdateDate &&
checkSignerExist &&
!isPlaceholder
(signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)) ||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
) {
navigate(`${hostUrl}placeHolderSign`);
}
//checking document has expired and signers does not exist and document not signed yet then navigate to pdfRequestFiles file
//draft type signyouselfdocument
else if (
currDate > expireUpdateDate &&
!checkSignerExist &&
!isPlaceholder &&
!signUrl
) {
// window.location.hash = `/signaturePdf`;
navigate(`${hostUrl}signaturePdf`);
}
//checking document has expired and signers exist and document then navigate to pdfRequestFiles file
else if (currDate > expireUpdateDate) {
// window.location.hash = `/pdfRequestFiles`;
navigate(`${hostUrl}pdfRequestFiles`);
}
//checking document has expired not been expired yet and signers exist and placeholder does not set yet then navigate to pdfRequestFiles file
//draft type request sign document
else if (!signUrl && checkSignerExist) {
navigate(`${hostUrl}placeHolderSign`);
// window.location.hash = `/placeHolderSign`;
} else {
// window.location.hash = `/signaturePdf/`;
navigate(`${hostUrl}signaturePdf`);
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
}
};
@@ -10,6 +10,9 @@ import { getHostUrl } from "../../../utils/Utils";
import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import SelectFolder from "../../../premitives/SelectFolder";
import ModalUi from "../../../premitives/ModalUi";
import { themeColor } from "../../../utils/ThemeColor/backColor";
function PdfFileComponent({
pdfData,
@@ -17,12 +20,20 @@ function PdfFileComponent({
setDocId,
setIsLoading,
setPdfData,
isList,
isList
}) {
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
@@ -39,12 +50,12 @@ function PdfFileComponent({
const handleOnclikFolder = (data) => {
const folderData = {
name: data.Name,
objectId: data.objectId,
objectId: data.objectId
};
setFolderName((prev) => [...prev, folderData]);
const loadObj = {
isLoad: true,
message: "This might take some time",
message: "This might take some time"
};
setIsLoading(loadObj);
@@ -57,7 +68,7 @@ function PdfFileComponent({
if (trimmedValue.length > 0) {
const updateName = {
Name: renameValue,
Name: renameValue
};
const docId = data.objectId;
@@ -83,8 +94,8 @@ function PdfFileComponent({
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
},
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then((result) => {
@@ -99,79 +110,176 @@ function PdfFileComponent({
//function for navigate user to microapp-signature component
const checkPdfStatus = async (data) => {
const hostUrl = getHostUrl();
const expireDate = data.ExpiryDate.iso;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
const signerExist = data.Signers && data.Signers;
const signUrl = data.SignedUrl && data.SignedUrl;
const isDecline = data.IsDeclined && data.IsDeclined;
const isPlaceholder = data.Placeholders && data.Placeholders;
//checking and navigate to signyouself page
const checkPlaceHolder = data.Placeholders;
//checking if document has completed
if (data.IsCompleted && signerExist) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} else if (data.IsCompleted && !signerExist) {
navigate(
`${hostUrl}signaturePdf/${data.objectId}`
);
// window.location.hash = `/recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`;
//checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
}
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
}
//checking if document has declined by someone
else if (isDecline) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} else if (currDate > expireUpdateDate && signerExist && !isPlaceholder) {
// window.location.hash = `/placeHolderSign/${data.objectId}`;
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
} else if (
currDate > expireUpdateDate &&
!signerExist &&
!isPlaceholder &&
!signUrl
) {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
// window.location.hash = `/signaturePdf/${data.objectId}`;
//checking draft type document
}
//checking if document has expired
else if (currDate > expireUpdateDate) {
//Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
} //checking if document is draft signers type and signers placeholder does not placed yet
else if (!signUrl && signerExist) {
// window.location.hash = `/placeHolderSign/${data.objectId}`;
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
}
//checking if document is request type and signers placeholder exist and does not completed yet
//then user can check progress of document and sign also
} //placeholder draft document
else if (
checkPlaceHolder &&
checkPlaceHolder.length > 0 &&
!data.IsCompleted
(signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)) ||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
) {
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
}
//checking document is draft and signyourself type then user can sign document
else {
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
// navigate(`/signaturePdf/${data.objectId}`);
// window.location.hash = `/signaturePdf/${data.objectId}`;
};
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;
}
};
//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;
const updatedData = pdfData.filter((x) => x.objectId !== docId);
setPdfData(updatedData);
})
.catch((err) => {
console.log("err", err);
});
};
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 json = Listdata.data;
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) => {
window.location.hash = `/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9MZWdhR2VuaWUtTWljcm9hcHBWMi9yZW1vdGVFbnRyeS5qcw==&moduleToLoad=AppRoutes&remoteName=legageniemicroapp/legagenie?${data.objectId}`;
};
//component to handle type of document and render according to type
const handleFolderData = (data, ind, listType) => {
// console.log("data", data);
let createddate,
status,
isDecline,
signerExist,
isExpire,
isComplete,
signUrl,
isPlaceholder;
@@ -186,64 +294,48 @@ function PdfFileComponent({
signUrl = data.SignedUrl && data.SignedUrl;
const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime();
let isExpire = false;
if (currDate > expireUpdateDate) {
isExpire = true;
} else {
isExpire = false;
}
if (isComplete) {
status = "Completed";
} else if (isDecline) {
status = "Declined";
} else if (isExpire && !isPlaceholder && signerExist) {
// status = "Expired";
} else if (!signerExist || signerExist?.length === 0) {
status = "Draft";
} else if (isExpire && !isPlaceholder && !signerExist && !signUrl) {
// status = "Expired";
} else if (
signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)
) {
status = "Draft";
} else if (isExpire) {
status = "Expired";
} else if (!signUrl) {
status = "Draft";
} else {
status = "InComplete";
status = "In Progress";
}
}
const handleDraftDoc = (data) => {
window.location.hash = `/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9MZWdhR2VuaWUtTWljcm9hcHBWMi9yZW1vdGVFbnRyeS5qcw==&moduleToLoad=AppRoutes&remoteName=legageniemicroapp/legagenie?${data.objectId}`;
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>
);
};
const handleMenuItemClick = (selectType, data) => {
// console.log("data",data)
if (selectType === "Download") {
// console.log("download")
const pdfName = data && data.Name;
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
} else if (selectType === "Rename") {
// console.log("rename")
setRenameValue(data.Name);
setRename(data.objectId);
}
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, '_');
}
const handleEnterPress = (e, data) => {
if (e.key === "Enter") {
handledRenameDoc(data);
}
};
return listType === "table" ? (
data.Type === "Folder" ? (
<tr onClick={() => handleOnclikFolder(data)}>
<td>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-folder"
aria-hidden="true"
@@ -261,7 +353,9 @@ function PdfFileComponent({
</tr>
) : data.Type === "AIDoc" ? (
<tr onClick={() => handleDraftDoc(data)}>
<td>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-file-text"
style={{ color: "#0ea3ed", marginRight: "8px", fontSize: "26px" }}
@@ -278,7 +372,9 @@ function PdfFileComponent({
</tr>
) : (
<tr onClick={() => checkPdfStatus(data)}>
<td>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-file-pdf"
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
@@ -312,7 +408,7 @@ function PdfFileComponent({
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer",
cursor: "pointer"
}}
>
<img
@@ -333,7 +429,6 @@ function PdfFileComponent({
}}
autoFocus={true}
type="text"
// onFocus={()=>console.log("focus")}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
@@ -344,7 +439,7 @@ function PdfFileComponent({
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
fontSize: "10px"
}}
/>
) : (
@@ -359,19 +454,12 @@ function PdfFileComponent({
sideOffset={5}
align="end"
>
{/* <ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item> */}
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
<i class="fa-solid fa-font"></i>
<span style={{ marginLeft: "8px" }}>Rename</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
@@ -385,7 +473,7 @@ function PdfFileComponent({
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer",
cursor: "pointer"
}}
>
<img
@@ -406,7 +494,6 @@ function PdfFileComponent({
}}
autoFocus={true}
type="text"
// onFocus={()=>console.log("focus")}
onBlur={() => handledRenameDoc(data)}
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
@@ -417,7 +504,7 @@ function PdfFileComponent({
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
fontSize: "10px"
}}
/>
) : (
@@ -432,26 +519,19 @@ function PdfFileComponent({
sideOffset={5}
align="end"
>
{/* <ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item> */}
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
<i class="fa-solid fa-font"></i>
<span style={{ marginLeft: "8px" }}>Rename</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
</div>
) : (
<HoverCard.Root>
<HoverCard.Root openDelay={0} closeDelay={100}>
<HoverCard.Trigger asChild>
<div>
<ContextMenu.Root>
@@ -484,7 +564,7 @@ function PdfFileComponent({
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px",
fontSize: "10px"
}}
/>
) : (
@@ -508,7 +588,7 @@ function PdfFileComponent({
<i className="fa fa-file"></i>
</div>
) : (
status === "InComplete" && (
status === "In Progress" && (
<div className="status-badge in-progress">
<i className="fa fa-paper-plane"></i>
</div>
@@ -522,20 +602,18 @@ function PdfFileComponent({
sideOffset={5}
align="end"
>
<ContextMenu.Item
onClick={() => handleMenuItemClick("Download", data)}
onSelect={(e) => console.log("event", e)}
className="ContextMenuItem"
>
Download
</ContextMenu.Item>
<ContextMenu.Item
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
Rename
</ContextMenu.Item>
{contextMenu.map((menu, ind) => {
return (
<ContextMenu.Item
key={ind}
onClick={() => handleMenuItemClick(menu.type, data)}
className="ContextMenuItem"
>
<i class={menu.icon}></i>
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
</ContextMenu.Item>
);
})}
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
@@ -558,56 +636,113 @@ function PdfFileComponent({
{signerExist && (
<>
<strong style={{ fontSize: "13px" }}>Signers: </strong>
{signerExist.map((data, key) => {
return (
<React.Fragment key={key}>
<span className="statusSpan">{data.Name}, </span>
</React.Fragment>
);
})}
{/* <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>
//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 (
<React.Fragment key={ind}>
{handleFolderData(data, ind, "table")}
</React.Fragment>
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
</div>
);
})}
</tbody>
</Table>
</div>
) : (
<div className="pdfContainer">
{pdfData.map((data, ind) => {
return (
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
</div>
);
})}
</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);
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
No
</button>
</div>
</ModalUi>
</>
);
}
@@ -6,12 +6,13 @@
.ContextMenuContent,
.ContextMenuSubContent {
min-width: 150px;
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,
@@ -27,7 +28,7 @@
height: 25px;
padding: 0 5px;
position: relative;
padding-left: 25px;
padding-left: 15px;
user-select: none;
outline: none;
}
@@ -84,14 +85,10 @@
color: var(--mauve-8);
}
.dropdown-menu {
min-width: 10rem;
}
.dropdown-item {
display: block;
width: 100%;
@@ -103,21 +100,18 @@
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 {
margin: 30px;
height: 100%;
@@ -402,6 +396,7 @@ a {
will-change: transform, opacity;
text-align: center;
align-items: center;
}
.HoverCardContent[data-side='top'] {
@@ -490,41 +485,74 @@ a {
display: none;
}
.folderPath{
.folderPath {
overflow-x: auto;
white-space: nowrap;
cursor: pointer;
user-select: none;
}
.folderPath::-webkit-scrollbar {
}
.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;
padding: 2px;
}
.folderComponent{
.folderComponent {
margin: 10px;
}
}
@media screen and (max-width:309px) {
@media screen and (max-width:309px) {
.pdfContainer {
justify-content: center;
}
.sort {
padding: 2px;
}
.folderComponent{
margin: 10px;
}
}
.folderComponent {
margin: 10px;
}
}
@@ -7,8 +7,14 @@ import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import { themeColor, iconColor } from "../../utils/ThemeColor/backColor";
import { getDrive } from "../../utils/Utils";
import AlertComponent from "../component/alertComponent";
import { useNavigate } from "react-router-dom";
import Title from "../component/Title";
import Parse from "parse";
import ModalUi from "../../premitives/ModalUi";
function PdfFile() {
const navigate = useNavigate();
const scrollRef = useRef(null);
const [isList, setIsList] = useState(false);
const [selectedSort, setSelectedSort] = useState("Date");
@@ -24,9 +30,18 @@ function PdfFile() {
message: "This might take some time"
});
const [docId, setDocId] = useState();
const [handleError, setHandleError] = 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) {
@@ -42,16 +57,18 @@ function PdfFile() {
isLoad: true,
message: "This might take some time"
};
setIsLoading(load);
const driveDetails = await getDrive();
setIsLoading(load);
const driveDetails = await getDrive();
if (driveDetails) {
if (driveDetails.length > 0) {
setPdfData(driveDetails);
sortApps(null, null, driveDetails);
}
const data = [
{
name: "OpenSignDrive",
name: "OpenSignDrive",
objectId: ""
}
];
@@ -79,6 +96,7 @@ function PdfFile() {
if (driveDetails) {
if (driveDetails.length > 0) {
setPdfData(driveDetails);
sortApps(null, null, driveDetails);
} else {
setPdfData([]);
}
@@ -100,43 +118,16 @@ function PdfFile() {
setIsFolder(true);
};
//function for handle folder name path
const handleRoute = (data) => {
let loadObj = {
isLoad: true,
message: "This might take some time"
};
if (data.name === "LegaDrive™") {
setIsLoading(loadObj);
if (docId) {
setDocId();
} else {
setTimeout(() => {
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
}, 1000);
const handleRoute = (index) => {
const updateFolderName = folderName.filter((x, i) => {
if (i <= index) {
return x;
}
} else if (data.name === folderName[folderName.length - 1].name) {
setIsLoading(loadObj);
setTimeout(() => {
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
}, 1000);
} else {
const findIndex = folderName.findIndex(
(fold) => fold.objectId === data.objectId
);
const newFolder = folderName.slice(0, findIndex + 1);
});
setFolderName(newFolder);
const getLastId = newFolder[newFolder.length - 1];
setDocId(getLastId.objectId);
setIsLoading(loadObj);
}
setFolderName(updateFolderName);
const getLastId = updateFolderName[updateFolderName.length - 1];
setDocId(getLastId.objectId);
};
//function for add new folder name
@@ -145,67 +136,60 @@ function PdfFile() {
const value = e.target.value;
setNewFolderName(value);
};
//function for add folder
//function for create folder
const handleAddFolder = async () => {
if (newFolderName) {
setIsFolderLoader(true);
const getParentObjId = folderName[folderName.length - 1];
const isParentId = getParentObjId && getParentObjId.objectId;
let data;
if (isParentId) {
data = {
Name: newFolderName,
Type: "Folder",
Folder: {
__type: "Pointer",
className: `${localStorage.getItem("_appName")}_Document`,
objectId: isParentId
}
};
} else {
data = {
Name: newFolderName,
Type: "Folder"
};
}
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
};
// console.log("data", data);
await axios
.post(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
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");
.then((Listdata) => {
// console.log("Listdata ", Listdata);
const json = Listdata.data;
// console.log("json ", json);
if (json) {
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);
if (docId) {
getPdfFolderDocumentList();
} else {
getPdfDocumentList();
}
}
})
.catch((err) => {
console.log("axois err ", err);
alert("something went wrong");
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
} else {
setError("Please fill out this field");
}
@@ -227,11 +211,11 @@ function PdfFile() {
}
};
const sortApps = () => {
const selectedSortType = selectedSort;
const sortOrder = sortingOrder;
const sortApps = (type, order, driveDetails) => {
const selectedSortType = type ? type : selectedSort ? selectedSort : "Date";
const sortOrder = order ? order : sortingOrder ? sortingOrder : "Decending";
let sortingData = pdfData;
let sortingData = driveDetails;
if (selectedSortType === "Name") {
sortingApp(sortingData, "Name", sortOrder);
} else if (selectedSortType === "Date") {
@@ -293,7 +277,9 @@ function PdfFile() {
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (isShowSort && !e.target.closest("#menu-container")) {
setIsShowSort(false);
setIsShowSort(!isShowSort);
} else if (isNewFol && !e.target.closest("#folder-menu")) {
setIsNewFol(!isNewFol);
}
};
@@ -303,28 +289,56 @@ function PdfFile() {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isShowSort]);
}, [isShowSort || isNewFol]);
const handleFolderTab = (folderData, isMove) => {
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"
}}
>
&gt;
</span>
</span>
</React.Fragment>
);
});
};
const oncloseFolder = () => {
setIsFolder(false);
setNewFolderName("");
setError("");
};
return (
<div className="folderComponent ">
<Title title={"OpenSign™ Drive"} drive={true} />
<div>
<Modal show={isFolder}>
<ModalHeader style={{ background: themeColor() }}>
<span style={{ color: "white" }}>Add New Folder</span>
{!folderLoader && (
<span
style={{ cursor: "pointer" }}
onClick={() => {
setNewFolderName("");
setIsFolder(false);
}}
>
X
</span>
)}
</ModalHeader>
<Modal.Body>
<AlertComponent
isShow={isAlert.isShow}
alertMessage={isAlert.alertMessage}
setIsAlert={setIsAlert}
/>
<ModalUi
isOpen={isFolder}
title={"Add New Folder"}
handleClose={oncloseFolder}
>
<div style={{ height: "100%", padding: 20 }}>
{folderLoader ? (
<div
style={{
@@ -345,7 +359,13 @@ function PdfFile() {
</span>
</div>
) : (
<form style={{ display: "flex", flexDirection: "column" }}>
<form
onSubmit={(e) => {
e.preventDefault();
handleAddFolder();
}}
style={{ display: "flex", flexDirection: "column" }}
>
<label
style={{
margin: "10px 0px 10px 0px",
@@ -364,46 +384,45 @@ function PdfFile() {
onChange={(e) => handleFolderName(e)}
// className="addFolderInput"
/>
<span style={{ color: "red", fontSize: "12px" }}>{error}</span>
<span
style={{ color: "red", fontSize: "12px", marginTop: "6px" }}
>
{error}
</span>
<div
style={{
margin: "20px 10px 10px 10px ",
display: "flex",
alignItems: "flex-end",
justifyContent: "flex-end",
alignContent: "flex-end"
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
>
></div>
<div style={{ display: "flex", flexDirection: "row" }}>
<button
type="submit"
style={{
borderRadius: "0px",
border: "1.5px solid #e3e2e1",
fontWeight: "600",
color: "black"
}}
className="finishBtn"
onClick={() => setIsFolder(false)}
>
Close
</button>
<button
onClick={() => handleAddFolder()}
style={{
background: themeColor()
}}
type="button"
type="submit"
className="finishBtn"
>
Add
</button>
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={oncloseFolder}
>
Close
</button>
</div>
</form>
)}
</Modal.Body>
</Modal>
</div>
</ModalUi>
{isLoading.isLoad ? (
<div
@@ -450,33 +469,72 @@ function PdfFile() {
}}
className="folderPath"
>
{folderName.map((data, id) => {
return (
<span
key={id}
onClick={() => handleRoute(data)}
style={{
color: "#a64b4e",
fontWeight: "400",
cursor: "pointer"
}}
>
{data.name}
<span
style={{
color: "#a64b4e",
fontWeight: "200",
cursor: "pointer",
margin: "0 4px"
}}
>
&gt;
</span>
</span>
);
})}
{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"}
@@ -511,7 +569,7 @@ function PdfFile() {
<span
onClick={() => {
setSelectedSort("Name");
sortApps();
sortApps("Name", null, pdfData);
}}
className="dropdown-item itemColor"
>
@@ -533,7 +591,7 @@ function PdfFile() {
<span
onClick={() => {
setSelectedSort("Date");
sortApps();
sortApps("Date", null, pdfData);
}}
className="dropdown-item itemColor"
>
@@ -556,7 +614,7 @@ function PdfFile() {
<span
onClick={() => {
setSortingOrder("Accending");
sortApps();
sortApps(null, "Accending", pdfData);
}}
className="dropdown-item itemColor"
>
@@ -578,7 +636,7 @@ function PdfFile() {
<span
onClick={() => {
setSortingOrder("Decending");
sortApps();
sortApps(null, "Decending", pdfData);
}}
className="dropdown-item itemColor"
>
@@ -620,14 +678,6 @@ function PdfFile() {
</div>
)}
</div>
<div className="sort" onClick={() => getParentFolder()}>
<i
className="fa fa-plus-square"
aria-hidden="true"
style={{ fontSize: "25px", color: `${iconColor()}` }}
></i>
</div>
</div>
</div>
@@ -157,7 +157,7 @@ const ManageSign = () => {
file = base64StringtoFile(image, `${replaceSpace}.png`);
}
}
console.log("isUrl ", isUrl);
// console.log("isUrl ", isUrl);
let imgUrl;
if (!isUrl) {
imgUrl = await uploadFile(file);
File diff suppressed because it is too large Load Diff
@@ -21,7 +21,11 @@ import {
getBase64FromIMG,
embedDocId,
multiSignEmbed,
pdfNewWidthFun
pdfNewWidthFun,
addDefaultSignatureImg,
onImageSelect,
placeholderHeight,
placeholderWidth
} from "../utils/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -33,6 +37,10 @@ import Header from "./component/header";
import RenderPdf from "./component/renderPdf";
import { contractUsers, contactBook, urlValidator } from "../utils/Utils";
import { modalAlign } from "../utils/Utils";
import AlertComponent from "./component/alertComponent";
import PlaceholderCopy from "./component/PlaceholderCopy";
import TourContentWithBtn from "../premitives/TourContentWithBtn";
import Title from "./component/Title";
//For signYourself inProgress section signer can add sign and complete doc sign.
function SignYourSelf() {
@@ -75,6 +83,8 @@ function SignYourSelf() {
const [tourStatus, setTourStatus] = useState([]);
const [noData, setNoData] = useState(false);
const [contractName, setContractName] = useState("");
const [containerWH, setContainerWH] = useState({});
const [isPageCopy, setIsPageCopy] = useState(false);
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
status: false
});
@@ -82,6 +92,8 @@ function SignYourSelf() {
status: false,
type: "load"
});
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isDontShow, setIsDontShow] = useState(false);
const divRef = useRef(null);
const nodeRef = useRef(null);
const [{ isOver }, drop] = useDrop({
@@ -91,12 +103,12 @@ function SignYourSelf() {
isOver: !!monitor.isOver()
})
});
const isMobile = window.innerWidth < 767;
const pdfRef = useRef();
const [{ isDragSign }, dragSignature] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 1,
@@ -109,7 +121,6 @@ function SignYourSelf() {
const [{ isDragStamp }, dragStamp] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 2,
@@ -134,7 +145,6 @@ function SignYourSelf() {
const [{ isDragStampSS }, dragStampSS] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 4,
@@ -178,6 +188,10 @@ function SignYourSelf() {
if (divRef.current) {
const pdfWidth = pdfNewWidthFun(divRef);
setPdfNewWidth(pdfWidth);
setContainerWH({
width: divRef.current.offsetWidth,
height: divRef.current.offsetHeight
});
}
}, [divRef.current]);
@@ -319,7 +333,6 @@ function SignYourSelf() {
isDrag: false,
key: key,
isStamp: monitor,
yBottom: window.innerHeight / 2 - 60
};
@@ -395,14 +408,22 @@ function SignYourSelf() {
for (let i = 0; i < xyPostion.length; i++) {
const posData = xyPostion[i].pos.filter((pos) => !pos.SignUrl);
if (posData && posData.length > 0) {
checkSignUrl.push(posData);
}
}
if (checkSignUrl && checkSignUrl.length > 0) {
alert("Please complete your signature!");
if (xyPostion.length === 0) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
return;
} else if (xyPostion.length > 0 && checkSignUrl.length > 0) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
return;
} else {
setIsCeleb(true);
setTimeout(() => {
@@ -423,57 +444,61 @@ function SignYourSelf() {
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true
});
const pngUrl = xyPostion;
//checking if signature is only one then send image url in jpeg formate to server
if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
//embed document's object id to all pages in pdf document
await embedDocId(pdfDoc, documentId, allPages);
const pdfBase64 = await pdfDoc.saveAsBase64({
useObjectStreams: false
});
for (let i = 0; i < xyPostion.length; i++) {
const imgUrlList = xyPostion[i].pos;
const pageNo = xyPostion[i].pageNumber;
imgUrlList.map(async (data) => {
let ImgUrl = data.SignUrl;
//cheking signUrl is defau;t signature url of custom url
const checkUrl = urlValidator(ImgUrl);
// //checking if signature is only one then send image url in jpeg formate to server
// if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
// //embed document's object id to all pages in pdf document
// await embedDocId(pdfDoc, documentId, allPages);
// const pdfBase64 = await pdfDoc.saveAsBase64({
// useObjectStreams: false
// });
//if default signature url then convert it in base 64
if (checkUrl) {
ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
}
//function for convert signature png base64 url to jpeg base64
convertPNGtoJPEG(ImgUrl)
.then((jpegBase64Data) => {
const removeBase64Fromjpeg = "data:image/jpeg;base64,";
const newImgUrl = jpegBase64Data.replace(
removeBase64Fromjpeg,
""
);
//function for call to embed signature in pdf and get digital signature pdf
signPdfFun(newImgUrl, documentId, data, pdfBase64, pageNo);
})
.catch((error) => {
console.error("Error:", error);
});
});
}
}
// for (let xyData of xyPostion) {
// const imgUrlList = xyData.pos;
// const pageNo = xyData.pageNumber;
// imgUrlList.map(async (data) => {
// let ImgUrl = data.SignUrl;
// //cheking signUrl is defau;t signature url of custom url
// const checkUrl = urlValidator(ImgUrl);
// //if default signature url then convert it in base 64
// if (checkUrl) {
// ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
// }
// //function for convert signature png base64 url to jpeg base64
// convertPNGtoJPEG(ImgUrl)
// .then((jpegBase64Data) => {
// const removeBase64Fromjpeg = "data:image/jpeg;base64,";
// const newImgUrl = jpegBase64Data.replace(
// removeBase64Fromjpeg,
// ""
// );
// //function for call to embed signature in pdf and get digital signature pdf
// signPdfFun(newImgUrl, documentId, data, pdfBase64, pageNo);
// })
// .catch((error) => {
// console.error("Error:", error);
// });
// });
// }
// }
//else if signature is more than one then embed all sign with the use of pdf-lib
else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
//embed document's object id to all pages in pdf document
await embedDocId(pdfDoc, documentId, allPages);
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
pdfDoc,
pdfOriginalWidth,
true
);
signPdfFun(pdfBytes, documentId);
}
// else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
const flag = true;
//embed document's object id to all pages in pdf document
await embedDocId(pdfDoc, documentId, allPages);
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
xyPostion,
pdfDoc,
pdfOriginalWidth,
flag,
containerWH
);
//function for call to embed signature in pdf and get digital signature pdf
signPdfFun(pdfBytes, documentId);
// }
setIsSignPad(false);
setIsEmail(true);
setXyPostion([]);
@@ -490,39 +515,44 @@ function SignYourSelf() {
pageNo
) => {
let singleSign;
const isMobile = window.innerWidth < 767;
const newWidth = window.innerWidth - 32;
const newWidth = containerWH.width;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
const imgWidth = xyPosData ? xyPosData.Width : 150;
if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
const height = xyPosData.Height ? xyPosData.Height : 60;
const bottomY = xyPosData.isDrag
? xyPosData.yBottom * scale - height * scale
: xyPosData.firstYPos
? xyPosData.yBottom * scale - height * scale + xyPosData.firstYPos
: xyPosData.yBottom * scale - height * scale;
singleSign = {
pdfFile: pdfBase64Url,
docId: documentId,
sign: {
Base64: base64Url,
Left: isMobile
? xyPosData.xPosition * scale + 43
: xyPosData.xPosition,
Bottom: bottomY,
Width: xyPosData.Width ? xyPosData.Width * scale : 150 * scale,
Height: height * scale,
Page: pageNo
}
};
} else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
singleSign = {
pdfFile: base64Url,
docId: documentId
};
}
// if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
// const xPos = () => {
// const resizePos = xyPosData.xPosition;
// if (isMobile) {
// return resizePos * scale;
// } else {
// return resizePos;
// }
// };
// const height = xyPosData.Height ? xyPosData.Height : 60;
// const resizePos = xyPosData.yBottom;
// const bottomY = xyPosData.isDrag
// ? resizePos * scale - height * scale
// : xyPosData.firstYPos
// ? resizePos * scale - height * scale + xyPosData.firstYPos
// : resizePos * scale - height * scale;
// singleSign = {
// pdfFile: pdfBase64Url,
// docId: documentId,
// sign: {
// Base64: base64Url,
// Left: xPos(),
// Bottom: bottomY,
// Width: placeholderWidth(xyPosData, scale),
// Height: placeholderHeight(xyPosData, scale),
// Page: pageNo
// }
// };
// } else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
singleSign = {
pdfFile: base64Url,
docId: documentId
// };
};
await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
@@ -534,14 +564,13 @@ function SignYourSelf() {
})
.then((Listdata) => {
const json = Listdata.data;
// console.log("json ", json);
setPdfUrl(json.result.data);
if (json.result.data) {
getDocumentDetails(false);
}
})
.catch((err) => {
// this.setState({ loading: false });
console.log("axois err ", err);
});
};
@@ -620,54 +649,43 @@ function SignYourSelf() {
//function for image upload or update
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
const imageType = event.target.files[0].type;
const reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onloadend = function (e) {
let width, height;
const image = new Image();
image.src = e.target.result;
image.onload = function () {
width = image.width;
height = image.height;
const aspectRatio = 460 / 184;
const imgR = width / height;
if (imgR > aspectRatio) {
width = 460;
height = 460 / imgR;
} else {
width = 184 * imgR;
height = 184;
}
setImgWH({ width: width, height: height });
imageRef.current.style.width = `${width}px`;
imageRef.current.style.height = `${height}px`;
};
image.src = reader.result;
setImage({ src: image.src, imgType: imageType });
};
onImageSelect(event, setImgWH, setImage);
}
};
//function for upload stamp or image
const saveImage = () => {
const getImage = onSaveImage(xyPostion, index, signKey, imgWH, image);
setXyPostion(getImage);
};
//function for save button to save signature or image url
const saveSign = (isDefaultSign) => {
const saveSign = (isDefaultSign, width, height) => {
const isTypeText = width && height ? true : false;
const signatureImg = isDefaultSign ? defaultSignImg : signature;
let imgWH = { width: width ? width : "", height: height ? height : "" };
setIsSignPad(false);
setIsImageSelect(false);
setImage();
if (isDefaultSign) {
const img = new Image();
img.src = defaultSignImg;
if (img.complete) {
imgWH = {
width: img.width,
height: img.height
};
}
}
const getUpdatePosition = onSaveSign(
xyPostion,
index,
signKey,
signatureImg
signatureImg,
imgWH,
isDefaultSign,
isTypeText
);
setXyPostion(getUpdatePosition);
@@ -692,60 +710,6 @@ function SignYourSelf() {
setSignBtnPosition([xySignature]);
};
//function for resize image and update width and height
const handleImageResize = (ref, key, direction, position) => {
const updateFilter = xyPostion[index].pos.filter(
(data, ind) => data.key === key && data.Width && data.Height
);
if (updateFilter.length > 0) {
const getXYdata = xyPostion[index].pos;
const getPosData = getXYdata;
const addSign = getPosData.map((url, ind) => {
if (url.key === key) {
return {
...url,
Width: ref.offsetWidth,
Height: ref.offsetHeight,
xPosition: position.x
};
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
setXyPostion(newUpdateUrl);
} else {
const getXYdata = xyPostion[index].pos;
const getPosData = getXYdata;
const addSign = getPosData.map((url, ind) => {
if (url.key === key) {
return {
...url,
Width: ref.offsetWidth,
Height: ref.offsetHeight
};
}
return url;
});
const newUpdateUrl = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: addSign };
}
return obj;
});
setXyPostion(newUpdateUrl);
}
};
const handleAllDelete = () => {
setXyPostion([]);
};
@@ -780,47 +744,38 @@ function SignYourSelf() {
}
};
//function for upload stamp or image
const saveImage = () => {
const getImage = onSaveImage(xyPostion, index, signKey, imgWH, image);
setXyPostion(getImage);
};
//function for add default signature url in local array
const addDefaultSignature = () => {
let xyDefaultPos = [];
for (let i = 0; i < xyPostion.length; i++) {
const getXYdata = xyPostion[i].pos;
const getPageNo = xyPostion[i].pageNumber;
const getPosData = getXYdata;
const getXyData = addDefaultSignatureImg(xyPostion, defaultSignImg);
const addSign = getPosData.map((url, ind) => {
if (url) {
return { ...url, SignUrl: defaultSignImg };
}
return url;
});
const newXypos = {
pageNumber: getPageNo,
pos: addSign
};
xyDefaultPos.push(newXypos);
}
setXyPostion(xyDefaultPos);
setXyPostion(getXyData);
setShowAlreadySignDoc({ status: false });
};
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const tourConfig = [
{
selector: '[data-tut="reactourFirst"]',
content: `Drag the signature or stamp placeholder onto the PDF to choose your desired signing location.`,
content: () => (
<TourContentWithBtn
message={`Drag the signature or stamp placeholder onto the PDF to choose your desired signing location.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourSecond"]',
content: `Drag and drop anywhere in this area. You can resize and move it later.`,
content: () => (
<TourContentWithBtn
message={`Drag and drop anywhere in this area. You can resize and move it later.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
@@ -829,47 +784,49 @@ function SignYourSelf() {
//function for update TourStatus
const closeTour = async () => {
setSignTour(false);
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const signyourselfIndex = tourStatus.findIndex(
(obj) => obj["signyourself"] === false || obj["signyourself"] === true
);
if (signyourselfIndex !== -1) {
updatedTourStatus[signyourselfIndex] = { signyourself: true };
} else {
updatedTourStatus.push({ signyourself: true });
}
} else {
updatedTourStatus = [{ signyourself: true }];
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
if (isDontShow) {
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const signyourselfIndex = tourStatus.findIndex(
(obj) => obj["signyourself"] === false || obj["signyourself"] === true
);
if (signyourselfIndex !== -1) {
updatedTourStatus[signyourselfIndex] = { signyourself: true };
} else {
updatedTourStatus.push({ signyourself: true });
}
)
.then((Listdata) => {
// const json = Listdata.data;
})
.catch((err) => {
console.log("axois err ", err);
});
} else {
updatedTourStatus = [{ signyourself: true }];
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
)
.then((Listdata) => {
// const json = Listdata.data;
})
.catch((err) => {
console.log("axois err ", err);
});
}
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Self Sign"} />
{isLoading.isLoad ? (
<Loader isLoading={isLoading} />
) : handleError ? (
@@ -898,17 +855,22 @@ function SignYourSelf() {
setAllPages={setAllPages}
setPageNumber={setPageNumber}
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
/>
{/* pdf render view */}
<div
style={{
marginLeft: pdfOriginalWidth > 500 && "20px",
marginRight: pdfOriginalWidth > 500 && "20px"
marginLeft: !isMobile && pdfOriginalWidth > 500 && "20px",
marginRight: !isMobile && pdfOriginalWidth > 500 && "20px"
}}
>
<AlertComponent
isShow={isAlert.isShow}
alertMessage={isAlert.alertMessage}
setIsAlert={setIsAlert}
/>
{/* this modal is used show this document is already sign */}
<Modal
show={showAlreadySignDoc.status}
onShow={() => modalAlign()}
@@ -949,6 +911,15 @@ function SignYourSelf() {
)}
</Modal.Footer>
</Modal>
<PlaceholderCopy
isPageCopy={isPageCopy}
setIsPageCopy={setIsPageCopy}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
/>
{/* this is modal of signature pad */}
<SignPad
isSignPad={isSignPad}
@@ -972,9 +943,9 @@ function SignYourSelf() {
isCeleb={isCeleb}
setIsEmail={setIsEmail}
pdfName={pdfDetails[0] && pdfDetails[0].Name}
signObjId={documentId}
setSuccessEmail={setSuccessEmail}
sender={jsonSender}
setIsAlert={setIsAlert}
/>
{/* pdf header which contain funish back button */}
<Header
@@ -989,35 +960,40 @@ function SignYourSelf() {
currentSigner={true}
alreadySign={pdfUrl ? true : false}
isSignYourself={true}
setIsEmail={setIsEmail}
/>
{/* className="hidePdf" */}
<div data-tut="reactourSecond">
<RenderPdf
pageNumber={pageNumber}
pdfOriginalWidth={pdfOriginalWidth}
pdfNewWidth={pdfNewWidth}
drop={drop}
successEmail={successEmail}
nodeRef={nodeRef}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleImageResize={handleImageResize}
isDragging={isDragging}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
handleDeleteSign={handleDeleteSign}
setSignKey={setSignKey}
pdfDetails={pdfDetails}
setIsDragging={setIsDragging}
xyPostion={xyPostion}
pdfRef={pdfRef}
pdfUrl={pdfUrl}
numPages={numPages}
pageDetails={pageDetails}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
/>
{containerWH && (
<RenderPdf
pageNumber={pageNumber}
pdfOriginalWidth={pdfOriginalWidth}
pdfNewWidth={pdfNewWidth}
drop={drop}
successEmail={successEmail}
nodeRef={nodeRef}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
isDragging={isDragging}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
handleDeleteSign={handleDeleteSign}
setSignKey={setSignKey}
pdfDetails={pdfDetails}
setIsDragging={setIsDragging}
xyPostion={xyPostion}
pdfRef={pdfRef}
pdfUrl={pdfUrl}
numPages={numPages}
pageDetails={pageDetails}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
setXyPostion={setXyPostion}
index={index}
containerWH={containerWH}
setIsPageCopy={setIsPageCopy}
/>
)}
</div>
</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
import React from "react";
import ModalUi from "../../premitives/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}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Close
</button>
</div>
</form>
</div>
</ModalUi>
);
};
export default AddRoleModal;
@@ -25,31 +25,43 @@ function CustomModal({
<div className="modalHeadDiv bg-danger">{headMsg && headMsg}</div>
<div className="modalBodyDIv">
<p className="pTagBody">{bodyMssg && bodyMssg}</p>
{footerMessage && (
<>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="modalFooterDiv">
<button
style={{
background: "#de4337"
}}
type="button"
className="finishBtn"
onClick={() => declineDoc()}
>
Yes
</button>
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => setIsDecline({ isDeclined: false })}
>
Close
</button>
</div>
</>
)}
</div>
{footerMessage && (
<div className="modalFooterDiv">
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => setIsDecline({ isDeclined: false })}
>
Close
</button>
<button
style={{
background: "#de4337"
}}
type="button"
className="finishBtn"
onClick={() => declineDoc()}
>
Yes
</button>
</div>
)}
</div>
</div>
)
@@ -0,0 +1,101 @@
import React, { useState } from "react";
import "../../css/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 || ""
});
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 data = {...formData }
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("/")[3]?.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>
{/* <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,35 @@
import React from "react";
import SelectSigners from "../../premitives/SelectSigners";
import AddUser from "../../premitives/AddUser";
import ModalUi from "../../premitives/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;
@@ -0,0 +1,259 @@
import React, { useState } from "react";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import { themeColor } from "../../utils/ThemeColor/backColor";
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,
rest,
newPageNumber,
existPlaceholderPosition
) => {
let obj;
const filterPosition =
existPlaceholderPosition &&
existPlaceholderPosition.filter(
(data) => data.xPosition !== rest.xPosition
);
//copy all page placeholder at requested position except first page
if (newPageNumber === 1 && type === "first") {
if (filterPosition && filterPosition.length > 0) {
return (obj = {
pageNumber: newPageNumber,
pos: [...filterPosition]
});
}
}
//copy all page placeholder at requested position except last page
else if (newPageNumber === props.allPages && type === "last") {
if (existPlaceholderPosition) {
return (obj = {
pageNumber: newPageNumber,
pos: [...filterPosition]
});
}
}
//copy all page placeholder at requested position with existing placeholder position
else if (existPlaceholderPosition) {
return (obj = {
pageNumber: newPageNumber,
pos: [...filterPosition, rest]
});
}
//copy all page placeholder at requested position
else {
return (obj = {
pageNumber: newPageNumber,
pos: [rest]
});
}
};
//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.filter(
(position) => position.key === props.signKey
);
const { key, ...rest } = currentPlaceholder[0];
for (let i = 0; i < props.allPages; i++) {
const newId = randomKey();
rest.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,
rest,
newPageNumber,
existPlaceholderPosition
);
if (getPlaceholderObj) {
newPlaceholderPosition.push(getPlaceholderObj);
}
newPageNumber++;
}
let updatedSignerPlaceholder;
if (props?.signerObjId) {
updatedSignerPlaceholder = signerPosition.map((signersData, ind) => {
if (signersData.signerObjId === props.signerObjId) {
return {
...signersData,
placeHolder: newPlaceholderPosition
};
}
return signersData;
});
} else {
updatedSignerPlaceholder = signerPosition.map((signersData, ind) => {
if (signersData.Id === props.Id) {
return {
...signersData,
placeHolder: newPlaceholderPosition
};
}
return signersData;
});
}
// const updatedSignerPlaceholder = signerPosition.map(
// (signersData, ind) => {
// if (signersData.signerObjId === props.signerObjId) {
// 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.filter(
(pos) => pos.key === props.signKey
);
const { key, ...rest } = currentPlaceholder[0];
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();
rest.key = newId;
//function for get copy to requested location of placeholder position
const getPlaceholderObj = getCopyPlaceholderPosition(
type,
rest,
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 (
<Modal show={props.isPageCopy}>
<ModalHeader style={{ background: themeColor() }}>
<span style={{ color: "white" }}>Copy to all pages</span>
</ModalHeader>
<Modal.Body>
{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>
);
})}
</Modal.Body>
<Modal.Footer>
<button
style={{
color: "black"
}}
type="button"
className="finishBtn"
onClick={() => props.setIsPageCopy(false)}
>
Cancel
</button>
<button
onClick={() => {
handleApplyCopy();
props.setIsPageCopy(false);
}}
style={{
background: themeColor()
}}
type="button"
disabled={!selectCopyType}
className="finishBtn"
>
Apply
</button>
</Modal.Footer>
</Modal>
);
}
export default PlaceholderCopy;
@@ -0,0 +1,19 @@
import React from "react";
import { Helmet } from "react-helmet";
function Title({ title, drive }) {
return (
<Helmet>
<title>{drive ? title : `${title} - OpenSign™`}</title>
<meta name="description" content={`${title} - OpenSign™`} />
<link
rel="icon"
type="image/png"
href={localStorage.getItem("fev_Icon")}
sizes="40x40"
/>
</Helmet>
);
}
export default Title;
@@ -0,0 +1,79 @@
import React from "react";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import { themeColor } from "../../utils/ThemeColor/backColor";
function AlertComponent({
isShow,
alertMessage,
setIsAlert,
isdefaultSign,
addDefaultSignature,
headBG
}) {
return (
<Modal show={isShow}>
<ModalHeader
style={{
color: "white",
background: headBG ? themeColor() : "#dc3545"
}}
>
<span>Alert</span>
</ModalHeader>
<Modal.Body>
<span>{alertMessage}</span>
</Modal.Body>
<Modal.Footer>
{isdefaultSign ? (
<>
<button
onClick={() => addDefaultSignature()}
style={{
background: themeColor()
}}
type="button"
className="finishBtn"
>
Yes
</button>
<button
onClick={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Close
</button>
</>
) : (
<button
onClick={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Ok
</button>
)}
</Modal.Footer>
</Modal>
);
}
export default AlertComponent;
@@ -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;
@@ -1,33 +1,35 @@
import React from "react";
function DefaultSignature({
themeColor,
defaultSignImg,
setShowAlreadySignDoc,
xyPostion
xyPostion,
setDefaultSignAlert
}) {
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
const alreadySign = {
status: true,
mssg: "Are you sure you want to sign at requested locations?",
sure: true
};
setShowAlreadySignDoc(alreadySign);
setDefaultSignAlert({
isShow: true,
alertMessage:
"Are you sure you want to auto sign at requested all locations?"
});
} else {
alert("please select position!");
setDefaultSignAlert({
isShow: true,
alertMessage: "please select position!"
});
}
};
return (
<div className="signerComponent">
<div>
<div
style={{
background: themeColor(),
color: "white",
padding: "5px",
fontFamily: "sans-serif"
fontFamily: "sans-serif",
marginTop: "5px"
}}
>
Signature
@@ -41,41 +43,34 @@ function DefaultSignature({
fontWeight: "600"
}}
>
{defaultSignImg ? (
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
src={defaultSignImg}
/>
</div>
<button
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
background: themeColor(),
color: "white",
marginTop: "20px",
cursor: "pointer"
width: "100%",
height: "100%",
objectFit: "contain"
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
) : (
<div style={{ margin: "10px" }}>
<span>
Click a signature placeholder to start signing the document!
</span>
src={defaultSignImg}
/>
</div>
)}
<button
style={{
background: themeColor(),
color: "white",
marginTop: "20px",
cursor: "pointer",
marginBottom: "10px"
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
</div>
</div>
);
@@ -1,7 +1,6 @@
import React, { useState } from "react";
import { saveAs } from "file-saver";
import celebration from "../../assests/newCeleb.gif";
import close from "../../assests/close.png";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import axios from "axios";
@@ -16,18 +15,18 @@ function EmailComponent({
isCeleb,
setIsEmail,
setSuccessEmail,
signObjId,
pdfName,
sender
sender,
setIsAlert
}) {
const [emailCount, setEmailCount] = useState([]);
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 < emailCount.length; i++) {
for (let i = 0; i < emailList.length; i++) {
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
@@ -39,12 +38,12 @@ function EmailComponent({
"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: emailCount[i],
recipient: emailList[i],
subject: `${sender.name} has signed the doc - ${pdfName}`,
from: sender.email,
html:
@@ -52,39 +51,57 @@ function EmailComponent({
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 " +
";'> <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 +
" Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender " +
" </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 Open Sign here.</p></div></div></body></html>"
" 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);
alert("Something went wrong!");
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
}
if (sendMail.data.result.status === "success") {
setIsEmail(false);
if (sendMail && sendMail.data.result.status === "success") {
setSuccessEmail(true);
setTimeout(() => {
setSuccessEmail(false);
}, 3000);
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}, 1500);
setIsLoading(false);
} else if (sendMail.data.result.status === "error") {
} else if (sendMail && sendMail.data.result.status === "error") {
setIsLoading(false);
alert("Something went wrong!");
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
} else {
setIsLoading(false);
alert("Something went wrong!");
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
};
//function for remove email
const removeChip = (index) => {
const updateEmailCount = emailCount.filter((data, key) => key !== index);
setEmailCount(updateEmailCount);
const updateEmailCount = emailList.filter((data, key) => key !== index);
setEmailList(updateEmailCount);
};
//function for get email value
const handleEmailValue = (e) => {
@@ -95,10 +112,10 @@ function EmailComponent({
//function for save email in array after press enter
const handleEnterPress = (e) => {
if (e.key === "Enter" && emailValue) {
setEmailCount((prev) => [...prev, emailValue]);
setEmailList((prev) => [...prev, emailValue]);
setEmailValue("");
} else if (e === "add" && emailValue) {
setEmailCount((prev) => [...prev, emailValue]);
setEmailList((prev) => [...prev, emailValue]);
setEmailValue("");
}
};
@@ -249,7 +266,7 @@ function EmailComponent({
>
Recipients added here will get a copy of the signed document.
</p>
{emailCount.length > 0 ? (
{emailList.length > 0 ? (
<>
<div className="addEmail">
<div
@@ -260,7 +277,7 @@ function EmailComponent({
flexWrap: "wrap"
}}
>
{emailCount.map((data, ind) => {
{emailList.map((data, ind) => {
return (
<div
className="emailChip"
@@ -279,21 +296,23 @@ function EmailComponent({
>
{data}
</span>
<img
alt="print img"
<span
style={{
color: "white",
fontSize: 13,
fontWeight: 600,
marginLeft: 7,
cursor: "pointer"
}}
onClick={() => removeChip(ind)}
src={close}
width={10}
height={10}
style={{ fontWeight: "600", marginLeft: "7px" }}
className="emailChipClose"
/>
>
<i className="fa-solid fa-xmark"></i>
</span>
</div>
);
})}
</div>
{emailCount.length <= 9 && (
{emailList.length <= 9 && (
<input
type="text"
value={emailValue}
@@ -363,18 +382,22 @@ function EmailComponent({
}}
type="button"
className="finishBtn"
onClick={() => setIsEmail(false)}
onClick={() => {
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}}
>
Close
</button>
<button
disabled={emailCount.length === 0 && true}
disabled={emailList.length === 0 && true}
style={{
background: themeColor(),
color: "white"
}}
type="button"
className={emailCount.length === 0 ? "defaultBtn" : "finishBtn"}
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
onClick={() => sendEmail()}
>
Send
@@ -0,0 +1,21 @@
import React from "react";
import "../../css/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;
@@ -1,6 +1,6 @@
import React from "react";
import * as Select from "@radix-ui/react-select";
import classnames from "classnames";
import React, { useState } from "react";
import ModalUi from "../../premitives/ModalUi";
import RecipientList from "../../premitives/RecipientList";
function FieldsComponent({
pdfUrl,
@@ -20,7 +20,6 @@ function FieldsComponent({
isDragSignatureSS,
isSignYourself,
addPositionOfSignature,
signersdata,
isSelectListId,
setSignerObjId,
@@ -29,29 +28,19 @@ function FieldsComponent({
isSigners,
dataTut,
dataTut2,
setIsShowEmail,
isMailSend,
selectedEmail,
setSelectedEmail,
handleAddSigner,
setUniqueId,
setRoleName,
handleDeleteUser,
signerPos,
handleRoleChange,
handleOnBlur
}) {
const [isSignersModal, setIsSignersModal] = useState(false);
const signStyle = pdfUrl ? "disableSign" : "signatureBtn";
const isMobile = window.innerWidth <767;
const SelectItem = React.forwardRef(
({ children, className, ...props }, forwardedRef) => {
return (
<Select.Item
className={classnames("SelectItem", className)}
{...props}
ref={forwardedRef}
>
<Select.ItemText>{children}</Select.ItemText>
<Select.ItemIndicator className="SelectItemIndicator"></Select.ItemIndicator>
</Select.Item>
);
}
);
const isMobile = window.innerWidth < 767;
const color = [
"#93a3db",
@@ -65,9 +54,12 @@ function FieldsComponent({
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc",
"#ffffcc"
];
const handleModal = () => {
setIsSignersModal(!isSignersModal);
};
return (
<>
{isMobile && isSignYourself ? (
@@ -88,102 +80,75 @@ function FieldsComponent({
padding: "10px 20px",
display: "flex",
alignItems: "center",
justifyContent: "center",
justifyContent: "center"
}}
onClick={() => {
// if (signersdata?.length) {
handleModal();
// }
}}
>
<span style={{ fontSize: "18px", fontWeight: "500" }}>
Signer :
</span>
<Select.Root
onValueChange={(obj) => {
const [selectedKey, selectedData] = obj.split("|");
const parseData = JSON.parse(selectedData);
setSignerObjId(parseData.objectId);
setIsSelectId(selectedKey);
setContractName(parseData.className);
setSelectedEmail(true);
<span
style={{
fontSize: "13px",
fontWeight: "700",
textAlign: "center"
}}
>
<Select.Trigger
className={selectedEmail ? "selectEmail": "SelectTrigger"}
style={{
background: isSelectListId
? color[isSelectListId % color.length]
: color[0],
}}
aria-label="Food"
>
<Select.Value
placeholder="Select signer.." />
{!selectedEmail &&
<Select.Icon className="SelectIcon">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
className="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.Icon>}
</Select.Trigger>
<Select.Portal>
<Select.Content
className="SelectContent"
style={{ zIndex: "5000" }}
>
<Select.ScrollUpButton className="SelectScrollButton">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
className="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.ScrollUpButton>
<Select.Viewport className="SelectViewport">
<Select.Group>
{signersdata.Signers.map((obj, ind) => {
return (
<SelectItem
selected
key={ind}
value={`${ind}|${JSON.stringify(obj)}`}
// value={(obj)}
>
{" "}
{obj.Email}
</SelectItem>
);
})}
{/* <SelectItem value="orange">Orange</SelectItem>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="grape"></SelectItem> */}
</Select.Group>
</Select.Viewport>
<Select.ScrollDownButton className="SelectScrollButton">
<i
style={{
marginTop: "5px",
marginLeft: "5px",
color: "#3b15d1",
fontSize: "20px",
}}
className="fa fa-angle-down"
aria-hidden="true"
></i>
</Select.ScrollDownButton>
</Select.Content>
</Select.Portal>
</Select.Root>
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
@@ -192,17 +157,7 @@ function FieldsComponent({
style={{ backgroundColor: themeColor() }}
>
<div
onClick={() => {
if (isSigners) {
if (selectedEmail) {
addPositionOfSignature("onclick", false);
} else {
setIsShowEmail(true);
}
} else {
addPositionOfSignature("onclick", false);
}
}}
onClick={() => addPositionOfSignature("onclick", false)}
ref={(element) => {
dragSignatureSS(element);
if (element) {
@@ -221,24 +176,15 @@ function FieldsComponent({
backgroundSize: "70% 70%",
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
paddingBottom: "2.2rem",
paddingBottom: "2.2rem"
}}
>
{/* <img
alt="sign img"
style={{
width: "30px",
height: "30px",
background: themeColor(),
}}
src={sign}
/> */}
<span
style={{
color: "white",
fontSize: "12px",
position: "relative",
top: "2.6rem",
top: "2.6rem"
}}
>
Signature
@@ -261,7 +207,7 @@ function FieldsComponent({
backgroundSize: "32px 33px",
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
paddingBottom: "2.2rem",
paddingBottom: "2.2rem"
}}
>
<span
@@ -269,7 +215,7 @@ function FieldsComponent({
color: "white",
fontSize: "12px",
position: "relative",
top: "2.6rem",
top: "2.6rem"
}}
>
Stamp
@@ -283,8 +229,7 @@ function FieldsComponent({
<div
style={{
background: themeColor(),
padding: "5px",
padding: "5px"
}}
>
<span className="signedStyle">Fields</span>
@@ -299,7 +244,7 @@ function FieldsComponent({
fontWeight: "400",
fontSize: "15px",
padding: "3px 20px 0px 20px",
color: "#bfbfbf",
color: "#bfbfbf"
}}
>
Signature
@@ -310,7 +255,7 @@ function FieldsComponent({
style={{
width: "30px",
height: "28px",
background: "#d3edeb",
background: "#d3edeb"
}}
src={sign}
/>
@@ -321,7 +266,7 @@ function FieldsComponent({
fontWeight: "400",
fontSize: "15px",
padding: "3px 0px 0px 35px",
color: "#bfbfbf",
color: "#bfbfbf"
}}
>
Stamp
@@ -332,7 +277,7 @@ function FieldsComponent({
style={{
width: "25px",
height: "28px",
background: "#d3edeb",
background: "#d3edeb"
}}
src={stamp}
/>
@@ -355,7 +300,7 @@ function FieldsComponent({
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)",
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)"
}}
>
<span
@@ -363,7 +308,7 @@ function FieldsComponent({
fontWeight: "400",
fontSize: "15px",
padding: "3px 20px 0px 20px",
color: "black",
color: "black"
}}
>
Signature
@@ -373,7 +318,7 @@ function FieldsComponent({
style={{
width: "30px",
height: "28px",
background: themeColor(),
background: themeColor()
}}
src={sign}
/>
@@ -393,7 +338,7 @@ function FieldsComponent({
style={{
opacity: isDragStamp ? 0.5 : 1,
boxShadow:
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)",
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)"
}}
>
<span
@@ -401,7 +346,7 @@ function FieldsComponent({
fontWeight: "400",
fontSize: "15px",
padding: "3px 0px 0px 35px",
color: !pdfUrl ? "black" : "#bfbfbf",
color: !pdfUrl ? "black" : "#bfbfbf"
}}
>
Stamp
@@ -412,7 +357,7 @@ function FieldsComponent({
style={{
width: "25px",
height: "28px",
background: themeColor(),
background: themeColor()
}}
src={stamp}
/>
@@ -422,6 +367,41 @@ function FieldsComponent({
</div>
</div>
)}
{isSignersModal && (
<ModalUi
title={"Recipients"}
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 Recipient
</div>
)}
</ModalUi>
)}
</>
);
}
@@ -33,7 +33,10 @@ function Header({
currentSigner,
dataTut4,
alreadySign,
isSignYourself
isSignYourself,
setIsEmail,
completeBtnTitle,
setIsEditTemplate
}) {
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
@@ -222,10 +225,7 @@ function Header({
);
};
return (
<div
style={{ padding: !isGuestSigner && "5px 0px 5px 0px" }}
className="mobileHead"
>
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
{isMobile && isShowHeader ? (
<div
id="navbar"
@@ -329,9 +329,29 @@ function Header({
</DropdownMenu.Item>
) : (
isSignYourself && (
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
</DropdownMenu.Item>
<>
<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
@@ -394,7 +414,7 @@ function Header({
}}
data-tut={dataTut4}
>
Send
{completeBtnTitle ? completeBtnTitle : "Send"}
</div>
) : (
<div
@@ -512,17 +532,17 @@ function Header({
) : isPlaceholder ? (
<>
{!isMailSend &&
signersdata.Signers &&
signersdata.Signers.length !== signerPos.length && (
signersdata.length > 0 &&
signersdata.length !== signerPos.length && (
<div>
{signerPos.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add all {signersdata.Signers.length - signerPos.length}{" "}
recipients signature
Add {signersdata.length - signerPos.length} recipients
signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.Signers.length - signerPos.length} more
Add {signersdata.length - signerPos.length} more
recipients signature
</span>
)}
@@ -530,6 +550,18 @@ function Header({
)}
<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);
@@ -552,7 +584,7 @@ function Header({
}}
className={isMailSend ? "sendMail" : "sendMail sendHover"}
>
Send
{completeBtnTitle ? completeBtnTitle : "Send"}
</button>
</div>
</>
@@ -690,6 +722,28 @@ function Header({
></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>
@@ -1,13 +1,16 @@
import React from "react";
import Modal from "react-bootstrap/Modal";
import ModalHeader from "react-bootstrap/esm/ModalHeader";
import { useNavigate } from "react-router-dom";
function ModalComponent({ isShow, type, setIsShowEmail }) {
const navigate = useNavigate();
return (
<Modal show={isShow}>
<ModalHeader className="bg-danger" style={{ color: "white" }}>
{type === "signersAlert" ? (
<span>Select signers</span>
) : type === "alreadyPlace" ? (
<span>Already Placed!</span>
) : (
<span>Document Expired!</span>
)}
@@ -16,24 +19,42 @@ function ModalComponent({ isShow, type, setIsShowEmail }) {
<Modal.Body>
{type === "signersAlert" ? (
<p>Please select signer for add placeholder!</p>
) : type === "alreadyPlace" ? (
<p>Already set placeHolder for this document.</p>
) : (
<p>This Document is no longer available.</p>
)}
</Modal.Body>
<Modal.Footer>
{type === "signersAlert" &&(
<button
onClick={() => {
setIsShowEmail(false);
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Ok
</button>)}
{type === "signersAlert" ? (
<button
onClick={() => {
setIsShowEmail(false);
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Ok
</button>
) : (
type === "alreadyPlace" && (
<button
onClick={() => {
navigate(-1);
}}
style={{
color: "black"
}}
type="button"
className="finishBtn"
>
Ok
</button>
)
)}
</Modal.Footer>
</Modal>
);
@@ -0,0 +1,32 @@
import React from "react";
import { themeColor } from "../../utils/ThemeColor/backColor";
import { resizeBorderExtraWidth } from "../../utils/Utils";
function PlaceholderBorder(props) {
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
return (
<div
className="borderResize"
style={{
borderColor: themeColor(),
borderStyle: "dashed",
// width: props.pos.Width
// ? props.pos.Width + getResizeBorderExtraWidth
// : 150 + getResizeBorderExtraWidth,
// height: props.pos.Height
// ? props.pos.Height + getResizeBorderExtraWidth
// : 60 + getResizeBorderExtraWidth,
width:
props?.posWidth(props.pos, props?.isSignYourself) +
getResizeBorderExtraWidth,
height:
props?.posHeight(props.pos, props?.isSignYourself) +
getResizeBorderExtraWidth,
borderWidth: "0.2px",
overflow: "hidden"
}}
></div>
);
}
export default PlaceholderBorder;
@@ -10,6 +10,7 @@ function RenderAllPdfPage({
setAllPages,
setPageNumber,
setSignBtnPosition,
pageNumber
}) {
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
@@ -20,75 +21,80 @@ function RenderAllPdfPage({
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
style={{
display: "flex",
flexDirection: "row",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
backgroundColor: "white",
height: "100%"
}}
>
<div style={{ width: "140px" }}>
<div
style={{
background: themeColor()
// color:"white"
}}
className="signedStyle"
>
Pages
</div>
<div>
<RSC id="RSC-Example" style={{width:"135px",height:window.innerHeight-130+'px'}} >
<Document
onLoadSuccess={onDocumentLoad}
file={pdfUrl ? pdfUrl : signPdfUrl}
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
<div>
<RSC
id="RSC-Example"
style={{
width: "135px",
height: window.innerHeight - 130 + "px"
}}
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
style={{
width: "100px",
border: "2px solid #878787",
// padding: "5px",
margin: "10px",
<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>
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>
<hr />
</div>
</div>
</div>
);
}
File diff suppressed because it is too large Load Diff
@@ -18,18 +18,45 @@ function SignPad({
isImageSelect,
imageRef,
onImageChange,
onSaveImage,
image,
defaultSign,
setSignature,
setSignature
}) {
const [penColor, setPenColor] = useState("blue");
const allColor = [bluePen, redPen, blackPen];
const canvasRef = useRef(null);
const spanRef = useRef(null);
const [isDefaultSign, setIsDefaultSign] = useState(false);
const [isTab, setIsTab] = useState("signature");
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);
useEffect(() => {
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;
//function for clear signature
setSignValue(currentUserName);
setFontSelect("Fasthand");
}, []);
//function for clear signature
const handleClear = () => {
if (canvasRef.current) {
@@ -47,14 +74,12 @@ function SignPad({
//save button component
const SaveBtn = () => {
// console.log("isSign",isSignImg,image)
return (
<div>
{!isStamp && !isImageSelect && (
<button
style={{
color: "black",
color: "black"
}}
type="button"
className="finishBtn saveBtn"
@@ -71,8 +96,13 @@ function SignPad({
setIsSignImg("");
onSaveSign(isDefaultSign);
} else {
setIsSignImg("");
onSaveSign();
if (isTab === "type") {
setIsSignImg("");
onSaveSign(false, textWidth, textHeight);
} else {
setIsSignImg("");
onSaveSign();
}
}
setPenColor("blue");
@@ -85,13 +115,15 @@ function SignPad({
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("signature");
setIsTab("draw");
}}
style={{
background: themeColor(),
color: "white",
color: "white"
}}
disabled={isSignImg || image || isDefaultSign ? false : true}
disabled={
isSignImg || image || isDefaultSign || textWidth ? false : true
}
type="button"
className={
isSignImg || image ? "finishBtn saveBtn" : "disabledFinish saveBtn"
@@ -103,12 +135,81 @@ function SignPad({
);
};
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();
}, [fontSelect]);
useEffect(() => {
// Load the default signature after the component mounts
if (canvasRef.current) {
canvasRef.current.fromDataURL(isSignImg);
}
if (isTab === "type") {
convertToImg(fontSelect, signValue);
}
}, [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 */}
@@ -120,7 +221,7 @@ function SignPad({
justifyContent: "space-between",
background: "white",
borderBottom: "1.6px solid #ebe6e6",
marginTop: "15px",
marginTop: "15px"
}}
>
<div
@@ -129,14 +230,12 @@ function SignPad({
flexDirection: "row",
justifyContent: "space-around",
alignItems: "end",
gap: 10,
// background: themeColor(),
gap: 10
}}
>
{isStamp ? (
<span style={{ color: themeColor() }} className="signTab">
Upload Image
Upload stamp image
</span>
) : (
<>
@@ -145,25 +244,25 @@ function SignPad({
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("signature");
setIsTab("draw");
setImage();
}}
style={{
color: isTab === "signature" ? themeColor() : "#515252",
color: isTab === "draw" ? themeColor() : "#515252",
marginLeft: "2px",
marginLeft: "2px"
}}
className="signTab"
>
Signature
Draw
</span>
<div
style={{
border:
isTab === "signature"
isTab === "draw"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
: "1.5px solid #ffffff"
}}
></div>
</div>
@@ -175,7 +274,7 @@ function SignPad({
setIsTab("uploadImage");
}}
style={{
color: isTab === "uploadImage" ? themeColor() : "#515252",
color: isTab === "uploadImage" ? themeColor() : "#515252"
}}
className="signTab"
>
@@ -186,11 +285,37 @@ function SignPad({
border:
isTab === "uploadImage"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
: "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>
{defaultSign && (
<div>
<span
@@ -202,7 +327,7 @@ function SignPad({
}}
style={{
color:
isTab === "mysignature" ? themeColor() : "#515252",
isTab === "mysignature" ? themeColor() : "#515252"
}}
className="signTab"
>
@@ -213,7 +338,7 @@ function SignPad({
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff",
: "1.5px solid #ffffff"
}}
></div>
</div>
@@ -228,7 +353,7 @@ function SignPad({
background: "none",
paddingLeft: "7px",
paddingRight: "7px",
marginRight: "5px",
marginRight: "5px"
}}
onClick={() => {
setPenColor("blue");
@@ -237,7 +362,7 @@ function SignPad({
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("signature");
setIsTab("draw");
}}
>
X
@@ -251,13 +376,12 @@ function SignPad({
<div
style={{
border: "1px solid black",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer",
cursor: "pointer"
// background:'rgb(255, 255, 255)'
}}
className="signatureCanvas"
@@ -268,7 +392,7 @@ function SignPad({
width: "100%",
height: "100%",
background: "rgb(255, 255, 255)",
objectFit: "contain",
objectFit: "contain"
}}
src={defaultSign}
/>
@@ -288,7 +412,7 @@ function SignPad({
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer",
cursor: "pointer"
}}
className="signatureCanvas"
onClick={() => imageRef.current.click()}
@@ -314,7 +438,7 @@ function SignPad({
border: "1px solid black",
marginBottom: 6,
// justifyContent:"center"
overflow: "hidden",
overflow: "hidden"
}}
className="signatureCanvas"
>
@@ -325,7 +449,7 @@ function SignPad({
src={image.src}
style={{
//overflow:"hidden",
objectFit: "contain",
objectFit: "contain"
}}
/>
</div>
@@ -334,38 +458,71 @@ function SignPad({
</div>
</>
)
) : isTab === "type" ? (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}}
>
<span className="signatureText">Signature:</span>
<input
maxLength={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 ref={spanRef}>nwbfmb</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>
) : (
<>
{/* {isSignImg ? (
<div
style={{
position: "relative",
border: "2px solid #888",
marginBottom: 6,
}}
className="signatureCanvas"
>
<img
alt="preview image"
src={isSignImg}
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</div>
) : ( */}
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
// width: "460px",
// height: "184px",
// width:"280px",
// height:"112px",
className: "signatureCanvas",
className: "signatureCanvas"
}}
backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
@@ -373,17 +530,16 @@ function SignPad({
}
dotSize={1}
/>
{/* )} */}
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop:"4px"
marginTop: "4px"
}}
>
<div style={{display:"flex",flexDirection:"row"}}>
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<img
@@ -396,10 +552,10 @@ function SignPad({
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white",
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
@@ -424,50 +580,6 @@ function SignPad({
</>
)}
</Modal.Body>
{/* <Modal.Footer>
{!isStamp && !isImageSelect && (
<button
style={{
color: "black",
}}
type="button"
className="finishBtn"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
if (!image) {
if (isDefaultSign) {
onSaveSign(isDefaultSign);
} else {
onSaveSign();
}
setPenColor("blue");
} else {
onSaveImage();
}
setIsSignPad(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
}}
style={{
background: themeColor(),
color: "white",
}}
type="button"
className="finishBtn"
>
Save
</button>
</Modal.Footer> */}
</Modal>
</div>
);
@@ -1,161 +1,32 @@
import React, { useState } from "react";
import check from "../../assests/checkBox.png";
import React from "react";
import { themeColor } from "../../utils/ThemeColor/backColor";
import "../../css/signerListPlace.css";
import RecipientList from "../../premitives/RecipientList";
function SignerListPlace({
signerPos,
signersdata,
isSelectListId,
setSignerObjId,
setIsSelectId,
setContractName
}) {
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc",
];
const nameColor = [
"#304fbf",
"#7d5270",
"#5f825b",
"#578077",
"#576e80",
"#6d527d",
"#cc00cc",
"#006666",
"#cc00ff",
"#ff9900",
"#336699",
"#cc9900",
];
const [isHover, setIsHover] = useState();
//function for onhover signer name change background color
const onHoverStyle = (ind) => {
const style = {
background: color[ind % color.length],
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
};
return style;
};
//function for onhover signer name remove background color
const nonHoverStyle = (ind) => {
const style = {
// width:"250px",
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
};
return style;
};
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
};
function SignerListPlace(props) {
return (
<div >
<div>
<div
style={{
background: themeColor(),
padding: "5px",
padding: "5px"
}}
>
<span className="signedStyle">Reicipents</span>
<span className="signedStyle">Recipients</span>
</div>
<div className="signerList">
{signersdata.Signers &&
signersdata.Signers.map((obj, ind) => {
return (
<div
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
key={ind}
style={
isHover === ind || isSelectListId === ind
? onHoverStyle(ind)
: nonHoverStyle(ind)
}
onClick={() => {
setSignerObjId(obj.objectId);
setIsSelectId(ind);
setContractName(obj.className)
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
}}
>
<div
className="signerStyle"
style={{
background: nameColor[ind % nameColor.length],
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px",
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold",
}}
>
{" "}
{getFirstLetter(obj.Name)}
</span>
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<span className="userName">{obj.Name}</span>
<span className="useEmail">{obj.Email}</span>
</div>
</div>
{signerPos.map((data, key) => {
return (
data.signerObjId === obj.objectId && (
<div key={key}>
<img alt="no img" src={check} width={20} height={20} />
</div>
)
);
})}
<hr />
</div>
);
})}
<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>
);
}
@@ -103,7 +103,12 @@ function Login() {
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
File diff suppressed because it is too large Load Diff
@@ -21,7 +21,10 @@ import {
contractDocument,
urlValidator,
multiSignEmbed,
embedDocId
embedDocId,
signPdfFun,
addDefaultSignatureImg,
onImageSelect
} from "../utils/Utils";
import Tour from "reactour";
import Signedby from "./component/signedby";
@@ -33,6 +36,9 @@ import Header from "./component/header";
import RenderPdf from "./component/renderPdf";
import CustomModal from "./component/CustomModal";
import { modalAlign } from "../utils/Utils";
import AlertComponent from "./component/alertComponent";
import TourContentWithBtn from "../premitives/TourContentWithBtn";
import Title from "./component/Title";
function EmbedPdfImage() {
const { id, contactBookId } = useParams();
const [isSignPad, setIsSignPad] = useState(false);
@@ -64,18 +70,23 @@ function EmbedPdfImage() {
const [checkTourStatus, setCheckTourStatus] = useState(false);
const [signerUserId, setSignerUserId] = useState();
const [tourStatus, setTourStatus] = useState([]);
const [completePdfData, setCompletePdfData] = useState([]);
const [isExpired, setIsExpired] = useState(false);
const [noData, setNoData] = useState(false);
const [contractName, setContractName] = useState("");
const [isDecline, setIsDecline] = useState({
isDeclined: false
});
const [addDefaultSign, setAddDefaultSign] = useState({
isShow: false,
alertMessage: ""
});
const [pdfLoadFail, setPdfLoadFail] = useState({
status: false,
type: "load"
});
const [containerWH, setContainerWH] = useState({});
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isDontShow, setIsDontShow] = useState(false);
const docId = id && id;
const isMobile = window.innerWidth < 767;
const index = xyPostion.findIndex((object) => {
@@ -91,8 +102,7 @@ function EmbedPdfImage() {
: `${localStorage.getItem("UserInformation")}` &&
`${localStorage.getItem("UserInformation")}`;
const jsonSender = JSON.parse(senderUser);
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
useEffect(() => {
getDocumentDetails();
}, []);
@@ -201,7 +211,6 @@ function EmbedPdfImage() {
setIsAlreadySign(alreadySign);
setPdfUrl(documentData[0].SignedUrl);
setSignedPdfData(documentData);
setCompletePdfData(documentData);
} else if (declined) {
const currentDecline = {
currnt: "another",
@@ -396,7 +405,10 @@ function EmbedPdfImage() {
}
if (checkSignUrl && checkSignUrl.length > 0) {
alert("Please complete your signature!");
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
} else {
const loadObj = {
isLoad: true,
@@ -417,187 +429,124 @@ function EmbedPdfImage() {
const pngUrl = xyPostion;
let pdfBase64;
// let pdfBase64;
//checking if signature is only one then send image url in jpeg formate to server
if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
if (isDocId) {
pdfBase64 = await getBase64FromUrl(url);
// if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
// if (isDocId) {
// pdfBase64 = await getBase64FromUrl(url);
// } else {
// //embed document's object id to all pages in pdf document
// await embedDocId(pdfDoc, docId, allPages);
// pdfBase64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
// }
// for (let xyData of xyPostion) {
// const imgUrlList = xyData.pos;
// const pageNo = xyData.pageNumber;
// imgUrlList.map(async (data) => {
// //cheking signUrl is defau;t signature url of custom url
// let ImgUrl = data.SignUrl;
// const checkUrl = urlValidator(ImgUrl);
// //if default signature url then convert it in base 64
// if (checkUrl) {
// ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
// }
// //function for convert signature png base64 url to jpeg base64
// convertPNGtoJPEG(ImgUrl)
// .then((jpegBase64Data) => {
// const removeBase64Fromjpeg = "data:image/jpeg;base64,";
// const newImgUrl = jpegBase64Data.replace(
// removeBase64Fromjpeg,
// ""
// );
// //function for embed signature in pdf and get digital signature pdf
// signPdfFun(
// newImgUrl,
// docId,
// signerUserId,
// pdfOriginalWidth,
// xyPostion,
// containerWH,
// setIsAlert,
// data,
// pdfBase64,
// pageNo
// )
// .then((res) => {
// if (res && res.status === "success") {
// getDocumentDetails();
// } else {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// }
// })
// .catch((err) => {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// });
// })
// .catch((error) => {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// });
// });
// }
// }
// //else if signature is more than one then embed all sign with the use of pdf-lib
// else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
const flag = false;
//embed document's object id to all pages in pdf document
if (!isDocId) {
await embedDocId(pdfDoc, docId, allPages);
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
pdfDoc,
pdfOriginalWidth,
flag,
containerWH
);
//function for embed signature in pdf and get digital signature pdf
try {
const res = await signPdfFun(
pdfBytes,
docId,
signerUserId,
pdfOriginalWidth,
xyPostion,
containerWH,
setIsAlert
);
if (res && res.status === "success") {
getDocumentDetails();
} else {
//embed document's object id to all pages in pdf document
await embedDocId(pdfDoc, docId, allPages);
pdfBase64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
}
for (let i = 0; i < xyPostion.length; i++) {
const imgUrlList = pngUrl[i].pos;
const pageNo = pngUrl[i].pageNumber;
imgUrlList.map(async (data) => {
//cheking signUrl is defau;t signature url of custom url
let ImgUrl = data.SignUrl;
const checkUrl = urlValidator(ImgUrl);
//if default signature url then convert it in base 64
if (checkUrl) {
ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
}
//function for convert signature png base64 url to jpeg base64
convertPNGtoJPEG(ImgUrl)
.then((jpegBase64Data) => {
const removeBase64Fromjpeg = "data:image/jpeg;base64,";
const newImgUrl = jpegBase64Data.replace(
removeBase64Fromjpeg,
""
);
//function for call to embed signature in pdf and get digital signature pdf
signPdfFun(newImgUrl, docId, data, pdfBase64, pageNo);
})
.catch((error) => {
console.error("Error:", error);
});
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
} catch (err) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
//else if signature is more than one then embed all sign with the use of pdf-lib
else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
//embed document's object id to all pages in pdf document
await embedDocId(pdfDoc, docId, allPages);
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
pdfDoc,
pdfOriginalWidth,
false
);
signPdfFun(pdfBytes, docId);
}
setIsSignPad(false);
setXyPostion([]);
}
setIsSignPad(false);
setXyPostion([]);
// }
}
//function for call cloud function signPdf and generate digital signature
const signPdfFun = async (
base64Url,
docId,
xyPosData,
pdfBase64Url,
pageNo
) => {
let singleSign;
const isMobile = window.innerWidth < 767;
const newWidth = window.innerWidth;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
const height = xyPosData ? xyPosData.Height : 60;
const xPos = (pos) => {
//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) {
const x = pos.xPosition * (pos.scale / scale);
return x * scale + 50;
} else {
const x = pos.xPosition / scale;
return x * 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) {
const x = pos.xPosition * pos.scale + 50;
return x;
} else {
return pos.xPosition;
}
}
};
const yBottom = (pos) => {
let yPosition;
//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) {
const y = pos.yPosition * (pos.scale / scale);
yPosition = pos.isDrag
? y * scale - height
: pos.firstYPos
? y * scale - height + pos.firstYPos
: y * scale - height;
return yPosition;
} else {
const y = pos.yBottom / scale;
yPosition = pos.isDrag
? y * scale - height
: pos.firstYPos
? y * scale - height + pos.firstYPos
: y * scale - height;
return yPosition;
}
} 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) {
const y = pos.yBottom * pos.scale;
yPosition = pos.isDrag
? y - height
: pos.firstYPos
? y - height + pos.firstYPos
: y - height;
return yPosition;
} else {
yPosition = pos.isDrag
? pos.yBottom - height
: pos.firstYPos
? pos.yBottom - height + pos.firstYPos
: pos.yBottom - height;
return yPosition;
}
}
};
if (xyPostion.length === 1 && xyPostion[0].pos.length === 1) {
const bottomY = yBottom(xyPosData);
singleSign = {
pdfFile: pdfBase64Url,
docId: docId,
userId: signerUserId,
sign: {
Base64: base64Url,
Left: xPos(xyPosData),
Bottom: bottomY,
Width: xyPosData.Width ? xyPosData.Width : 150,
Height: height,
Page: pageNo
}
};
} else if (xyPostion.length > 0 && xyPostion[0].pos.length > 0) {
singleSign = {
pdfFile: base64Url,
docId: docId,
userId: signerUserId
};
}
await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
})
.then((Listdata) => {
const json = Listdata.data;
if (json.result.data) {
getDocumentDetails();
}
})
.catch((err) => {
alert("something went wrong");
});
};
//function for change page
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
@@ -606,60 +555,44 @@ function EmbedPdfImage() {
//function for image upload or update
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
const imageType = event.target.files[0].type;
const reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onloadend = function (e) {
let width, height;
const image = new Image();
image.src = e.target.result;
image.onload = function () {
width = image.width;
height = image.height;
const aspectRatio = 460 / 184;
const imgR = width / height;
if (imgR > aspectRatio) {
width = 460;
height = 460 / imgR;
} else {
width = 184 * imgR;
height = 184;
}
setImgWH({ width: width, height: height });
imageRef.current.style.width = `${width}px`;
imageRef.current.style.height = `${height}px`;
};
image.src = reader.result;
setImage({ src: image.src, imgType: imageType });
};
onImageSelect(event, setImgWH, setImage);
}
};
//function for save button to save signature or image url
const saveSign = (isDefaultSign) => {
const saveSign = (isDefaultSign, width, height) => {
const isTypeText = width && height ? true : false;
const signatureImg = isDefaultSign ? defaultSignImg : signature;
let imgWH = { width: width ? width : "", height: height ? height : "" };
setIsSignPad(false);
setIsImageSelect(false);
setImage();
if (isDefaultSign) {
const img = new Image();
img.src = defaultSignImg;
if (img.complete) {
imgWH = {
width: img.width,
height: img.height
};
}
}
const getUpdatePosition = onSaveSign(
xyPostion,
index,
signKey,
signatureImg
signatureImg,
imgWH,
isDefaultSign,
isTypeText
);
if (getUpdatePosition) {
setXyPostion(getUpdatePosition);
}
};
//function for upload stamp image
const saveImage = () => {
const getImage = onSaveImage(xyPostion, index, signKey, imgWH, image);
@@ -697,98 +630,106 @@ function EmbedPdfImage() {
}
})
.catch((err) => {
console.log("error updating field is decline ", err);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
};
const addDefaultSignature = () => {
let xyDefaultPos = [];
for (let i = 0; i < xyPostion.length; i++) {
const getXYdata = xyPostion[i].pos;
const getPageNo = xyPostion[i].pageNumber;
const getPosData = getXYdata;
const getXyData = addDefaultSignatureImg(xyPostion, defaultSignImg);
const addSign = getPosData.map((url, ind) => {
if (url) {
return { ...url, SignUrl: defaultSignImg };
}
return url;
});
const newXypos = {
pageNumber: getPageNo,
pos: addSign
};
xyDefaultPos.push(newXypos);
}
setXyPostion(xyDefaultPos);
setIsAlreadySign({ status: false });
setXyPostion(getXyData);
setAddDefaultSign({
isShow: false,
alertMessage: ""
});
};
//function for update TourStatus
const closeTour = async () => {
setSignTour(false);
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const recipientssignIndex = tourStatus.findIndex(
(obj) =>
obj["recipientssign"] === false || obj["recipientssign"] === true
);
if (recipientssignIndex !== -1) {
updatedTourStatus[recipientssignIndex] = { recipientssign: true };
} else {
updatedTourStatus.push({ recipientssign: true });
}
} else {
updatedTourStatus = [{ recipientssign: true }];
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
if (isDontShow) {
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const recipientssignIndex = tourStatus.findIndex(
(obj) =>
obj["recipientssign"] === false || obj["recipientssign"] === true
);
if (recipientssignIndex !== -1) {
updatedTourStatus[recipientssignIndex] = { recipientssign: true };
} else {
updatedTourStatus.push({ recipientssign: true });
}
)
.then((Listdata) => {
// const json = Listdata.data;
// const res = json.results;
// console.log("res", json);
})
.catch((err) => {
console.log("axois err ", err);
});
} else {
updatedTourStatus = [{ recipientssign: true }];
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then((Listdata) => {
// const json = Listdata.data;
// const res = json.results;
// console.log("res", json);
})
.catch((err) => {
console.log("axois err ", err);
});
}
};
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const tourFunction = () => {
const tourConfig = [
{
selector: '[data-tut="reactourFirst"]',
content: `You can click "Auto Sign All" to automatically sign at all the locations meant to be signed by you.Make sure that you review the document properly before you click this button.`,
content: () => (
<TourContentWithBtn
message={`You can click "Auto Sign All" to automatically sign at all the locations meant to be signed by you.Make sure that you review the document properly before you click this button.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourSecond"]',
content: `Click any of such placeholders appearing on the document to sign.You will see the options to draw sign or upload an image once you click here.`,
content: () => (
<TourContentWithBtn
message={`Click any of such placeholders appearing on the document to sign.You will see the options to draw sign or upload an image once you click here.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourThird"]',
content: `Click here to finish & download the signed document.You will also receive a copy on your email.`,
content: () => (
<TourContentWithBtn
message={`Click here to finish & download the signed document.You will also receive a copy on your email.`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
@@ -814,6 +755,7 @@ function EmbedPdfImage() {
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Sign Document"} />
{isLoading.isLoad ? (
<Loader isLoading={isLoading} />
) : handleError ? (
@@ -845,6 +787,20 @@ function EmbedPdfImage() {
declineDoc={declineDoc}
setIsDecline={setIsDecline}
/>
<AlertComponent
isShow={isAlert.isShow}
alertMessage={isAlert.alertMessage}
setIsAlert={setIsAlert}
/>
<AlertComponent
isShow={addDefaultSign.isShow}
alertMessage={addDefaultSign.alertMessage}
setIsAlert={setAddDefaultSign}
isdefaultSign={true}
addDefaultSignature={addDefaultSignature}
headBG={themeColor()}
/>
{/* this modal is used for show expired alert */}
<CustomModal
containerWH={containerWH}
@@ -865,14 +821,16 @@ function EmbedPdfImage() {
allPages={allPages}
setAllPages={setAllPages}
setPageNumber={setPageNumber}
pageNumber={pageNumber}
/>
{/* <div style={{width:"160px",border:"1px solid red"}}>kebjke</div> */}
{/* pdf render view */}
<div
style={{
marginLeft: !isGuestSigner && pdfOriginalWidth > 500 && "20px",
marginRight: !isGuestSigner && pdfOriginalWidth > 500 && "20px"
marginLeft: !isMobile && pdfOriginalWidth > 500 && "20px",
marginRight: !isMobile && pdfOriginalWidth > 500 && "20px"
}}
ref={divRef}
>
{/* this modal is used show this document is already sign */}
<Modal
@@ -901,18 +859,6 @@ function EmbedPdfImage() {
>
Close
</button>
{isAlreadySign.sure && (
<button
onClick={() => addDefaultSignature()}
style={{
background: themeColor()
}}
type="button"
className="finishBtn"
>
Yes
</button>
)}
</Modal.Footer>
</Modal>
{/* this is modal of signature pad */}
@@ -948,25 +894,29 @@ function EmbedPdfImage() {
decline={true}
alreadySign={pdfUrl ? true : false}
/>
<RenderPdf
pageNumber={pageNumber}
pdfOriginalWidth={pdfOriginalWidth}
pdfNewWidth={pdfNewWidth}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
setSignKey={setSignKey}
pdfDetails={signedPdfData}
xyPostion={xyPostion}
successEmail={false}
pdfUrl={pdfUrl}
numPages={numPages}
pageDetails={pageDetails}
recipient={true}
isAlreadySign={isAlreadySign}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
/>
{containerWH && (
<RenderPdf
pageNumber={pageNumber}
pdfOriginalWidth={pdfOriginalWidth}
pdfNewWidth={pdfNewWidth}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
setSignKey={setSignKey}
pdfDetails={signedPdfData}
xyPostion={xyPostion}
successEmail={false}
pdfUrl={pdfUrl}
numPages={numPages}
pageDetails={pageDetails}
recipient={true}
isAlreadySign={isAlreadySign}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
setXyPostion={setXyPostion}
index={index}
containerWH={containerWH}
/>
)}
</div>
{!pdfUrl ? (
@@ -982,6 +932,7 @@ function EmbedPdfImage() {
xyPostion={xyPostion}
setXyPostion={setXyPostion}
setShowAlreadySignDoc={setIsAlreadySign}
setIsAlert={setAddDefaultSign}
/>
</div>
) : (
+8 -3
View File
@@ -9,7 +9,10 @@ import DraftDocument from "./Component/DraftDocument";
import PdfRequestFiles from "./Component/PdfRequestFiles";
import LegaDrive from "./Component/LegaDrive/LegaDrive";
import PageNotFound from "./Component/PageNotFound";
import TemplatePlaceHolder from "./Component/TemplatePlaceholder";
import Parse from "parse";
Parse.serverURL = localStorage.getItem("baseUrl");
Parse.initialize(localStorage.getItem("parseAppId"));
// `AppRoutes` is used to define route path of app and
// it expose to host app, check moduleFederation.config.js for more
function AppRoutes() {
@@ -24,8 +27,9 @@ function AppRoutes() {
<Route path="/signaturePdf/:docId" element={<SignYourselfPdf />} />
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:id/:contactBookId"
element={<RecipientSignPdf />}
path="/recipientSignPdf/:docId/:contactBookId"
// element={<RecipientSignPdf />
element={<PdfRequestFiles />}
/>
{/* recipient placeholder set route with rowlevel data */}
<Route path="/placeHolderSign" element={<PlaceHolderSign />} />{" "}
@@ -47,6 +51,7 @@ function AppRoutes() {
{/* lega drive route */}
<Route path="/legadrive" element={<LegaDrive />} />
{/* Page Not Found */}
<Route path="/template/:templateId" element={<TemplatePlaceHolder />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</div>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

@@ -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,72 @@
.modaloverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.75);
justify-content: center;
align-items: center;
z-index: 900;
}
.modalcontainer {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
z-index: 901;
max-height: 80%;
min-width: 500px;
overflow-y: auto;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer and Edge */
}
/* Hide scrollbar for Webkit browsers */
.modalcontainer::-webkit-scrollbar {
display: none;
}
.modalheader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
border-bottom: 1px solid #ccc;
color: #ffffff;
}
/* Styling for the modal title */
.modaltitle {
font-size: 1.2rem;
font-weight:normal;
}
/* Styling for the close button */
.closebtn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
@media (max-width:767px) {
.modalcontainer {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
max-height: 80%;
min-width: 90%;
overflow-y: auto;
}
}
@@ -5,11 +5,12 @@
background-color: rgba(0, 0, 0, 0.75);
display: flex;
flex-direction: column;
align-items: center;
align-items: center;
justify-content: center;
}
.childDiv {
width: 50%;
width: 40%;
display: flex;
flex-direction: column;
justify-content: center;
@@ -27,7 +28,7 @@
border-top-left-radius: 0.3rem;
border-top-right-radius: 0.3rem;
font-size: 17px;
font-weight: 500;
font-weight: 400;
}
.modalBodyDIv {
@@ -48,12 +49,12 @@
.modalFooterDiv {
margin: 10px;
display: flex;
justify-content: flex-end;
justify-content: flex-start;
}
@media screen and (max-width:500px) {
.childDiv {
margin: 50px 7px;
width: 90%;
width: 80%;
}
}
@@ -0,0 +1,6 @@
.hrStyle{
border-top-width: 1px !important;
color: #c3bcbc !important;
height: 0px !important;
margin-bottom: 10px !important;
}
@@ -1,3 +1,7 @@
@import url('https://fonts.googleapis.com/css2?family=Fasthand&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Delicious+Handrawn&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Cedarville+Cursive&display=swap');
.signatureCanvas {
border: 2px solid #888;
background-color: rgb(255, 255, 255);
@@ -9,6 +13,36 @@
width: 460px;
}
.borderResize {
position: absolute;
display: inline-block;
width: 14px;
height: 14px;
}
.mailBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background-color: rgb(79 190 241);
border: none !important;
color: white !important;
}
.mailBtn:hover {
box-shadow: 0 2px 4px rgba(154, 36, 36, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.emailToast {
position: absolute;
z-index: 10;
background: #aeedae;
padding: 0 3px 3px 3px;
margin: 2px;
border-radius: 2px;
}
.signatureBtn {
border: 1.5px solid #47a3ad;
margin-bottom: 10px;
@@ -80,10 +114,6 @@
padding: 5px 10px;
}
.emailChipClose:hover {
cursor: pointer;
}
.addEmailInput {
padding: 10px;
border-radius: 4px;
@@ -97,10 +127,9 @@
}
.signatureInput {
padding: 10px;
width: 80%;
border-radius: 4px;
font-size: 0.75rem;
font-size: 20px;
border: 1px solid #e2dddd;
color: black;
font-family: inherit;
@@ -108,12 +137,83 @@
font-variation-settings: inherit;
font-weight: inherit;
overflow: visible;
height: 30px;
}
.signatureInput:focus {
outline: none;
border: 1px solid blue;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
}
.resizable-box-overlay {
position: absolute;
border: 2px solid #ddd;
overflow: hidden;
background-color: white;
}
.box-content {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.close-button {
align-self: flex-end;
margin: 8px;
cursor: pointer;
}
.placeholdCloseBtn {
position: absolute;
right: -5px;
top: -5px;
border-radius: 100%;
font-size: 10px;
cursor: pointer;
padding: 0px 5.1px 1px 5px;
}
.signCloseBtn {
position: absolute;
right: -8px;
top: -18px;
font-size: 14px;
cursor: pointer;
z-index: 2;
background: white;
}
.signCopy {
position: absolute;
right: 12px;
top: -18px;
font-size: 13px;
cursor: pointer;
z-index: 2;
background: white;
}
.signUserIcon {
position: absolute;
right: 32px;
top: -18px;
font-size: 13px;
cursor: pointer;
z-index: 2;
background: white;
}
.ScrollbarsCustom-Track {
width: 4px !important;
}
.signYourselfBlock {
padding: 0px;
display: flex !important;
text-align: center !important;
justify-content: center !important;
align-items: center !important;
}
.finishBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 30px;
@@ -165,7 +265,7 @@
.defaultSignBox {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
width: 180px;
width: 90%;
height: 111px;
padding: 8px 20px;
color: white;
@@ -343,6 +443,7 @@
width: 180px;
background-color: white;
height: 100%;
}
.modalBody {
@@ -458,7 +559,6 @@
gap: 5px;
border: none;
background-color: #887abf;
width: 200px;
white-space: nowrap;
overflow: hidden !important;
@@ -469,6 +569,7 @@ option {
overflow-y: scroll;
}
.SelectTrigger:hover {
border: none;
outline: none;
@@ -570,16 +671,30 @@ option {
.fontOptionContainer {
border: 1px solid #d6d3d3;
margin: 10px 5px 5px 5px;
margin: 10px 0px 5px 0px;
}
.dropDownStyle {
width: auto;
}
.autoSignScroll{
overflow-y: auto;
}
.autoSignScroll::-webkit-scrollbar {
display: none;
}
@media screen and (max-width:766px) {
.showPages {
display: none;
}
.dropdown-menu {
min-width: 0rem !important;
}
.signatureContainer {
display: flex;
flex-direction: column;
@@ -606,8 +721,7 @@ option {
#navbar {
overflow: hidden;
z-index: 10;
z-index: 49;
}
/* Navbar links */
@@ -643,7 +757,7 @@ option {
.stickyfooter {
position: fixed;
z-index: 32 !important;
z-index: 99 !important;
bottom: 0px;
right: 0rem;
}
@@ -683,6 +797,10 @@ option {
.mobileHead {
display: flex;
}
.signatureText{
margin-right: 5px;
font-size: 12px;
}
}
@media screen and (max-width:487px) and (min-width:351px) {
@@ -707,6 +825,12 @@ option {
.mobileHead {
display: flex;
}
.signTab {
font-weight: 500 !important;
font-size: 12px !important;
}
}
@@ -765,7 +889,7 @@ option {
font-weight: 500 !important;
font-size: 10px !important;
font-size: 8px !important;
}
.uploadImgLogo {
@@ -784,10 +908,13 @@ option {
.signature-backdrop {
position: fixed;
top: 0; bottom: 0; left: 0; right: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #000;
}
.signature-backdrop.fade.in {
opacity: 0.5;
}
}
@@ -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;
}
@@ -0,0 +1,293 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import "../css/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,30 @@
import React from "react";
const Alert = ({ children, type }) => {
const textcolor = type ? theme(type) : theme();
function theme(color) {
switch (color) {
case "success":
return "border-[#c3e6cb] bg-[#d4edda] text-[#155724]";
case "info":
return "border-[#adcdeb] bg-[#c1daf0] text-[#153756]";
case "danger":
return "border-[#f0a8a8] bg-[#f4bebe] text-[#c42121]";
default:
return "border-[#d6d6d6] bg-[#d9d9d9] text-[#575757]";
}
}
return (
<>
{children && (
<div
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] `}
>
{children}
</div>
)}
</>
);
};
export default Alert;
@@ -0,0 +1,175 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "./Alert";
import { themeColor } from "../utils/ThemeColor/backColor";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
__type: "Pointer",
className: folderCls,
objectId: parentFolderId
};
const [name, setName] = useState("");
const [folderList, setFolderList] = useState([]);
const [isAlert, setIsAlert] = useState(false);
const [selectedParent, setSelectedParent] = useState();
const [alert, setAlert] = useState({ type: "info", message: "" });
useEffect(() => {
fetchFolder();
// eslint-disable-next-line
}, []);
const fetchFolder = async () => {
try {
const FolderQuery = new Parse.Query(folderCls);
if (parentFolderId) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
}
}
} catch (error) {
console.log("Err ", error);
}
};
const handleCreateFolder = async (event) => {
event.preventDefault();
if (name) {
const currentUser = Parse.User.current();
const exsitQuery = new Parse.Query(folderCls);
exsitQuery.equalTo("Name", name);
exsitQuery.equalTo("Type", "Folder");
if (parentFolderId) {
exsitQuery.equalTo("Folder", folderPtr);
}
const templExist = await exsitQuery.first();
if (templExist) {
setAlert({ type: "dange", message: "Folder already exist!" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
} else {
const template = new Parse.Object(folderCls);
template.set("Name", name);
template.set("Type", "Folder");
if (selectedParent) {
template.set("Folder", {
__type: "Pointer",
className: folderCls,
objectId: selectedParent
});
} else if (parentFolderId) {
template.set("Folder", folderPtr);
}
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
const res = await template.save();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (onSuccess) {
setAlert({
type: "success",
message: "Folder created successfully!"
});
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
onSuccess(result);
}
}
}
} else {
setAlert({ type: "info", message: "Please fill folder name" });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
}
};
const handleOptions = (e) => {
setSelectedParent(e.target.value);
};
return (
<div>
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder">
<h1 style={{ fontWeight: "500", fontSize: "1rem" }}>Create Folder</h1>
<div style={{ fontSize: "12px", marginTop: "11px" }}>
<label className="block">
Name<span style={{ color: "red", fontSize: "13px" }}> *</span>
</label>
<input
style={{
padding: "8px 11px",
width: "100%",
border: "1px solid rgb(195, 188, 188)",
borderRadius: "5px",
outline: "none",
fontSize: "12px"
}}
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div style={{ fontSize: "12px", marginTop: "7px" }}>
<label className="block">Parent Folder</label>
<select
value={selectedParent}
onChange={handleOptions}
style={{
padding: "8px 11px",
width: "100%",
border: "1px solid rgb(195, 188, 188)",
borderRadius: "5px",
outline: "none",
fontSize: "12px"
}}
>
<option>select</option>
{folderList.length > 0 &&
folderList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
<div>
<button
onClick={handleCreateFolder}
style={{
display: "flex",
alignItems: "center",
borderRadius: "5px",
backgroundColor: themeColor(),
color: "white",
marginTop: "22px",
outline: "none",
border: "none",
padding: "10px 8px",
fontSize: "14px"
}}
>
<i className="fa-solid fa-plus mr-1"></i>
<span>Create</span>
</button>
</div>
</div>
</div>
);
};
export default CreateFolder;

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