Compare commits

...
57 Commits
Author SHA1 Message Date
prafull-opensignlabsandGitHub e643d96797 Merge pull request #506 from OpenSignLabs/api_signembed
fix: handle uncaught error in createdocument and createmplate API
2024-03-19 13:24:05 +05:30
prafull-opensignlabs 1ff4160ca4 fix: handle uncaught error in createdocument and createmplate API 2024-03-19 13:23:10 +05:30
prafull-opensignlabsandGitHub 62f66bf524 Merge pull request #505 from OpenSignLabs/api_signembed
fix: widgets are not embedding in pdf using API
2024-03-19 13:12:46 +05:30
prafull-opensignlabs de3bc37fa5 fix: widgets are not embeding in pdf using API 2024-03-19 13:11:23 +05:30
AmolandGitHub ea660384c3 Merge pull request #504 from OpenSignLabs/prafull-opensignlabs-patch-4
chore: Update mongo url in local env
2024-03-19 12:01:08 +05:30
prafull-opensignlabsandGitHub 210cb5e754 chore: Update mongo url in local env 2024-03-19 12:00:28 +05:30
AmolandGitHub ed45bfef84 Merge pull request #503 from OpenSignLabs/prafull-opensignlabs-patch-1 2024-03-19 10:59:23 +05:30
prafull-opensignlabsandGitHub a8e77e93f7 chore: change serverURL 2024-03-19 10:58:32 +05:30
prafull-opensignlabsandGitHub 969071bb28 Merge pull request #501 from OpenSignLabs/placeholder_sign
fix: show warning on placeholder_sign page for document already send to signers for signature
2024-03-19 10:29:33 +05:30
RaktimaNXG 76491a77f1 fix: add comment 2024-03-19 10:27:41 +05:30
AmolandGitHub 9a51eed2bc Merge pull request #502 from OpenSignLabs/fix_maxupload 2024-03-19 10:09:48 +05:30
prafull-opensignlabs a675a3c8fd refactor: change console dir to console log 2024-03-18 19:49:13 +05:30
prafull-opensignlabs fea30930eb fix: handle uncaught err of max upload pdf in form 2024-03-18 19:47:56 +05:30
RaktimaNXG 7373b086e4 fix: change warning messages 2024-03-18 19:00:02 +05:30
RaktimaNXG 2fc1eda761 fix: remove console 2024-03-18 18:22:14 +05:30
RaktimaNXG 2c100ad399 fix: show warning on placeholder_sign page for document already send to signers for signature 2024-03-18 17:40:27 +05:30
prafull-opensignlabsandGitHub 1cefe01aee Merge pull request #499 from OpenSignLabs/fix_mobile_issue
fix:  width issue of text widget & default data for widgets information pop-ups in mobile display.
2024-03-18 17:09:17 +05:30
RaktimaNXG f1cf395761 fix: textInput widget popup select arrow key design 2024-03-18 17:05:15 +05:30
RaktimaNXG 4af3d8f78b fix: dragging issue of text widget in mobile view 2024-03-18 16:21:14 +05:30
AmolandGitHub cf21ac7405 Merge pull request #500 from OpenSignLabs/fix_Smtp 2024-03-18 15:16:27 +05:30
prafull-opensignlabs 5f11e92c0c fix: handle uncaught error of SMTP 2024-03-18 15:14:23 +05:30
RaktimaNXG c3ffda4dd2 fix: resolving width inconsistencies with of text widgets & default data for widgets information pop-ups in mobile display. 2024-03-18 12:45:08 +05:30
AmolandGitHub c929b6b7a9 Update Dockerhubfile 2024-03-18 00:19:06 +05:30
AmolandGitHub eb2abd3e6d Merge pull request #493 from OpenSignLabs/widget_modal 2024-03-15 19:33:53 +05:30
prafull-opensignlabs 1c4c2e87b4 refactor: change design of widgets in mobile 2024-03-15 19:30:33 +05:30
prafull-opensignlabs 4fda5b4531 refactor: stop showing initial popup when wdigets drag 2024-03-15 19:04:02 +05:30
AmolandGitHub df61e143af Merge pull request #492 from OpenSignLabs/update_menu 2024-03-15 14:27:06 +05:30
prafull-opensignlabs 3a55c35445 feat: add create document btn in dashboard, update menu & change Root to Opensign™ drive in folder input 2024-03-15 14:25:50 +05:30
prafull-opensignlabsandGitHub 08476ba3d6 Merge pull request #490 from OpenSignLabs/feat_label
fix: date is not changable in signyourself flow of mobile
2024-03-14 21:40:40 +05:30
prafull-opensignlabs db9ae71d67 fix: date is not changable in signyourself flow of mobile 2024-03-14 21:39:22 +05:30
prafull-opensignlabsandGitHub 39806e3ad6 Merge pull request #489 from OpenSignLabs/feat_label
fix: checkbox not embeding in pdf
2024-03-14 21:25:18 +05:30
prafull-opensignlabs 7882c61b81 fix: checkbox not embeding in pdf 2024-03-14 21:22:48 +05:30
AndrewandGitHub b8f886902f Merge pull request #488 from OpenSignLabs/feat_label 2024-03-14 20:38:38 +05:30
prafull-opensignlabs 17660dae21 fix: border of radio button & checkbox is not proper 2024-03-14 20:12:41 +05:30
prafull-opensignlabs 7584667643 fix: when click on label in radio or checkbox widget it's border not visible 2024-03-14 18:35:40 +05:30
AndrewandGitHub b47d1cff07 Merge pull request #487 from OpenSignLabs/feat_label 2024-03-14 17:00:22 +05:30
prafull-opensignlabs b99d8e6d6e feat: add show/hide label in checkbox and radio widget 2024-03-14 16:50:46 +05:30
prafull-opensignlabsandGitHub 8b15bceac8 Merge pull request #472 from OpenSignLabs/signature_setting
fix: already placed signature widget setting icon issue with old data
2024-03-14 10:04:24 +05:30
RaktimaNXG 86b0e90948 fix: date z-index and default data of email,name,company name, job title widgets in guest login flow 2024-03-13 20:25:18 +05:30
prafull-opensignlabsandGitHub 3bc57a999c Merge pull request #486 from OpenSignLabs/fix_formreset
fix: reset button not working in forms
2024-03-13 20:18:33 +05:30
prafull-opensignlabs 27730c89e8 fix: signers not get reset in request signature form on reset button 2024-03-13 20:17:22 +05:30
prafull-opensignlabs 82eeffa6e4 fix: reset button not working in forms 2024-03-13 18:36:24 +05:30
RaktimaNXG 53147caebc fix: checkbox checked issue in signyour-self flow 2024-03-13 17:47:31 +05:30
RaktimaNXG 6004522d5c feat: change radio widgets name radio to radio button 2024-03-13 16:36:03 +05:30
AndrewandGitHub 266c2463c9 Merge pull request #485 from OpenSignLabs/update_tour 2024-03-13 15:19:55 +05:30
prafull-opensignlabs 1b215f53e7 feat: add help for select folder input 2024-03-13 15:17:04 +05:30
RaktimaNXG 73b2ba6abd fix: default date issue and remove commented code, unnecessary using useEffect 2024-03-13 14:30:25 +05:30
RaktimaNXG c04fd80a38 feat: change label widget name to text and text widget name to text input 2024-03-13 11:40:17 +05:30
prafull-opensignlabs 61b123cb61 refator: update dashboard tour messages 2024-03-13 10:31:49 +05:30
RaktimaNXG 08e805569f fix: disable date change in placeholder and template flow 2024-03-13 09:56:59 +05:30
RaktimaNXG 375aa0b7e7 fix: invalid date issue for indian format 2024-03-12 20:02:11 +05:30
RaktimaNXG b94e0fa052 refactor: change order of minimum & maximum input in checkbox setting 2024-03-12 16:50:50 +05:30
prafull-opensignlabsandGitHub b6752549ed Merge pull request #484 from OpenSignLabs/fix_e_sign
fix: E-sign is not available in completed document
2024-03-11 17:42:40 +05:30
prafull-opensignlabs 51505af6d8 fix: E-sign is not available in completed document 2024-03-11 17:35:20 +05:30
RaktimaNXG 0e5cffa1fa fix: widgets issue after placing on multiple pages 2024-03-11 16:59:10 +05:30
RaktimaNXG 885b5287b4 fix: condition of render setting icon for widgets 2024-03-08 18:39:02 +05:30
RaktimaNXG 5348c6620a fix: signature widget setting icon for placed with old data 2024-03-08 17:51:00 +05:30
41 changed files with 2260 additions and 745 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database. # A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
MASTER_KEY=XnAadwKxxByMr MASTER_KEY=XnAadwKxxByMr
# Mongodb URI to connect to # Mongodb URI to connect to
MONGODB_URI=mongodb://host.docker.internal:27017/OpenSignDB MONGODB_URI=mongodb://localhost:27017/OpenSignDB
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions. # Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
PARSE_MOUNT=/app PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead) # Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
+3
View File
@@ -14,6 +14,9 @@ RUN npm install
COPY apps/OpenSign/ . COPY apps/OpenSign/ .
COPY apps/OpenSign/.husky . COPY apps/OpenSign/.husky .
# build
RUN npm run build
# Make port 3000 available to the world outside this container # Make port 3000 available to the world outside this container
EXPOSE 3000 EXPOSE 3000
+14
View File
@@ -43,6 +43,7 @@
"react-scrollbars-custom": "^4.1.1", "react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0", "react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6", "react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3",
"reactour": "^1.19.2", "reactour": "^1.19.2",
"redux": "^4.2.1", "redux": "^4.2.1",
"redux-thunk": "^2.4.2", "redux-thunk": "^2.4.2",
@@ -20145,6 +20146,19 @@
} }
} }
}, },
"node_modules/react-tooltip": {
"version": "5.26.3",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.26.3.tgz",
"integrity": "sha512-MpYAws8CEHUd/RC4GaDCdoceph/T4KHM5vS5Dbk8FOmLMvvIht2ymP2htWdrke7K6lqPO8rz8+bnwWUIXeDlzg==",
"dependencies": {
"@floating-ui/dom": "^1.6.1",
"classnames": "^2.3.0"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
}
},
"node_modules/react-transition-group": { "node_modules/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+2 -1
View File
@@ -38,6 +38,7 @@
"react-scrollbars-custom": "^4.1.1", "react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0", "react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6", "react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3",
"reactour": "^1.19.2", "reactour": "^1.19.2",
"redux": "^4.2.1", "redux": "^4.2.1",
"redux-thunk": "^2.4.2", "redux-thunk": "^2.4.2",
@@ -48,7 +49,7 @@
}, },
"scripts": { "scripts": {
"start": "serve -s build", "start": "serve -s build",
"start-dev" :"react-scripts start", "start-dev": "react-scripts start",
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt", "version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"", "version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
"build": "npm run version && react-scripts build", "build": "npm run version && react-scripts build",
@@ -0,0 +1,47 @@
import React from "react";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../../constant/Utils";
const DashboardButton = (props) => {
const navigate = useNavigate();
function openReport() {
if (props.Data && props.Data.Redirect_type) {
const Redirect_type = props.Data.Redirect_type;
const id = props.Data.Redirect_id;
if (Redirect_type === "Form") {
navigate(`/form/${id}`);
} else if (Redirect_type === "Report") {
navigate(`/report/${id}`);
} else if (Redirect_type === "Url") {
openInNewTab(id);
}
}
}
return (
<div
onClick={() => openReport()}
className={`${
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full px-3 py-2 flex text-white rounded-md shadow overflow-hidden`}
>
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="">
<div className="text-base lg:text-lg text-black"> {props.Label}</div>
</div>
</div>
</div>
);
};
export default DashboardButton;
@@ -1,14 +1,51 @@
import React, { Suspense, lazy } from "react"; import React, { Suspense, lazy } from "react";
const DashboardButton = lazy(() => import("./DashboardButton"));
const DashboardCard = lazy(() => import("./DashboardCard")); const DashboardCard = lazy(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport")); const DashboardReport = lazy(() => import("./DashboardReport"));
const buttonList = [
{
label: "Sign yourself",
redirectId: "sHAnZphf69",
redirectType: "Form"
},
{
label: "Request signature",
redirectId: "8mZzFxbG1z",
redirectType: "Form"
}
];
const GetDashboard = (props) => { const GetDashboard = (props) => {
const Button = ({ label, redirectId, redirectType }) => (
<div className={"bg-white rounded-md shadow w-full"}>
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<DashboardButton
Icon={"fa-solid fa-plus"}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
</Suspense>
</div>
);
const renderSwitchWithTour = (col) => { const renderSwitchWithTour = (col) => {
switch (col.widget.type) { switch (col.widget.type) {
case "Card": case "Card":
return ( return (
<div <div
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"} className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
data-tut={col.widget.data.tourSection} data-tut={col.widget.data.tourSection}
style={{ background: col.widget.bgColor }} style={{ background: col.widget.bgColor }}
> >
@@ -42,7 +79,7 @@ const GetDashboard = (props) => {
return ( return (
<div data-tut={col.widget.data.tourSection}> <div data-tut={col.widget.data.tourSection}>
<Suspense fallback={<div>please wait</div>}> <Suspense fallback={<div>please wait</div>}>
<div className="mb-4 md:mb-0"> <div className="mb-3 md:mb-0">
<DashboardReport Record={col.widget} /> <DashboardReport Record={col.widget} />
</div> </div>
</Suspense> </Suspense>
@@ -58,7 +95,7 @@ const GetDashboard = (props) => {
case "Card": case "Card":
return ( return (
<div <div
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"} className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
style={{ background: col.widget.bgColor }} style={{ background: col.widget.bgColor }}
> >
<Suspense fallback={<div>please wait</div>}> <Suspense fallback={<div>please wait</div>}>
@@ -76,7 +113,7 @@ const GetDashboard = (props) => {
case "report": { case "report": {
return ( return (
<Suspense fallback={<div>please wait</div>}> <Suspense fallback={<div>please wait</div>}>
<div className="mb-4 md:mb-0"> <div className="mb-3 md:mb-0">
<DashboardReport Record={col.widget} /> <DashboardReport Record={col.widget} />
</div> </div>
</Suspense> </Suspense>
@@ -88,6 +125,21 @@ const GetDashboard = (props) => {
}; };
return ( return (
<div> <div>
<div className="mb-3">
<div
data-tut={"tourbutton"}
className="flex flex-col md:flex-row gap-6 md:gap-8"
>
{buttonList.map((btn) => (
<Button
key={btn.label}
label={btn.label}
redirectType={btn.redirectType}
redirectId={btn.redirectId}
/>
))}
</div>
</div>
{props.dashboard.map((val, key) => ( {props.dashboard.map((val, key) => (
<div key={"a" + key} className="row"> <div key={"a" + key} className="row">
{val.columns.map((col, i) => {val.columns.map((col, i) =>
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { themeColor } from "../../constant/const"; import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
function DropdownWidgetOption(props) { function DropdownWidgetOption(props) {
const [dropdownOptionList, setDropdownOptionList] = useState([ const [dropdownOptionList, setDropdownOptionList] = useState([
"option-1", "option-1",
@@ -10,6 +11,7 @@ function DropdownWidgetOption(props) {
const [maxCount, setMaxCount] = useState(0); const [maxCount, setMaxCount] = useState(0);
const [dropdownName, setDropdownName] = useState(props.type); const [dropdownName, setDropdownName] = useState(props.type);
const [isReadOnly, setIsReadOnly] = useState(false); const [isReadOnly, setIsReadOnly] = useState(false);
const [isHideLabel, setIsHideLabel] = useState(false);
const [status, setStatus] = useState("required"); const [status, setStatus] = useState("required");
const [defaultValue, setDefaultValue] = useState(""); const [defaultValue, setDefaultValue] = useState("");
const statusArr = ["required", "optional"]; const statusArr = ["required", "optional"];
@@ -19,6 +21,7 @@ function DropdownWidgetOption(props) {
setDropdownOptionList(["option-1", "option-2"]); setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type); setDropdownName(props.type);
setIsReadOnly(false); setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0); setMinCount(0);
setMaxCount(0); setMaxCount(0);
setDefaultCheckbox([]); setDefaultCheckbox([]);
@@ -39,6 +42,7 @@ function DropdownWidgetOption(props) {
props.currWidgetsDetails?.options?.validation?.maxRequiredCount props.currWidgetsDetails?.options?.validation?.maxRequiredCount
); );
setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly); setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly);
setIsHideLabel(props.currWidgetsDetails?.options?.isHideLabel);
setStatus(props.currWidgetsDetails?.options?.status || "required"); setStatus(props.currWidgetsDetails?.options?.status || "required");
setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || ""); setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || "");
setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []); setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []);
@@ -106,13 +110,15 @@ function DropdownWidgetOption(props) {
null, null,
null, null,
status, status,
defaultData defaultData,
isHideLabel
); );
// props.setShowDropdown(false); // props.setShowDropdown(false);
setDropdownOptionList(["option-1", "option-2"]); setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type); setDropdownName(props.type);
// props.setCurrWidgetsDetails({}); // props.setCurrWidgetsDetails({});
setIsReadOnly(false); setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0); setMinCount(0);
setMaxCount(0); setMaxCount(0);
setDefaultCheckbox([]); setDefaultCheckbox([]);
@@ -143,7 +149,7 @@ function DropdownWidgetOption(props) {
// styleClass={"dropdownModal"} // styleClass={"dropdownModal"}
isOpen={props.showDropdown} isOpen={props.showDropdown}
title={props.title} title={props.title}
closeOff={true} showClose={false}
> >
<div style={{ height: "100%", padding: 20 }}> <div style={{ height: "100%", padding: 20 }}>
<form <form
@@ -153,20 +159,6 @@ function DropdownWidgetOption(props) {
}} }}
> >
<div className="dropdownContainer"> <div className="dropdownContainer">
{["checkbox", "radio"].includes(props.type) &&
!props.isSignYourself && (
<div>
<input
type="checkbox"
checked={isReadOnly}
onChange={(e) => {
setIsReadOnly(e.target.checked);
}}
/>
<label style={{ marginLeft: "10px" }}>Is read only</label>
</div>
)}
<label style={{ fontSize: "13px", fontWeight: "600" }}> <label style={{ fontSize: "13px", fontWeight: "600" }}>
Name<span style={{ color: "red", fontSize: 13 }}> *</span> Name<span style={{ color: "red", fontSize: 13 }}> *</span>
</label> </label>
@@ -178,36 +170,6 @@ function DropdownWidgetOption(props) {
className="drodown-input" className="drodown-input"
/> />
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Minimun check
</label>
<input
required
defaultValue={0}
value={minCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className="drodown-input"
/>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className="drodown-input"
/>
</>
)}
<label <label
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }} style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
> >
@@ -274,7 +236,6 @@ function DropdownWidgetOption(props) {
></i> ></i>
</div> </div>
))} ))}
<i <i
onClick={handleAddInput} onClick={handleAddInput}
style={{ style={{
@@ -285,8 +246,38 @@ function DropdownWidgetOption(props) {
}} }}
className="fa-solid fa-square-plus" className="fa-solid fa-square-plus"
></i> ></i>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Minimun check
</label>
<input
required
defaultValue={0}
value={minCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className="drodown-input"
/>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className="drodown-input"
/>
</>
)}
</div> </div>
{["dropdown", "radio"].includes(props.type) && ( {["dropdown", radioButtonWidget].includes(props.type) && (
<> <>
<label <label
style={{ style={{
@@ -321,7 +312,7 @@ function DropdownWidgetOption(props) {
</select> </select>
</> </>
)} )}
{props.type !== "checkbox" && props.type !== "radio" && ( {props.type !== "checkbox" && props.type !== radioButtonWidget && (
<> <>
<div <div
style={{ style={{
@@ -358,8 +349,40 @@ function DropdownWidgetOption(props) {
</div> </div>
</> </>
)} )}
</div> {["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 mt-2 items-center text-center">
{!props.isSignYourself && (
<div>
<input
id="isreadonly"
type="checkbox"
checked={isReadOnly}
onChange={(e) => {
setIsReadOnly(e.target.checked);
}}
/>
<label className="ml-1" htmlFor="isreadonly">
Is read only
</label>
</div>
)}
<div>
<input
id="ishidelabel"
type="checkbox"
checked={isHideLabel}
onChange={(e) => {
setIsHideLabel(e.target.checked);
}}
/>
<label className="ml-1" htmlFor="ishidelabel">
Hide labels
</label>
</div>
</div>
)}
</div>
<div <div
style={{ style={{
height: "1px", height: "1px",
+267 -192
View File
@@ -2,17 +2,122 @@ import React, { useState, useEffect } from "react";
import BorderResize from "./BorderResize"; import BorderResize from "./BorderResize";
import PlaceholderBorder from "./PlaceholderBorder"; import PlaceholderBorder from "./PlaceholderBorder";
import { Rnd } from "react-rnd"; import { Rnd } from "react-rnd";
import { defaultWidthHeight, isMobile } from "../../constant/Utils"; import {
defaultWidthHeight,
isMobile,
onChangeInput,
radioButtonWidget,
textInputWidget,
textWidget
} from "../../constant/Utils";
import PlaceholderType from "./PlaceholderType"; import PlaceholderType from "./PlaceholderType";
import moment from "moment"; import moment from "moment";
import "../../styles/opensigndrive.css"; import "../../styles/opensigndrive.css";
const selectFormat = (data) => {
switch (data) {
case "L":
return "MM/dd/yyyy";
case "DD-MM-YYYY":
return "dd-MM-yyyy";
case "DD/MM/YYYY":
return "dd/MM/yyyy";
case "LL":
return "MMMM dd, yyyy";
case "DD MMM, YYYY":
return "dd MMM, yyyy";
case "YYYY-MM-DD":
return "yyyy-MM-dd";
case "MM-DD-YYYY":
return "MM-dd-yyyy";
case "MM.DD.YYYY":
return "MM.dd.yyyy";
case "MMM DD, YYYY":
return "MMM dd, yyyy";
case "DD MMMM, YYYY":
return "dd MMMM, yyyy";
default:
return "MM/dd/yyyy";
}
};
const changeDateToMomentFormat = (format) => {
switch (format) {
case "MM/dd/yyyy":
return "L";
case "dd-MM-yyyy":
return "DD-MM-YYYY";
case "dd/MM/yyyy":
return "DD/MM/YYYY";
case "MMMM dd, yyyy":
return "LL";
case "dd MMM, yyyy":
return "DD MMM, YYYY";
case "yyyy-MM-dd":
return "YYYY-MM-DD";
case "MM-dd-yyyy":
return "MM-DD-YYYY";
case "MM.dd.yyyy":
return "MM.DD.YYYY";
case "MMM dd, yyyy":
return "MMM DD, YYYY";
case "dd MMMM, yyyy":
return "DD MMMM, YYYY";
default:
return "L";
}
};
//function to get default date
const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
let date;
if (format && format === "dd-MM-yyyy") {
const newdate = selectedDate
? selectedDate
: moment(new Date()).format(changeDateToMomentFormat(format));
const [day, month, year] = newdate.split("-");
date = new Date(`${year}-${month}-${day}`);
} else {
date = new Date(selectedDate);
}
const value = date;
return value;
};
//function to get default format
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
function Placeholder(props) { function Placeholder(props) {
const [isDraggingEnabled, setDraggingEnabled] = useState(true); const [isDraggingEnabled, setDraggingEnabled] = useState(true);
const [isShowDateFormat, setIsShowDateFormat] = useState(false); const [isShowDateFormat, setIsShowDateFormat] = useState(false);
const [selectDate, setSelectDate] = useState(); const [selectDate, setSelectDate] = useState({
date:
props.pos.type === "date"
? moment(
getDefaultdate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
).getTime()
).format(
changeDateToMomentFormat(props.pos?.options?.validation?.format)
)
: "",
format:
props.pos.type === "date"
? getDefaultFormat(props.pos?.options?.validation?.format)
: ""
});
const [dateFormat, setDateFormat] = useState([]); const [dateFormat, setDateFormat] = useState([]);
const [saveDateFormat, setSaveDateFormat] = useState(""); const [startDate, setStartDate] = useState(
props.pos.type === "date" &&
getDefaultdate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
)
);
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
width: null,
height: null
});
const dateFormatArr = [ const dateFormatArr = [
"L", "L",
"DD-MM-YYYY", "DD-MM-YYYY",
@@ -25,70 +130,41 @@ function Placeholder(props) {
"DD MMMM, YYYY" "DD MMMM, YYYY"
]; ];
const selectFormat = (data) => { useEffect(() => {
switch (data) { const updateWidth = () => {
case "L": const rndElement = document.getElementById(props.pos.key);
return "MM/dd/yyyy"; if (rndElement) {
case "DD-MM-YYYY": const { width, height } = rndElement.getBoundingClientRect();
return "dd-MM-yyyy"; setGetCheckboxRenderWidth({ width: width, height: height });
case "DD/MM/YYYY": }
return "dd/MM/yyyy"; };
case "LL":
return "MMMM dd, yyyy"; // Delay to ensure rendering is complete
case "DD MMM, YYYY": const timer = setTimeout(updateWidth, 0);
return "dd MMM, YYYY";
case "YYYY-MM-DD": return () => clearTimeout(timer);
return "YYYY-MM-dd"; }, [props.pos]);
case "MM-DD-YYYY":
return "MM-dd-YYYY";
case "MM.DD.YYYY":
return "MM.dd.YYYY";
case "MMM DD, YYYY":
return "MMM dd, YYYY";
case "DD MMMM, YYYY":
return "dd MMMM, YYYY";
default:
return "dd/MM/yyyy";
}
};
useEffect(() => { useEffect(() => {
//set default current date and default format MM/dd/yyyy const closeMenuOnOutsideClick = (e) => {
if (props.isSignYourself) { if (!isDraggingEnabled && !e.target.closest("#changeIsDragging")) {
const date = new Date(); setDraggingEnabled(true);
const milliseconds = date.getTime(); }
const newDate = moment(milliseconds).format("MM/DD/YYYY"); };
const dateObj = {
date: newDate,
format: "MM/dd/YYYY"
};
setSelectDate(dateObj);
} else {
const defaultRes = props?.pos?.options?.response;
const defaultFormat = props.pos?.options?.validation?.format;
const updateDate = defaultRes
? new Date(props?.pos?.options?.response)
: new Date();
const dateFormat = defaultFormat ? defaultFormat : "MM/DD/YYYY";
const milliseconds = updateDate.getTime();
const newDate = moment(milliseconds).format(dateFormat);
const dateObj = {
date: newDate,
format: props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format
: "MM/dd/YYYY"
};
setSelectDate(dateObj);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function for add selected date and format in selectFormat document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isDraggingEnabled]);
//function change format array list with selected date and format
const changeDateFormat = () => { const changeDateFormat = () => {
const updateDate = []; const updateDate = [];
dateFormatArr.map((data) => { dateFormatArr.map((data) => {
let date; let date;
if (selectDate.format === "dd-MM-yyyy") { if (selectDate && selectDate.format === "dd-MM-yyyy") {
const [day, month, year] = selectDate.date.split("-"); const [day, month, year] = selectDate.date.split("-");
date = new Date(`${year}-${month}-${day}`); date = new Date(`${year}-${month}-${day}`);
} else { } else {
@@ -104,13 +180,15 @@ function Placeholder(props) {
}); });
setDateFormat(updateDate); setDateFormat(updateDate);
}; };
useEffect(() => { useEffect(() => {
if (props.isPlaceholder || props.isSignYourself) { if (props.isPlaceholder || props.isSignYourself) {
selectDate && changeDateFormat(); selectDate && changeDateFormat();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectDate]); }, [selectDate]);
//handle to close drop down menu onclick screen
//it detect outside click of date dropdown menu
useEffect(() => { useEffect(() => {
const closeMenuOnOutsideClick = (e) => { const closeMenuOnOutsideClick = (e) => {
if (isShowDateFormat && !e.target.closest("#menu-container")) { if (isShowDateFormat && !e.target.closest("#menu-container")) {
@@ -124,20 +202,21 @@ function Placeholder(props) {
}; };
}, [isShowDateFormat]); }, [isShowDateFormat]);
//onclick placeholder function to open signature pad //`handleWidgetIdandPopup` is used to set current widget id and open relative popup
const handlePlaceholderClick = () => { const handleWidgetIdandPopup = () => {
if (props.setSelectWidgetId) { if (props.setSelectWidgetId) {
props.setSelectWidgetId(props.pos.key); props.setSelectWidgetId(props.pos.key);
} }
const widgetTypeExist = [ const widgetTypeExist = [
"text", textInputWidget,
"checkbox", "checkbox",
"name", "name",
"company", "company",
"job title", "job title",
"date", "date",
"email" "email",
textWidget
].includes(props.pos.type); ].includes(props.pos.type);
if (widgetTypeExist) { if (widgetTypeExist) {
@@ -167,7 +246,7 @@ function Placeholder(props) {
} else if ( } else if (
props.isPlaceholder && props.isPlaceholder &&
!props.isDragging && !props.isDragging &&
props.pos.type !== "label" props.pos.type !== textWidget
) { ) {
if (props.pos.key === props.selectWidgetId) { if (props.pos.key === props.selectWidgetId) {
props.handleLinkUser(props.data.Id); props.handleLinkUser(props.data.Id);
@@ -194,9 +273,19 @@ function Placeholder(props) {
} }
} }
}; };
const handleOnClickPlaceholder = () => {
const handleWidgetsOnclick = () => { if (!props.isNeedSign) {
if (props.pos.type === "radio") { props.setWidgetType(props.pos.type);
}
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
handleWidgetIdandPopup();
} else if (props.isPlaceholder || props.isSignYourself) {
handleWidgetIdandPopup();
}
};
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
const handleOnClickSettingIcon = () => {
if (props.pos.type === radioButtonWidget) {
props.setIsRadio(true); props.setIsRadio(true);
} else if (props.pos.type === "dropdown") { } else if (props.pos.type === "dropdown") {
props?.setShowDropdown(true); props?.setShowDropdown(true);
@@ -206,13 +295,57 @@ function Placeholder(props) {
props?.handleNameModal(true); props?.handleNameModal(true);
} }
if (props.isPlaceholder) { if (props.isPlaceholder && props.type !== textWidget) {
props.setUniqueId(props.data.Id); props.setUniqueId(props.data.Id);
} }
props.setSignKey(props.pos.key); props.setSignKey(props.pos.key);
props.setWidgetType(props.pos.type); props.setWidgetType(props.pos.type);
props.setCurrWidgetsDetails(props.pos); props.setCurrWidgetsDetails(props.pos);
}; };
//function ro set state value of onclick on widget's copy icon
const handleCopyPlaceholder = (e) => {
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
} else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId);
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
}
e.stopPropagation();
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
};
//function to save date and format on local array onchange date and onclick format
const handleSaveDate = (data, isDateChange) => {
let updateDate = data.date;
//check if date change by user
if (isDateChange) {
//`changeDateToMomentFormat` is used to convert date as per required to moment package
updateDate = moment(data.date).format(
changeDateToMomentFormat(data.format)
);
}
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
//`getDefaultdate` is used to convert update date in new Date() format
const date = moment(
getDefaultdate(updateDate, data?.format).getTime()
).format(changeDateToMomentFormat(data?.format));
//`onChangeInput` is used to save data related to date in a placeholder field
onChangeInput(
date,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data.Id,
false,
data?.format
);
setSelectDate({ date: date, format: data?.format });
};
const PlaceholderIcon = () => { const PlaceholderIcon = () => {
return ( return (
props.isShowBorder && ( props.isShowBorder && (
@@ -223,48 +356,42 @@ function Placeholder(props) {
<i <i
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleWidgetsOnclick(); handleOnClickSettingIcon();
}} }}
onTouchEnd={(e) => { onTouchEnd={(e) => {
e.stopPropagation(); e.stopPropagation();
handleWidgetsOnclick(); handleOnClickSettingIcon();
}} }}
className="fa-solid fa-gear settingIcon" className="fa-solid fa-gear settingIcon"
style={{ style={{
color: "#188ae2", color: "#188ae2",
right: "9px", right: "29px",
top: "-28px" top: "-19px"
}} }}
></i> ></i>
) : ( ) : (
props.pos.type !== "date" && ((!props?.pos?.type && props.pos.isStamp) ||
props.pos.type !== "label" && (props?.pos?.type &&
props.pos.type !== "signature" && !["date", textWidget, "signature"].includes(
!props.isSignYourself && ( props.pos.type
) &&
!props.isSignYourself)) && (
<i <i
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleWidgetsOnclick(); handleOnClickSettingIcon();
}} }}
onTouchEnd={(e) => { onTouchEnd={(e) => {
e.stopPropagation(); e.stopPropagation();
handleWidgetsOnclick(); handleOnClickSettingIcon();
}} }}
className="fa-solid fa-gear settingIcon" className="fa-solid fa-gear settingIcon"
style={{ style={{ color: "#188ae2", right: "47px", top: "-19px" }}
color: "#188ae2",
right: ["checkbox", "radio"].includes(props.pos.type)
? "24px"
: "47px",
top: ["checkbox", "radio"].includes(props.pos.type)
? "-28px"
: "-19px"
}}
></i> ></i>
) )
)} )}
{props.pos.type !== "label" && !props.isSignYourself && ( {props.pos.type !== textWidget && !props.isSignYourself && (
<i <i
data-tut="reactourLinkUser" data-tut="reactourLinkUser"
className="fa-regular fa-user signUserIcon" className="fa-regular fa-user signUserIcon"
@@ -278,19 +405,7 @@ function Placeholder(props) {
props.handleLinkUser(props.data.Id); props.handleLinkUser(props.data.Id);
props.setUniqueId(props.data.Id); props.setUniqueId(props.data.Id);
}} }}
style={{ style={{ color: "#188ae2", right: "32px", top: "-18px" }}
color: "#188ae2",
right:
props.pos.type === "checkbox" ||
props.pos.type === "radio"
? "8px"
: "32px",
top:
props.pos.type === "checkbox" ||
props.pos.type === "radio"
? "-28px"
: "-18px"
}}
></i> ></i>
)} )}
</> </>
@@ -347,10 +462,12 @@ function Placeholder(props) {
e.stopPropagation(); e.stopPropagation();
setIsShowDateFormat(!isShowDateFormat); setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data); setSelectDate(data);
handleSaveDate(data);
}} }}
onClick={() => { onClick={() => {
setIsShowDateFormat(!isShowDateFormat); setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data); setSelectDate(data);
handleSaveDate(data);
}} }}
className="dropdown-item itemColor" className="dropdown-item itemColor"
style={{ fontSize: "12px" }} style={{ fontSize: "12px" }}
@@ -364,35 +481,9 @@ function Placeholder(props) {
)} )}
<i <i
className="fa-regular fa-copy signCopy" className="fa-regular fa-copy signCopy"
onClick={(e) => { onClick={(e) => handleCopyPlaceholder(e)}
if (props.data) { onTouchEnd={(e) => handleCopyPlaceholder(e)}
props.setSignerObjId(props.data.signerObjId); style={{ color: "#188ae2", right: "12px", top: "-18px" }}
props.setUniqueId(props.data.Id);
}
e.stopPropagation();
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
}}
onTouchEnd={(e) => {
if (props.data) {
props.setSignerObjId(props.data.signerObjId);
props.setUniqueId(props.data.Id);
}
e.stopPropagation();
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
}}
style={{
color: "#188ae2",
right:
props.pos.type === "checkbox" || props.pos.type === "radio"
? "-9px"
: "12px",
top:
props.pos.type === "checkbox" || props.pos.type === "radio"
? "-28px"
: "-18px"
}}
></i> ></i>
<i <i
className="fa-regular fa-circle-xmark signCloseBtn" className="fa-regular fa-circle-xmark signCloseBtn"
@@ -415,17 +506,7 @@ function Placeholder(props) {
props.setIsStamp(false); props.setIsStamp(false);
} }
}} }}
style={{ style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
color: "#188ae2",
right:
props.pos.type === "checkbox" || props.pos.type === "radio"
? "-27px"
: "-8px",
top:
props.pos.type === "checkbox" || props.pos.type === "radio"
? "-28px"
: "-18px"
}}
></i> ></i>
</> </>
) )
@@ -434,11 +515,11 @@ function Placeholder(props) {
return ( return (
<Rnd <Rnd
id={props.pos.key}
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""} data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
//ref={nodeRef}
key={props.pos.key} key={props.pos.key}
lockAspectRatio={ lockAspectRatio={
props.pos.type !== "label" && props.pos.type !== textWidget &&
(props.pos.Width (props.pos.Width
? props.pos.Width / props.pos.Height ? props.pos.Width / props.pos.Height
: defaultWidthHeight(props.pos.type).width / : defaultWidthHeight(props.pos.type).width /
@@ -454,10 +535,10 @@ function Placeholder(props) {
props.data && props.isNeedSign props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId && ? props.data?.signerObjId === props.signerObjId &&
props.pos.type !== "checkbox" && props.pos.type !== "checkbox" &&
props.pos.type !== "radio" props.pos.type !== radioButtonWidget
? true ? true
: false : false
: props.pos.type !== "radio" && : props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" && props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId && props.pos.key === props.selectWidgetId &&
true, true,
@@ -467,8 +548,8 @@ function Placeholder(props) {
bounds="parent" bounds="parent"
className="signYourselfBlock" className="signYourselfBlock"
style={{ style={{
border: props.pos.type !== "checkbox" && "1px solid #007bff", border: "1px solid #007bff",
borderRadius: props.pos.type !== "checkbox" && "2px", borderRadius: "2px",
textAlign: textAlign:
props.pos.type !== "name" && props.pos.type !== "name" &&
props.pos.type !== "company" && props.pos.type !== "company" &&
@@ -482,21 +563,27 @@ function Placeholder(props) {
: "all-scroll", : "all-scroll",
zIndex: zIndex:
props.pos.type === "date" props.pos.type === "date"
? "99" ? props.pos.key === props.selectWidgetId
? 99 + 1
: 99
: props?.pos?.zIndex : props?.pos?.zIndex
? props.pos.zIndex ? props.pos.zIndex
: "5", : "5",
background: props.data background: props.data ? props.data.blockColor : "rgb(203 233 237)"
? props.data.blockColor
: props.pos.type !== "checkbox" && "rgb(203 233 237)"
}} }}
onDrag={() => { onDrag={() => {
setDraggingEnabled(true); setDraggingEnabled(true);
props.handleTabDrag && props.handleTabDrag(props.pos.key); props.handleTabDrag && props.handleTabDrag(props.pos.key);
}} }}
size={{ size={{
width: props.posWidth(props.pos, props.isSignYourself), width:
height: props.posHeight(props.pos, props.isSignYourself) props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself)
}} }}
onResizeStart={() => { onResizeStart={() => {
setDraggingEnabled(true); setDraggingEnabled(true);
@@ -508,7 +595,7 @@ function Placeholder(props) {
disableDragging={ disableDragging={
props.isNeedSign props.isNeedSign
? true ? true
: props.isPlaceholder && props.pos.type !== "date" : props.isPlaceholder && ![textWidget].includes(props.pos.type)
? false ? false
: !isDraggingEnabled : !isDraggingEnabled
} }
@@ -532,41 +619,23 @@ function Placeholder(props) {
false false
); );
}} }}
onClick={() => { onClick={() => handleOnClickPlaceholder()}
!props.isNeedSign && props.setWidgetType(props.pos.type);
props.isNeedSign && props.data?.signerObjId === props.signerObjId
? handlePlaceholderClick()
: props.isPlaceholder
? handlePlaceholderClick()
: props.isSignYourself && handlePlaceholderClick();
}}
> >
{props.isShowBorder && {props.isShowBorder &&
props.pos.type !== "radio" && props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" && props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId ? ( props.pos.key === props.selectWidgetId ? (
<BorderResize <BorderResize right={-12} top={-11} />
right={
props.pos.type === "checkbox" || props.pos.type === "radio"
? -21
: -12
}
top={
props.pos.type === "checkbox" || props.pos.type === "radio"
? -21
: -11
}
/>
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? ( ) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
props.data?.signerObjId === props.signerObjId && props.data?.signerObjId === props.signerObjId &&
props.pos.type !== "radio" && props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" ? ( props.pos.type !== "checkbox" ? (
<BorderResize /> <BorderResize />
) : ( ) : (
<></> <></>
) )
) : ( ) : (
props.pos.type !== "radio" && props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" && props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId && <BorderResize /> props.pos.key === props.selectWidgetId && <BorderResize />
)} )}
@@ -576,6 +645,7 @@ function Placeholder(props) {
setDraggingEnabled={setDraggingEnabled} setDraggingEnabled={setDraggingEnabled}
pos={props.pos} pos={props.pos}
isPlaceholder={props.isPlaceholder} isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
/> />
)} )}
{isMobile ? ( {isMobile ? (
@@ -583,18 +653,21 @@ function Placeholder(props) {
style={{ style={{
left: props.xPos(props.pos, props.isSignYourself), left: props.xPos(props.pos, props.isSignYourself),
top: props.yPos(props.pos, props.isSignYourself), top: props.yPos(props.pos, props.isSignYourself),
width: props.posWidth(props.pos, props.isSignYourself), width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
// "auto", //props.posWidth(props.pos, props.isSignYourself),
// height: props.posHeight(props.pos, props.isSignYourself), // height: props.posHeight(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10" zIndex: "10"
}} }}
onTouchEnd={() => { onTouchEnd={() => handleOnClickPlaceholder()}
!props.isNeedSign && props.setWidgetType(props.pos.type);
props.isNeedSign && props.data?.signerObjId === props.signerObjId
? handlePlaceholderClick()
: props.isPlaceholder
? handlePlaceholderClick()
: props.isSignYourself && handlePlaceholderClick();
}}
> >
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />} {props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
@@ -615,9 +688,10 @@ function Placeholder(props) {
isNeedSign={props.isNeedSign} isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate} setSelectDate={setSelectDate}
selectDate={selectDate} selectDate={selectDate}
setSaveDateFormat={setSaveDateFormat}
saveDateFormat={saveDateFormat}
setValidateAlert={props.setValidateAlert} setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
/> />
</div> </div>
) : ( ) : (
@@ -640,9 +714,10 @@ function Placeholder(props) {
isNeedSign={props.isNeedSign} isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate} setSelectDate={setSelectDate}
selectDate={selectDate} selectDate={selectDate}
setSaveDateFormat={setSaveDateFormat}
saveDateFormat={saveDateFormat}
setValidateAlert={props.setValidateAlert} setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
/> />
</> </>
)} )}
@@ -2,30 +2,46 @@ import React from "react";
import { themeColor } from "../../constant/const"; import { themeColor } from "../../constant/const";
import { import {
defaultWidthHeight, defaultWidthHeight,
resizeBorderExtraWidth isMobile,
radioButtonWidget,
resizeBorderExtraWidth,
textWidget
} from "../../constant/Utils"; } from "../../constant/Utils";
function PlaceholderBorder(props) { function PlaceholderBorder(props) {
const getResizeBorderExtraWidth = const getResizeBorderExtraWidth = resizeBorderExtraWidth();
props.pos.type === "checkbox" || props.pos.type === "radio"
? 38
: resizeBorderExtraWidth();
const defaultWidth = defaultWidthHeight(props.pos.type).width; const defaultWidth = defaultWidthHeight(props.pos.type).width;
const defaultHeight = defaultWidthHeight(props.pos.type).height; const defaultHeight = defaultWidthHeight(props.pos.type).height;
const handleMinWidth = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
} else {
return props.pos.Width
? props.pos.Width + getResizeBorderExtraWidth
: defaultWidth + getResizeBorderExtraWidth;
}
};
const handleMinHeight = () => {
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
} else {
return props.pos.Height
? props.pos.Height + getResizeBorderExtraWidth
: defaultHeight + getResizeBorderExtraWidth;
}
};
return ( return (
<div <div
onMouseEnter={props?.setDraggingEnabled(true)} onMouseEnter={() => !isMobile && props?.setDraggingEnabled(true)}
onTouchEnd={() =>
props.pos.type === textWidget && props?.setDraggingEnabled(true)
}
className="borderResize" className="borderResize"
style={{ style={{
borderColor: themeColor, borderColor: themeColor,
borderStyle: "dashed", borderStyle: "dashed",
minWidth: handleMinWidth(),
width: props.pos.Width minHeight: handleMinHeight(),
? props.pos.Width + getResizeBorderExtraWidth
: defaultWidth + getResizeBorderExtraWidth,
height: props.pos.Height
? props.pos.Height + getResizeBorderExtraWidth
: defaultHeight + getResizeBorderExtraWidth,
borderWidth: "0.2px", borderWidth: "0.2px",
overflow: "hidden" overflow: "hidden"
}} }}
@@ -1,7 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { themeColor } from "../../constant/const"; import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { randomId } from "../../constant/Utils"; import { randomId, textWidget } from "../../constant/Utils";
function PlaceholderCopy(props) { function PlaceholderCopy(props) {
const copyType = ["All pages", "All pages but last", "All pages but first"]; const copyType = ["All pages", "All pages but last", "All pages but first"];
@@ -180,7 +180,13 @@ function PlaceholderCopy(props) {
copyPlaceholder("first"); copyPlaceholder("first");
} }
}; };
const handleUniqueId = () => {
if (props.widgetType === textWidget) {
props.setUniqueId(props?.tempSignerId);
props.setTempSignerId("");
}
props.setIsPageCopy(false);
};
return ( return (
<ModalUi <ModalUi
isOpen={props.isPageCopy} isOpen={props.isPageCopy}
@@ -220,7 +226,7 @@ function PlaceholderCopy(props) {
<button <button
onClick={() => { onClick={() => {
handleApplyCopy(); handleApplyCopy();
props.setIsPageCopy(false); handleUniqueId();
}} }}
style={{ background: themeColor }} style={{ background: themeColor }}
type="button" type="button"
@@ -232,7 +238,9 @@ function PlaceholderCopy(props) {
<button <button
type="button" type="button"
className="finishBtn cancelBtn" className="finishBtn cancelBtn"
onClick={() => props.setIsPageCopy(false)} onClick={() => {
handleUniqueId();
}}
> >
Cancel Cancel
</button> </button>
@@ -1,5 +1,14 @@
import React, { useEffect, useState, forwardRef, useRef } from "react"; import React, { useEffect, useState, forwardRef, useRef } from "react";
import { getMonth, getYear, onChangeInput, range } from "../../constant/Utils"; import {
getMonth,
getYear,
isMobile,
onChangeInput,
radioButtonWidget,
range,
textInputWidget,
textWidget
} from "../../constant/Utils";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css"; import "../../styles/signature.css";
@@ -7,10 +16,7 @@ import RegexParser from "regex-parser";
function PlaceholderType(props) { function PlaceholderType(props) {
const type = props?.pos?.type; const type = props?.pos?.type;
const [selectOption, setSelectOption] = useState( const [selectOption, setSelectOption] = useState("");
props.pos?.options?.defaultValue ? props.pos?.options?.defaultValue : ""
);
const [startDate, setStartDate] = useState(new Date());
const [validatePlaceholder, setValidatePlaceholder] = useState(""); const [validatePlaceholder, setValidatePlaceholder] = useState("");
const inputRef = useRef(null); const inputRef = useRef(null);
const [textValue, setTextValue] = useState(); const [textValue, setTextValue] = useState();
@@ -80,7 +86,7 @@ function PlaceholderType(props) {
case "number": case "number":
setValidatePlaceholder("12345"); setValidatePlaceholder("12345");
break; break;
case "text": case textInputWidget:
setValidatePlaceholder("enter text"); setValidatePlaceholder("enter text");
break; break;
default: default:
@@ -89,10 +95,7 @@ function PlaceholderType(props) {
} }
useEffect(() => { useEffect(() => {
if (props.isNeedSign && type === "date") { if (type && type === "checkbox" && props.isNeedSign) {
const updateDate = new Date();
setStartDate(updateDate);
} else if (type && type === "checkbox" && props.isNeedSign) {
const isDefaultValue = props.pos.options?.defaultValue; const isDefaultValue = props.pos.options?.defaultValue;
if (isDefaultValue) { if (isDefaultValue) {
setSelectedCheckbox(isDefaultValue); setSelectedCheckbox(isDefaultValue);
@@ -103,7 +106,18 @@ function PlaceholderType(props) {
checkRegularExpress(props.pos?.options?.validation?.type); checkRegularExpress(props.pos?.options?.validation?.type);
} }
setTextValue( setTextValue(
props.pos?.options?.defaultValue ? props.pos?.options?.defaultValue : "" props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
);
setSelectOption(
props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
); );
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@@ -120,54 +134,8 @@ function PlaceholderType(props) {
} }
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos?.options?.defaultValue]); }, [props.pos]);
useEffect(() => {
if (type && type === "date") {
if (props.selectDate) {
let updateDate;
if (props.selectDate.format === "dd-MM-yyyy") {
const [day, month, year] = props.saveDateFormat.split("-");
updateDate = new Date(`${year}-${month}-${day}`);
} else {
if (props?.saveDateFormat) {
updateDate = new Date(props.saveDateFormat);
}
}
// const updateDate = new Date(props.saveDateFormat);
setStartDate(updateDate);
const dateObj = {
date: props.saveDateFormat,
format: props.selectDate
? props.selectDate?.format
: props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format
: "MM/dd/YYYY"
};
props.setSelectDate(dateObj);
onChangeInput(
props.saveDateFormat,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data.Id,
false,
props.selectDate?.format
? props.selectDate.format
: props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format
: "MM/dd/YYYY"
);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.saveDateFormat]);
const dateValue = (value) => {
props.setSaveDateFormat(value);
return <span>{value}</span>;
};
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => ( const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div <div
className="inputPlaceholder" className="inputPlaceholder"
@@ -175,7 +143,7 @@ function PlaceholderType(props) {
onClick={onClick} onClick={onClick}
ref={ref} ref={ref}
> >
{dateValue(value)} {value}
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i> <i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
</div> </div>
)); ));
@@ -307,7 +275,16 @@ function PlaceholderType(props) {
isRadio isRadio
); );
}; };
//function to set onchange date
const handleOnDateChange = (date) => {
props.setStartDate(date);
const isDateChange = true;
const dateObj = {
date: date,
format: props.selectDate.format
};
props.handleSaveDate(dateObj, isDateChange);
};
switch (type) { switch (type) {
case "signature": case "signature":
return props.pos.SignUrl ? ( return props.pos.SignUrl ? (
@@ -356,51 +333,55 @@ function PlaceholderType(props) {
<div style={{ zIndex: props.isSignYourself && "99" }}> <div style={{ zIndex: props.isSignYourself && "99" }}>
{props.pos.options?.values?.map((data, ind) => { {props.pos.options?.values?.map((data, ind) => {
return ( return (
<input <div key={ind} className="flex items-center text-center gap-0.5">
key={ind} <input
style={{ style={{
width: props.pos.Width, width: props.pos.Width,
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
marginBottom: "6px", marginTop: ind === 0 ? 0 : "5px"
marginTop: "5px" }}
}} onBlur={handleInputBlur}
onBlur={handleInputBlur} disabled={
disabled={ props.isNeedSign &&
props.isNeedSign && (props.pos.options?.isReadOnly ||
(props.pos.options?.isReadOnly || props.data?.signerObjId !== props.signerObjId)
props.data?.signerObjId !== props.signerObjId) }
} type="checkbox"
type="checkbox" checked={selectCheckbox(ind)}
checked={selectCheckbox(ind)} onChange={(e) => {
onChange={(e) => { if (e.target.checked) {
if (e.target.checked) { if (!props.isPlaceholder) {
if (!props.isPlaceholder) { const maxRequired =
const maxRequired = props.pos.options?.validation?.maxRequiredCount;
props.pos.options?.validation?.maxRequiredCount; const maxCountInt =
const maxCountInt = maxRequired && parseInt(maxRequired); maxRequired && parseInt(maxRequired);
if (maxCountInt > 0) { if (maxCountInt > 0) {
if ( if (
selectedCheckbox && selectedCheckbox &&
selectedCheckbox?.length <= maxCountInt - 1 selectedCheckbox?.length <= maxCountInt - 1
) { ) {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind); handleCheckboxValue(e.target.checked, ind);
} }
} else {
handleCheckboxValue(e.target.checked, ind);
} }
} else {
handleCheckboxValue(e.target.checked, ind);
} }
} else { }}
handleCheckboxValue(e.target.checked, ind); />
} {!props.pos.options?.isHideLabel && (
}} <label className="text-xs mb-0 text-center">{data}</label>
/> )}
</div>
); );
})} })}
</div> </div>
); );
case "text": case textInputWidget:
return props.isSignYourself || return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? ( (props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input <input
@@ -639,21 +620,21 @@ function PlaceholderType(props) {
</div> </div>
)} )}
disabled={ disabled={
props.isNeedSign && props.data?.signerObjId !== props.signerObjId props.isPlaceholder ||
(props.isNeedSign &&
props.data?.signerObjId !== props.signerObjId)
} }
onBlur={handleInputBlur} onBlur={handleInputBlur}
closeOnScroll={true} closeOnScroll={true}
className="inputPlaceholder" className="inputPlaceholder"
style={{ outlineColor: "#007bff" }} style={{ outlineColor: "#007bff" }}
selected={ selected={
startDate props?.startDate
? startDate ? props?.startDate
: props.pos.options?.response && : props.pos.options?.response &&
new Date(props.pos.options.response) new Date(props.pos.options.response)
} }
onChange={(date) => { onChange={(date) => handleOnDateChange(date)}
setStartDate(date);
}}
popperPlacement="top-end" popperPlacement="top-end"
customInput={<ExampleCustomInput />} customInput={<ExampleCustomInput />}
dateFormat={ dateFormat={
@@ -661,7 +642,7 @@ function PlaceholderType(props) {
? props.selectDate?.format ? props.selectDate?.format
: props.pos?.options?.validation?.format : props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format ? props.pos?.options?.validation?.format
: "MM/dd/YYYY" : "MM/dd/yyyy"
} }
/> />
</div> </div>
@@ -721,43 +702,49 @@ function PlaceholderType(props) {
<span>{type}</span> <span>{type}</span>
</div> </div>
); );
case "radio": case radioButtonWidget:
return ( return (
<div> <div>
{props.pos.options?.values.map((data, ind) => { {props.pos.options?.values.map((data, ind) => {
return ( return (
<input <div key={ind} className="flex items-center text-center gap-0.5">
key={ind} <input
style={{ style={{
width: props.pos.Width, width: props.pos.Width,
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
marginBottom: "6px", marginTop: ind === 0 ? 0 : "5px"
marginTop: "5px" }}
}} type="radio"
type="radio" disabled={
disabled={ props.isNeedSign &&
props.isNeedSign && (props.pos.options?.isReadOnly ||
(props.pos.options?.isReadOnly || props.data?.signerObjId !== props.signerObjId)
props.data?.signerObjId !== props.signerObjId)
}
checked={handleRadioCheck(data)}
onChange={(e) => {
if (!props.isPlaceholder) {
handleCheckRadio(e.target.checked, data);
} }
}} checked={handleRadioCheck(data)}
/> onChange={(e) => {
if (!props.isPlaceholder) {
handleCheckRadio(e.target.checked, data);
}
}}
/>
{!props.pos.options?.isHideLabel && (
<label className="text-xs mb-0">{data}</label>
)}
</div>
); );
})} })}
</div> </div>
); );
case "label": case textWidget:
return ( return (
<textarea <textarea
placeholder="Enter label" placeholder="Enter label"
rows={1} rows={1}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => { onChange={(e) => {
setTextValue(e.target.value);
onChangeInput( onChangeInput(
e.target.value, e.target.value,
props.pos.key, props.pos.key,
@@ -768,7 +755,11 @@ function PlaceholderType(props) {
false false
); );
}} }}
className="labelTextArea" className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap" }} style={{ whiteSpace: "pre-wrap" }}
cols="50" cols="50"
/> />
@@ -56,7 +56,9 @@ function RenderPdf({
selectWidgetId, selectWidgetId,
unSignedWidgetId, unSignedWidgetId,
setIsCheckbox, setIsCheckbox,
handleNameModal handleNameModal,
setTempSignerId,
uniqueId
}) { }) {
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const newWidth = containerWH.width; const newWidth = containerWH.width;
@@ -268,6 +270,8 @@ function RenderPdf({
setIsInitial={setIsInitial} setIsInitial={setIsInitial}
setValidateAlert={setValidateAlert} setValidateAlert={setValidateAlert}
unSignedWidgetId={unSignedWidgetId} unSignedWidgetId={unSignedWidgetId}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
/> />
</React.Fragment> </React.Fragment>
) )
@@ -393,6 +397,8 @@ function RenderPdf({
setSelectWidgetId={setSelectWidgetId} setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId} selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal} handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
/> />
</React.Fragment> </React.Fragment>
); );
@@ -574,6 +580,8 @@ function RenderPdf({
setSelectWidgetId={setSelectWidgetId} setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId} selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal} handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
/> />
</React.Fragment> </React.Fragment>
); );
@@ -3,7 +3,12 @@ import ModalUi from "../../primitives/ModalUi";
import RecipientList from "./RecipientList"; import RecipientList from "./RecipientList";
import { useDrag } from "react-dnd"; import { useDrag } from "react-dnd";
import WidgetList from "./WidgetList"; import WidgetList from "./WidgetList";
import { widgets } from "../../constant/Utils"; import {
radioButtonWidget,
textInputWidget,
textWidget,
widgets
} from "../../constant/Utils";
import { themeColor } from "../../constant/const"; import { themeColor } from "../../constant/const";
function WidgetComponent({ function WidgetComponent({
dragSignature, dragSignature,
@@ -58,16 +63,15 @@ function WidgetComponent({
isDragCheck: !!monitor.isDragging() isDragCheck: !!monitor.isDragging()
}) })
}); });
const [, text] = useDrag({ const [, textInput] = useDrag({
type: "BOX", type: "BOX",
item: { item: {
type: "BOX", type: "BOX",
id: 7, id: 7,
text: "text" text: textInputWidget
}, },
collect: (monitor) => ({ collect: (monitor) => ({
isDragText: !!monitor.isDragging() isDragTextInput: !!monitor.isDragging()
}) })
}); });
const [, initials] = useDrag({ const [, initials] = useDrag({
@@ -153,28 +157,28 @@ function WidgetComponent({
isDragEmail: !!monitor.isDragging() isDragEmail: !!monitor.isDragging()
}) })
}); });
const [, radio] = useDrag({ const [, radioButton] = useDrag({
type: "BOX", type: "BOX",
item: { item: {
type: "BOX", type: "BOX",
id: 15, id: 15,
text: "radio" text: radioButtonWidget
}, },
collect: (monitor) => ({ collect: (monitor) => ({
isDragRadio: !!monitor.isDragging() isDragRadiotton: !!monitor.isDragging()
}) })
}); });
const [, label] = useDrag({ const [, text] = useDrag({
type: "BOX", type: "BOX",
item: { item: {
type: "BOX", type: "BOX",
id: 16, id: 16,
text: "label" text: textWidget
}, },
collect: (monitor) => ({ collect: (monitor) => ({
isDragLabel: !!monitor.isDragging() isDragText: !!monitor.isDragging()
}) })
}); });
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
@@ -204,17 +208,17 @@ function WidgetComponent({
dragSignature, dragSignature,
dragStamp, dragStamp,
initials, initials,
label, name,
jobTitle,
company,
date,
text, text,
textInput,
checkbox, checkbox,
dropdown, dropdown,
radio, radioButton,
image, image,
date, email
name,
email,
company,
jobTitle
]; ];
const getWidgetArray = widgets; const getWidgetArray = widgets;
const newUpdateSigner = getWidgetArray.map((obj, ind) => { const newUpdateSigner = getWidgetArray.map((obj, ind) => {
@@ -227,14 +231,16 @@ function WidgetComponent({
const filterWidgets = widget.filter( const filterWidgets = widget.filter(
(data) => (data) =>
data.type !== "dropdown" && data.type !== "radio" && data.type !== "label" data.type !== "dropdown" &&
data.type !== radioButtonWidget &&
data.type !== textWidget
); );
const labelWidget = widget.filter((data) => data.type !== "label"); const textWidgetData = widget.filter((data) => data.type !== textWidget);
const updateWidgets = isSignYourself const updateWidgets = isSignYourself
? filterWidgets ? filterWidgets
: isTemplateFlow : isTemplateFlow
? labelWidget ? textWidgetData
: widget; : widget;
return ( return (
<> <>
@@ -258,11 +264,7 @@ function WidgetComponent({
alignItems: "center", alignItems: "center",
justifyContent: "center" justifyContent: "center"
}} }}
onClick={() => { onClick={() => handleModal()}
// if (signersdata?.length) {
handleModal();
// }
}}
> >
<span <span
style={{ style={{
@@ -340,7 +342,7 @@ function WidgetComponent({
display: "flex", display: "flex",
overflowX: "scroll", overflowX: "scroll",
whiteSpace: "nowrap", whiteSpace: "nowrap",
padding: "10px" padding: "10px 5px 10px 1px"
}} }}
> >
<WidgetList <WidgetList
@@ -4,7 +4,7 @@ import { getWidgetType } from "../../constant/Utils";
function WidgetList(props) { function WidgetList(props) {
return props.updateWidgets.map((item, ind) => { return props.updateWidgets.map((item, ind) => {
return ( return (
<div key={ind} style={{ marginBottom: "10px" }}> <div key={ind} style={{ marginBottom: "5px" }}>
<div <div
className="widgets" className="widgets"
onClick={() => { onClick={() => {
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css"; import "../../styles/AddUser.css";
import RegexParser from "regex-parser"; import RegexParser from "regex-parser";
import { textInputWidget } from "../../constant/Utils";
const WidgetNameModal = (props) => { const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({ const [formdata, setFormdata] = useState({
@@ -69,7 +70,7 @@ const WidgetNameModal = (props) => {
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/"; return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
case "number": case "number":
return "/^\\d+$/"; return "/^\\d+$/";
case "text": case textInputWidget:
return "/^[a-zA-Zs]+$/"; return "/^[a-zA-Zs]+$/";
default: default:
return type; return type;
@@ -111,8 +112,8 @@ const WidgetNameModal = (props) => {
required required
/> />
</div> </div>
{(props.defaultdata?.type === "text" || {(props.defaultdata?.type === textInputWidget ||
props.widgetName === "text") && ( props.widgetName === textInputWidget) && (
<> <>
<div className="form-section"> <div className="form-section">
<label htmlFor="textvalidate" style={{ fontSize: 13 }}> <label htmlFor="textvalidate" style={{ fontSize: 13 }}>
@@ -128,18 +129,7 @@ const WidgetNameModal = (props) => {
> >
<div style={{ width: "100%", position: "relative" }}> <div style={{ width: "100%", position: "relative" }}>
<input <input
style={{ className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
padding: "0.5rem 0.75rem",
width: "92%",
borderWidth: "1px",
borderColor: "#d1d5db",
borderTopLeftRadius: "0.375rem",
borderBottomLeftRadius: "0.375rem",
outline: "none",
fontSize: "0.75rem",
position: "relative",
zIndex: 2
}}
name="textvalidate" name="textvalidate"
placeholder="Enter custom expression" placeholder="Enter custom expression"
value={formdata.textvalidate} value={formdata.textvalidate}
@@ -147,8 +137,7 @@ const WidgetNameModal = (props) => {
// onBlur={() => handleBlurRegex()} // onBlur={() => handleBlurRegex()}
/> />
<select <select
style={{ position: "absolute", left: 0, zIndex: 1 }} className="validationlist addUserInput"
className="addUserInput"
name="textvalidate" name="textvalidate"
value={formdata.textvalidate} value={formdata.textvalidate}
onChange={(e) => handleChange(e)} onChange={(e) => handleChange(e)}
@@ -243,8 +232,8 @@ const WidgetNameModal = (props) => {
})} })}
</div> </div>
</div> </div>
{(props.defaultdata?.type === "text" || {(props.defaultdata?.type === textInputWidget ||
props?.widgetName === "text") && ( props?.widgetName === textInputWidget) && (
<div className="form-section"> <div className="form-section">
<label htmlFor="hint" style={{ fontSize: 13 }}> <label htmlFor="hint" style={{ fontSize: 13 }}>
Hint Hint
@@ -11,6 +11,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const [name, setName] = useState(""); const [name, setName] = useState("");
const [folderList, setFolderList] = useState([]); const [folderList, setFolderList] = useState([]);
const [isAlert, setIsAlert] = useState(false); const [isAlert, setIsAlert] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [selectedParent, setSelectedParent] = useState(); const [selectedParent, setSelectedParent] = useState();
const [alert, setAlert] = useState({ type: "info", message: "" }); const [alert, setAlert] = useState({ type: "info", message: "" });
useEffect(() => { useEffect(() => {
@@ -42,6 +43,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
}; };
const handleCreateFolder = async (event) => { const handleCreateFolder = async (event) => {
event.preventDefault(); event.preventDefault();
handleLoader(true);
if (name) { if (name) {
const currentUser = Parse.User.current(); const currentUser = Parse.User.current();
const exsitQuery = new Parse.Query(folderCls); const exsitQuery = new Parse.Query(folderCls);
@@ -52,7 +54,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
} }
const templExist = await exsitQuery.first(); const templExist = await exsitQuery.first();
if (templExist) { if (templExist) {
setAlert({ type: "dange", message: "Folder already exist!" }); setAlert({ type: "danger", message: "Folder already exist!" });
setIsAlert(true); setIsAlert(true);
setTimeout(() => { setTimeout(() => {
setIsAlert(false); setIsAlert(false);
@@ -74,6 +76,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id)); template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
const res = await template.save(); const res = await template.save();
if (res) { if (res) {
handleLoader(false);
setAlert({ setAlert({
type: "success", type: "success",
message: "Folder created successfully!" message: "Folder created successfully!"
@@ -88,6 +91,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
} }
} }
} else { } else {
handleLoader(false);
setAlert({ type: "info", message: "Please fill folder name" }); setAlert({ type: "info", message: "Please fill folder name" });
setIsAlert(true); setIsAlert(true);
setTimeout(() => { setTimeout(() => {
@@ -98,10 +102,21 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const handleOptions = (e) => { const handleOptions = (e) => {
setSelectedParent(e.target.value); setSelectedParent(e.target.value);
}; };
const handleLoader = (status) => {
setIsLoader(status);
};
return ( return (
<div> <div>
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>} {isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder"> <div id="createFolder" className="relative">
{isLoader && (
<div className="absolute h-full w-full flex justify-center items-center ">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
<h1 className="text-base font-semibold">Create Folder</h1> <h1 className="text-base font-semibold">Create Folder</h1>
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block"> <label className="block">
@@ -133,6 +148,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
<div> <div>
<button <button
onClick={handleCreateFolder} onClick={handleCreateFolder}
disabled={isLoader}
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3" className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
> >
<i className="fa-solid fa-plus mr-1"></i> <i className="fa-solid fa-plus mr-1"></i>
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import CreateFolder from "./CreateFolder"; import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi"; import ModalUi from "../../../primitives/ModalUi";
import Tooltip from "../../../primitives/Tooltip";
const SelectFolder = ({ required, onSuccess, folderCls }) => { const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
const [isOpen, SetIsOpen] = useState(false); const [isOpen, SetIsOpen] = useState(false);
const [clickFolder, setClickFolder] = useState(""); const [clickFolder, setClickFolder] = useState("");
const [selectFolder, setSelectedFolder] = useState({}); const [selectFolder, setSelectedFolder] = useState({});
@@ -22,6 +23,21 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
} }
// eslint-disable-next-line // eslint-disable-next-line
}, [isOpen]); }, [isOpen]);
// below useEffect is used to reset folder selection if user pass isReset = true from parent component
useEffect(() => {
if (isReset == true) {
handleReset();
}
}, [isReset]);
const handleReset = () => {
setFolderPath({});
setSelectedFolder("");
setClickFolder("");
setIsAdd(false);
setFolderList([]);
setTabList([]);
};
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
const fetchFolder = async (folderPtr) => { const fetchFolder = async (folderPtr) => {
setIsLoader(true); setIsLoader(true);
try { try {
@@ -47,6 +63,8 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
setIsLoader(false); setIsLoader(false);
} }
}; };
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
const handleSelect = (item) => { const handleSelect = (item) => {
setFolderList([]); setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name }); setClickFolder({ ObjectId: item.objectId, Name: item.Name });
@@ -73,8 +91,9 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
} }
}; };
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => { const handleSubmit = () => {
let url = "Root"; let url = "OpenSign™ Drive";
tabList.forEach((t) => { tabList.forEach((t) => {
url = url + " / " + t.Name; url = url + " / " + t.Name;
}); });
@@ -85,6 +104,8 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
} }
SetIsOpen(false); SetIsOpen(false);
}; };
// `handleCancel` is used to clear list of folder, close popup and folderUrl
const handleCancel = () => { const handleCancel = () => {
SetIsOpen(false); SetIsOpen(false);
setClickFolder({}); setClickFolder({});
@@ -92,6 +113,7 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
setTabList([]); setTabList([]);
}; };
// `handleCancel` is call when user click on folder name from path/tab in popup
const removeTabListItem = async (e, i) => { const removeTabListItem = async (e, i) => {
e.preventDefault(); e.preventDefault();
// setEditable(false); // setEditable(false);
@@ -121,9 +143,11 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
fetchFolder(folderPtr); fetchFolder(folderPtr);
} }
}; };
// `handleCreate` is used to open folder creation form in popup
const handleCreate = () => { const handleCreate = () => {
setIsAdd(!isAdd); setIsAdd(!isAdd);
}; };
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
const handleAddFolder = () => { const handleAddFolder = () => {
setFolderList([]); setFolderList([]);
if (clickFolder && clickFolder.ObjectId) { if (clickFolder && clickFolder.ObjectId) {
@@ -145,7 +169,7 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
{required && <span className="text-red-500 text-[13px]">*</span>} {required && <span className="text-red-500 text-[13px]">*</span>}
</label> </label>
</div> </div>
<div className="rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center"> <div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
<div> <div>
<i <i
className="far fa-folder-open text-[40px] text-[#33bbff]" className="far fa-folder-open text-[40px] text-[#33bbff]"
@@ -156,7 +180,9 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
<div className="font-semibold "> <div className="font-semibold ">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<p> <p>
{selectFolder && selectFolder.Name ? selectFolder.Name : "Root"} {selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
</p> </p>
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}> <div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
<i <i
@@ -170,20 +196,27 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""} {selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p> </p>
</div> </div>
<div className="absolute top-1 right-1 cursor-pointer">
<Tooltip
message={
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
}
/>
</div>
</div> </div>
<ModalUi <ModalUi
title={"Select Folder"} title={"Select Folder"}
isOpen={isOpen} isOpen={isOpen}
handleClose={handleCancel} handleClose={handleCancel}
> >
<div className="w-full min-w-[300px] md:min-w-[500px] px-3"> <div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]"> <div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span <span
className="cursor-pointer" className="cursor-pointer"
title="Root" title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)} onClick={(e) => removeTabListItem(e)}
> >
Root /{" "} OpenSign Drive /{" "}
</span> </span>
{tabList && {tabList &&
tabList.map((tab, i) => ( tabList.map((tab, i) => (
@@ -201,23 +234,25 @@ const SelectFolder = ({ required, onSuccess, folderCls }) => {
<hr /> <hr />
</div> </div>
<div className="mt-2 mb-3"> <div className="mt-2 mb-3">
{!isAdd && <div className="max-h-[210px] overflow-auto">
folderList.length > 0 && {!isAdd &&
folderList.map((folder) => ( folderList.length > 0 &&
<div folderList.map((folder) => (
key={folder.Name} <div
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer" key={folder.Name}
onClick={() => handleSelect(folder)} className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
> onClick={() => handleSelect(folder)}
<div className="flex items-center gap-2"> >
<i <div className="flex items-center gap-2">
className="fa fa-folder text-[#33bbff] text-[1.4rem]" <i
aria-hidden="true" className="fa fa-folder text-[#33bbff] text-[1.4rem]"
></i> aria-hidden="true"
<span className="font-semibold">{folder.Name}</span> ></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div> </div>
</div> ))}
))} </div>
{isAdd && ( {isAdd && (
<CreateFolder <CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId} parentFolderId={clickFolder && clickFolder.ObjectId}
@@ -19,7 +19,7 @@ const SignersInput = (props) => {
Modal.setAppElement("body"); Modal.setAppElement("body");
const [state, setState] = useState(undefined); const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]); // const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = React.useState([]); const [selected, setSelected] = useState([]);
const [isModal, setIsModel] = useState(false); const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions); const onChange = (selectedOptions) => setSelected(selectedOptions);
const [modalIsOpen, setModalIsOpen] = useState(false); const [modalIsOpen, setModalIsOpen] = useState(false);
@@ -63,6 +63,11 @@ const SignersInput = (props) => {
useEffect(() => { useEffect(() => {
GetSelectListData(); GetSelectListData();
}, []); }, []);
useEffect(() => {
if (props.isReset && props.isReset === true) {
setSelected([]);
}
}, [props.isReset]);
useEffect(() => { useEffect(() => {
if (selected && selected.length) { if (selected && selected.length) {
+101 -69
View File
@@ -5,6 +5,9 @@ import React from "react";
import { rgb } from "pdf-lib"; import { rgb } from "pdf-lib";
export const isMobile = window.innerWidth < 767; export const isMobile = window.innerWidth < 767;
export const textInputWidget = "text input";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
export const openInNewTab = (url) => { export const openInNewTab = (url) => {
window.open(url, "_blank", "noopener,noreferrer"); window.open(url, "_blank", "noopener,noreferrer");
}; };
@@ -162,12 +165,32 @@ export const widgets = [
iconSize: "15px" iconSize: "15px"
}, },
{ {
type: "label", type: "name",
icon: "fa-solid fa-user",
iconSize: "21px"
},
{
type: "job title",
icon: "fa-solid fa-address-card",
iconSize: "17px"
},
{
type: "company",
icon: "fa-solid fa-building",
iconSize: "25px"
},
{
type: "date",
icon: "fa-solid fa-calendar-days",
iconSize: "20px"
},
{
type: textWidget,
icon: "fa-solid fa-text-width", icon: "fa-solid fa-text-width",
iconSize: "20px" iconSize: "20px"
}, },
{ {
type: "text", type: textInputWidget,
icon: "fa-solid fa-font", icon: "fa-solid fa-font",
iconSize: "21px" iconSize: "21px"
}, },
@@ -182,7 +205,7 @@ export const widgets = [
iconSize: "19px" iconSize: "19px"
}, },
{ {
type: "radio", type: radioButtonWidget,
icon: "fa-regular fa-circle-dot", icon: "fa-regular fa-circle-dot",
iconSize: "20px" iconSize: "20px"
}, },
@@ -191,40 +214,19 @@ export const widgets = [
icon: "fa-solid fa-image", icon: "fa-solid fa-image",
iconSize: "20px" iconSize: "20px"
}, },
{
type: "date",
icon: "fa-solid fa-calendar-days",
iconSize: "20px"
},
{
type: "name",
icon: "fa-solid fa-user",
iconSize: "21px"
},
{ {
type: "email", type: "email",
icon: "fa-solid fa-envelope", icon: "fa-solid fa-envelope",
iconSize: "20px" iconSize: "20px"
},
{
type: "company",
icon: "fa-solid fa-building",
iconSize: "25px"
},
{
type: "job title",
icon: "fa-solid fa-address-card",
iconSize: "17px"
} }
]; ];
const getDate = () => { export const getDate = () => {
const date = new Date(); const date = new Date();
const milliseconds = date.getTime(); const milliseconds = date.getTime();
const newDate = moment(milliseconds).format("MM/DD/YYYY"); const newDate = moment(milliseconds).format("MM/DD/YYYY");
return newDate; return newDate;
}; };
export const addWidgetOptions = (type) => { export const addWidgetOptions = (type) => {
const defaultOpt = { const defaultOpt = {
name: "", name: "",
@@ -236,8 +238,11 @@ export const addWidgetOptions = (type) => {
case "stamp": case "stamp":
return defaultOpt; return defaultOpt;
case "checkbox": case "checkbox":
return defaultOpt; return {
case "text": ...defaultOpt,
options: { isReadOnly: false, isHideLabel: false }
};
case textInputWidget:
return { ...defaultOpt, validation: { type: "text", pattern: "" } }; return { ...defaultOpt, validation: { type: "text", pattern: "" } };
case "initials": case "initials":
return defaultOpt; return defaultOpt;
@@ -248,16 +253,25 @@ export const addWidgetOptions = (type) => {
case "job title": case "job title":
return { ...defaultOpt, validation: { type: "text", pattern: "" } }; return { ...defaultOpt, validation: { type: "text", pattern: "" } };
case "date": case "date":
return { ...defaultOpt, response: getDate() }; return {
...defaultOpt,
response: getDate(),
validation: { format: "MM/dd/yyyy", type: "date-format" }
};
case "image": case "image":
return defaultOpt; return defaultOpt;
case "email": case "email":
return { ...defaultOpt, validation: { type: "email", pattern: "" } }; return { ...defaultOpt, validation: { type: "email", pattern: "" } };
case "dropdown": case "dropdown":
return defaultOpt; return defaultOpt;
case "radio": case radioButtonWidget:
return { ...defaultOpt, values: [] }; return {
case "label": ...defaultOpt,
values: [],
isReadOnly: false,
isHideLabel: false
};
case textWidget:
return defaultOpt; return defaultOpt;
default: default:
return {}; return {};
@@ -274,26 +288,14 @@ export const getWidgetType = (item, marginLeft) => {
marginLeft: marginLeft && `${marginLeft}px` marginLeft: marginLeft && `${marginLeft}px`
}} }}
> >
<div <div className="flex items-center mr-1">
style={{ {!isMobile && (
display: "flex", <i
alignItems: "center", className="fa-sharp fa-solid fa-grip-vertical"
marginLeft: "5px" style={{ color: "#908d8d", fontSize: "13px", marginLeft: 4 }}
}} ></i>
> )}
<i <span className=" font-[400] text-[15px] text-black ml-[5px]">
className="fa-sharp fa-solid fa-grip-vertical"
style={{ color: "#908d8d", fontSize: "13px" }}
></i>
<span
style={{
fontWeight: "400",
fontSize: "15px",
// padding: "3px 20px 0px 20px",
color: "black",
marginLeft: "5px"
}}
>
{item.type} {item.type}
</span> </span>
</div> </div>
@@ -323,7 +325,7 @@ export const defaultWidthHeight = (type) => {
return { width: 150, height: 60 }; return { width: 150, height: 60 };
case "checkbox": case "checkbox":
return { width: 15, height: 15 }; return { width: 15, height: 15 };
case "text": case textInputWidget:
return { width: 150, height: 25 }; return { width: 150, height: 25 };
case "dropdown": case "dropdown":
return { width: 120, height: 22 }; return { width: 120, height: 22 };
@@ -341,9 +343,9 @@ export const defaultWidthHeight = (type) => {
return { width: 70, height: 70 }; return { width: 70, height: 70 };
case "email": case "email":
return { width: 150, height: 20 }; return { width: 150, height: 20 };
case "radio": case radioButtonWidget:
return { width: 15, height: 30 }; return { width: 15, height: 30 };
case "label": case textWidget:
return { width: 150, height: 17 }; return { width: 150, height: 17 };
default: default:
return { width: 150, height: 60 }; return { width: 150, height: 60 };
@@ -692,13 +694,27 @@ export const onChangeInput = (
const updatePosition = getXYdata.map((positionData) => { const updatePosition = getXYdata.map((positionData) => {
if (positionData.key === signKey) { if (positionData.key === signKey) {
return { if (dateFormat) {
...positionData, return {
options: { ...positionData,
...positionData.options, options: {
response: value ...positionData.options,
} response: value,
}; validation: {
type: "date-format",
format: dateFormat // This indicates the required date format explicitly.
}
}
};
} else {
return {
...positionData,
options: {
...positionData.options,
response: value
}
};
}
} }
return positionData; return positionData;
}); });
@@ -970,7 +986,6 @@ export const multiSignEmbed = async (
} else { } else {
updateItem = item.pos; updateItem = item.pos;
} }
const newWidth = containerWH.width; const newWidth = containerWH.width;
const scale = isMobile ? pdfOriginalWidth / newWidth : 1; const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
const pageNo = item.pageNumber; const pageNo = item.pageNumber;
@@ -1060,11 +1075,11 @@ export const multiSignEmbed = async (
? labelDefaultHeight ? labelDefaultHeight
: scaleHeight; : scaleHeight;
const widgetHeight = const widgetHeight =
position.type === "radio" position.type === radioButtonWidget
? 10 ? 10
: position.type === "checkbox" : position.type === "checkbox"
? 10 ? 10
: newUpdateHeight; : newUpdateHeight;
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight; const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
if (signyourself) { if (signyourself) {
@@ -1125,7 +1140,7 @@ export const multiSignEmbed = async (
} }
}; };
const widgetTypeExist = [ const widgetTypeExist = [
"text", textInputWidget,
"name", "name",
"company", "company",
"job title", "job title",
@@ -1157,6 +1172,14 @@ export const multiSignEmbed = async (
yPosition = yPos(position, ind); yPosition = yPos(position, ind);
addYPosition = height + 8; addYPosition = height + 8;
} }
if (!position?.options?.isHideLabel) {
// below line of code is used to embed label with radio button in pdf
page.drawText(item, {
x: xPos(position) + 15,
y: yPosition + 2,
size: height
});
}
checkbox.addToPage(page, { checkbox.addToPage(page, {
x: xPos(position), x: xPos(position),
y: yPosition, y: yPosition,
@@ -1171,7 +1194,7 @@ export const multiSignEmbed = async (
checkbox.enableReadOnly(); checkbox.enableReadOnly();
}); });
} }
} else if (position.type === "label") { } else if (position.type === textWidget) {
const font = await pdfDoc.embedFont("Helvetica"); const font = await pdfDoc.embedFont("Helvetica");
const fontSize = 12; const fontSize = 12;
let textContent; let textContent;
@@ -1251,10 +1274,11 @@ export const multiSignEmbed = async (
x: xPos(position), x: xPos(position),
y: yPos(position), y: yPos(position),
width: scaleWidth, width: scaleWidth,
height: scaleHeight height: scaleHeight,
borderWidth: 0
}); });
dropdown.enableReadOnly(); dropdown.enableReadOnly();
} else if (position.type === "radio") { } else if (position.type === radioButtonWidget) {
const radioRandomId = "radio" + randomId(); const radioRandomId = "radio" + randomId();
const radioGroup = form.createRadioGroup(radioRandomId); const radioGroup = form.createRadioGroup(radioRandomId);
let addYPosition = 18; let addYPosition = 18;
@@ -1268,6 +1292,14 @@ export const multiSignEmbed = async (
yPosition = yPos(position); yPosition = yPos(position);
} }
if (!position?.options?.isHideLabel) {
// below line of code is used to embed label with radio button in pdf
page.drawText(data, {
x: xPos(position) + 15,
y: yPosition + 2,
size: 11
});
}
radioGroup.addOptionToPage(data, page, { radioGroup.addOptionToPage(data, page, {
x: xPos(position), x: xPos(position),
y: yPosition, y: yPosition,
+7 -1
View File
@@ -94,10 +94,16 @@ const HomeLayout = () => {
position: "top" position: "top"
// style: { backgroundColor: "#abd4d2" }, // style: { backgroundColor: "#abd4d2" },
}, },
{
selector: '[data-tut="tourbutton"]',
content: `To upload documents for self-signing or to request others signatures, simply select the respective buttons.`,
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
...resArr, ...resArr,
{ {
selector: '[data-tut="reactourLast"]', selector: '[data-tut="reactourLast"]',
content: `You are good to go`, content: `You are ready to start using OpenSign! If you need support feel free to contact us.`,
position: "top" position: "top"
// style: { backgroundColor: "#abd4d2" }, // style: { backgroundColor: "#abd4d2" },
} }
+31 -16
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { formJson } from "../json/FormJson"; import { formJson } from "../json/FormJson";
import AddUser from "../components/AddUser"; import AddUser from "../components/AddUser";
@@ -11,6 +11,7 @@ import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title"; import Title from "../components/Title";
import PageNotFound from "./PageNotFound"; import PageNotFound from "./PageNotFound";
// `Form` render all type of Form on this basis of their provided in path
function Form() { function Form() {
const { id } = useParams(); const { id } = useParams();
@@ -27,6 +28,7 @@ function Form() {
} }
const Forms = (props) => { const Forms = (props) => {
const maxFileSize = 20;
const navigate = useNavigate(); const navigate = useNavigate();
const [signers, setSigners] = useState([]); const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" }); const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
@@ -40,29 +42,32 @@ const Forms = (props) => {
const [fileupload, setFileUpload] = useState([]); const [fileupload, setFileUpload] = useState([]);
const [fileload, setfileload] = useState(false); const [fileload, setfileload] = useState(false);
const [percentage, setpercentage] = useState(0); const [percentage, setpercentage] = useState(0);
const [isReset, setIsReset] = useState(false);
const [isAlert, setIsAlert] = useState(false); const [isAlert, setIsAlert] = useState(false);
const [isSubmit, setIsSubmit] = useState(false); const [isSubmit, setIsSubmit] = useState(false);
const [isErr, setIsErr] = useState(""); const [isErr, setIsErr] = useState("");
const handleStrInput = (e) => { const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
}; };
useEffect(() => {
handleReset();
}, [props.title]);
const handleFileInput = (e) => { const handleFileInput = (e) => {
setpercentage(0); setpercentage(0);
try { try {
let files = e.target.files; let files = e.target.files;
if (typeof files[0] !== "undefined") { if (typeof files[0] !== "undefined") {
const mb = Math.round(files[0].bytes / Math.pow(1024, 2)); const mb = Math.round(files[0].size / Math.pow(1024, 2));
if (mb > 10) { if (mb > maxFileSize) {
alert( alert(
`The selected file size is too large. Please select a file less than ${Math.round( `The selected file size is too large. Please select a file less than ${maxFileSize} MB`
10
)} MB`
); );
setFileUpload([]);
e.target.value = "";
return; return;
} else {
handleFileUpload(files[0]);
} }
handleFileUpload(files[0]);
} else { } else {
alert("Please select file."); alert("Please select file.");
return false; return false;
@@ -110,10 +115,10 @@ const Forms = (props) => {
const url = file.link; const url = file.link;
const mb = Math.round(file.bytes / Math.pow(1024, 2)); const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) { if (mb > maxFileSize) {
setTimeout(() => { setTimeout(() => {
alert( alert(
`The selected file size is too large. Please select a file less than 10 MB` `The selected file size is too large. Please select a file less than ${maxFileSize} MB`
); );
}, 500); }, 500);
return; return;
@@ -222,15 +227,19 @@ const Forms = (props) => {
}; };
const handleReset = () => { const handleReset = () => {
setIsReset(true);
setSigners([]); setSigners([]);
setFolder({ ObjectId: "", Name: "" }); setFolder({ ObjectId: "", Name: "" });
setFormData({ setFormData({
Name: "", Name: "",
Description: "", Description: "",
Note: "" Note: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "false"
}); });
setFileUpload([]); setFileUpload([]);
setpercentage(0); setpercentage(0);
setTimeout(() => setIsReset(false), 50);
}; };
return ( return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50"> <div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
@@ -334,7 +343,9 @@ const Forms = (props) => {
onChange={(e) => handleStrInput(e)} onChange={(e) => handleStrInput(e)}
/> />
</div> </div>
{props.signers && <SignersInput onChange={handleSigners} required />} {props.signers && (
<SignersInput onChange={handleSigners} isReset={isReset} required />
)}
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block"> <label className="block">
Note<span className="text-red-500 text-[13px]">*</span> Note<span className="text-red-500 text-[13px]">*</span>
@@ -347,7 +358,11 @@ const Forms = (props) => {
required required
/> />
</div> </div>
<SelectFolder onSuccess={handleFolder} folderCls={props.Cls} /> <SelectFolder
onSuccess={handleFolder}
folderCls={props.Cls}
isReset={isReset}
/>
{props.title === "Request Signatures" && ( {props.title === "Request Signatures" && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
@@ -399,10 +414,10 @@ const Forms = (props) => {
type="submit" type="submit"
disabled={isSubmit} disabled={isSubmit}
> >
Submit Next
</button> </button>
<div <div
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none" className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
onClick={() => handleReset()} onClick={() => handleReset()}
> >
Reset Reset
+9
View File
@@ -4,6 +4,7 @@ import "../styles/loginPage.css";
import loader from "../assets/images/loader2.gif"; import loader from "../assets/images/loader2.gif";
import axios from "axios"; import axios from "axios";
import { themeColor } from "../constant/const"; import { themeColor } from "../constant/const";
import { contractUsers } from "../constant/Utils";
function GuestLogin() { function GuestLogin() {
const { id, userMail, contactBookId, serverUrl } = useParams(); const { id, userMail, contactBookId, serverUrl } = useParams();
@@ -103,11 +104,19 @@ function GuestLogin() {
} else { } else {
let _user = user.data.result; let _user = user.data.result;
const parseId = localStorage.getItem("parseAppId"); const parseId = localStorage.getItem("parseAppId");
const contractUserDetails = await contractUsers(_user.email);
localStorage.setItem("UserInformation", JSON.stringify(_user)); localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem( localStorage.setItem(
`Parse/${parseId}/currentUser`, `Parse/${parseId}/currentUser`,
JSON.stringify(_user) JSON.stringify(_user)
); );
if (contractUserDetails) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
);
}
localStorage.setItem("username", _user.name); localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken); localStorage.setItem("accesstoken", _user.sessionToken);
//save isGuestSigner true in local to handle login flow header in mobile view //save isGuestSigner true in local to handle login flow header in mobile view
+11 -5
View File
@@ -19,7 +19,8 @@ import {
onImageSelect, onImageSelect,
onSaveSign, onSaveSign,
onSaveImage, onSaveImage,
addDefaultSignatureImg addDefaultSignatureImg,
radioButtonWidget
} from "../constant/Utils"; } from "../constant/Utils";
import Loader from "../primitives/LoaderWithMsg"; import Loader from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError"; import HandleError from "../primitives/HandleError";
@@ -48,6 +49,7 @@ function PdfRequestFiles() {
const [imgWH, setImgWH] = useState({}); const [imgWH, setImgWH] = useState({});
const imageRef = useRef(null); const imageRef = useRef(null);
const [handleError, setHandleError] = useState(); const [handleError, setHandleError] = useState();
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isLoading, setIsLoading] = useState({ const [isLoading, setIsLoading] = useState({
isLoad: true, isLoad: true,
message: "This might take some time" message: "This might take some time"
@@ -385,7 +387,8 @@ function PdfRequestFiles() {
for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) { for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) {
checkboxExist = checkboxExist =
checkUser[0].placeHolder[i].pos[j].type === "checkbox"; checkUser[0].placeHolder[i].pos[j].type === "checkbox";
radioExist = checkUser[0].placeHolder[i].pos[j].type === "radio"; radioExist =
checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget;
if (checkboxExist) { if (checkboxExist) {
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
(position) => (position) =>
@@ -434,7 +437,8 @@ function PdfRequestFiles() {
} else if (radioExist) { } else if (radioExist) {
requiredRadio = checkUser[0].placeHolder[i].pos.filter( requiredRadio = checkUser[0].placeHolder[i].pos.filter(
(position) => (position) =>
!position.options?.isReadOnly && position.type === "radio" !position.options?.isReadOnly &&
position.type === radioButtonWidget
); );
if (requiredRadio && requiredRadio?.length > 0) { if (requiredRadio && requiredRadio?.length > 0) {
let checkSigned; let checkSigned;
@@ -457,7 +461,7 @@ function PdfRequestFiles() {
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter( const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
(position) => (position) =>
position.options?.status === "required" && position.options?.status === "required" &&
position.type !== "radio" && position.type !== radioButtonWidget &&
position.type !== "checkbox" position.type !== "checkbox"
); );
if (requiredWidgets && requiredWidgets?.length > 0) { if (requiredWidgets && requiredWidgets?.length > 0) {
@@ -984,7 +988,7 @@ function PdfRequestFiles() {
: isDecline.currnt === "YouDeclined" : isDecline.currnt === "YouDeclined"
? "You have declined this document!" ? "You have declined this document!"
: isDecline.currnt === "another" && : isDecline.currnt === "another" &&
"You cannot sign this document as it has been declined by one or more person(s)." "You cannot sign this document as it has been declined by one or more recipient(s)."
} }
footerMessage={isDecline.currnt === "Sure"} footerMessage={isDecline.currnt === "Sure"}
declineDoc={declineDoc} declineDoc={declineDoc}
@@ -1173,6 +1177,8 @@ function PdfRequestFiles() {
setIsInitial={setIsInitial} setIsInitial={setIsInitial}
setValidateAlert={setValidateAlert} setValidateAlert={setValidateAlert}
unSignedWidgetId={unSignedWidgetId} unSignedWidgetId={unSignedWidgetId}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
/> />
)} )}
</div> </div>
+103 -35
View File
@@ -24,7 +24,10 @@ import {
randomId, randomId,
defaultWidthHeight, defaultWidthHeight,
multiSignEmbed, multiSignEmbed,
addWidgetOptions addWidgetOptions,
textInputWidget,
textWidget,
radioButtonWidget
} from "../constant/Utils"; } from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf"; import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -76,6 +79,7 @@ function PlaceHolderSign() {
const [isResize, setIsResize] = useState(false); const [isResize, setIsResize] = useState(false);
const [zIndex, setZIndex] = useState(1); const [zIndex, setZIndex] = useState(1);
const [signKey, setSignKey] = useState(); const [signKey, setSignKey] = useState();
const [tempSignerId, setTempSignerId] = useState("");
const [pdfLoadFail, setPdfLoadFail] = useState({ const [pdfLoadFail, setPdfLoadFail] = useState({
status: false, status: false,
type: "load" type: "load"
@@ -98,6 +102,10 @@ function PlaceHolderSign() {
const [widgetName, setWidgetName] = useState(false); const [widgetName, setWidgetName] = useState(false);
const [mailStatus, setMailStatus] = useState(""); const [mailStatus, setMailStatus] = useState("");
const [isCurrUser, setIsCurrUser] = useState(false); const [isCurrUser, setIsCurrUser] = useState(false);
const [isAlreadyPlace, setIsAlreadyPlace] = useState({
status: false,
message: ""
});
const color = [ const color = [
"#93a3db", "#93a3db",
"#e6c3db", "#e6c3db",
@@ -189,15 +197,49 @@ function PlaceHolderSign() {
//getting document details //getting document details
const documentData = await contractDocument(documentId); const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
// const alreadyPlaceholder = const alreadyPlaceholder = documentData[0]?.SignedUrl;
// documentData[0].Placeholders && documentData[0].Placeholders; // Check if document is sent for signing
// if (alreadyPlaceholder && alreadyPlaceholder.length > 0) { if (alreadyPlaceholder) {
// setIsAlreadyPlace(true); // Check if the document is completed
// } const isCompleted =
// setSignersData(documentData[0]); documentData[0].IsCompleted && documentData[0]?.IsCompleted;
// setIsSelectId(0); // Get the expiration date of the document
// setSignerObjId(documentData[0].Signers[0].objectId); const expireDate = documentData[0].ExpiryDate.iso;
// setContractName(documentData[0].Signers[0].className); // Check if the document has been declined
const declined =
documentData[0].IsDeclined && documentData[0]?.IsDeclined;
// Get the expiration update date in milliseconds
const expireUpdateDate = new Date(expireDate).getTime();
// Get the current date in milliseconds
const currDate = new Date().getTime();
if (isCompleted) {
// If document is completed
setIsAlreadyPlace({
status: true,
message: "This document has been signed by all Signers."
});
} else if (declined) {
// If document has been declined
setIsAlreadyPlace({
status: true,
message:
"This document has been declined by one or more recipient(s)."
});
} else if (currDate > expireUpdateDate) {
// If document has expired
setIsAlreadyPlace({
status: true,
message: "This Document is no longer available."
});
} else {
// If document is dispatched for signing
setIsAlreadyPlace({
status: true,
message: "The document has already been dispatched for signing."
});
}
}
setPdfDetails(documentData); setPdfDetails(documentData);
if (documentData[0].Signers && documentData[0].Signers.length > 0) { if (documentData[0].Signers && documentData[0].Signers.length > 0) {
@@ -313,9 +355,6 @@ function PlaceHolderSign() {
//function for setting position after drop signature button over pdf //function for setting position after drop signature button over pdf
const addPositionOfSignature = (item, monitor) => { const addPositionOfSignature = (item, monitor) => {
if (item && item.text) {
setWidgetName(item.text);
}
getSignerPos(item, monitor); getSignerPos(item, monitor);
}; };
@@ -332,7 +371,6 @@ function PlaceHolderSign() {
let dropData = []; let dropData = [];
let placeHolder; let placeHolder;
const dragTypeValue = item?.text ? item.text : monitor.type; const dragTypeValue = item?.text ? item.text : monitor.type;
if (item === "onclick") { if (item === "onclick") {
const dropObj = { const dropObj = {
//onclick put placeholder center on pdf //onclick put placeholder center on pdf
@@ -380,18 +418,16 @@ function PlaceHolderSign() {
pos: dropData pos: dropData
}; };
} }
setSelectWidgetId(key); setSelectWidgetId(key);
if (signer) { if (signer) {
let filterSignerPos; let filterSignerPos;
if (dragTypeValue === "label") { if (dragTypeValue === textWidget) {
filterSignerPos = signerPos.filter((data) => data.Role === "prefill"); filterSignerPos = signerPos.filter((data) => data.Role === "prefill");
} else { } else {
filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
} }
const { blockColor, Role } = signer; const { blockColor, Role } = signer;
//adding placholder in existing signer pos array (placaholder) //adding placholder in existing signer pos array (placaholder)
if (filterSignerPos.length > 0) { if (filterSignerPos.length > 0) {
const getPlaceHolder = filterSignerPos[0].placeHolder; const getPlaceHolder = filterSignerPos[0].placeHolder;
@@ -412,7 +448,7 @@ function PlaceHolderSign() {
}; };
updatePlace.push(xyPos); updatePlace.push(xyPos);
let updatesignerPos; let updatesignerPos;
if (dragTypeValue === "label") { if (dragTypeValue === textWidget) {
updatesignerPos = signerPos.map((x) => updatesignerPos = signerPos.map((x) =>
x.Role === "prefill" ? { ...x, placeHolder: updatePlace } : x x.Role === "prefill" ? { ...x, placeHolder: updatePlace } : x
); );
@@ -425,7 +461,7 @@ function PlaceHolderSign() {
setSignerPos(updatesignerPos); setSignerPos(updatesignerPos);
} else { } else {
let updatesignerPos; let updatesignerPos;
if (dragTypeValue === "label") { if (dragTypeValue === textWidget) {
updatesignerPos = signerPos.map((x) => updatesignerPos = signerPos.map((x) =>
x.Role === "prefill" x.Role === "prefill"
? { ...x, placeHolder: [...x.placeHolder, placeHolder] } ? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
@@ -442,9 +478,8 @@ function PlaceHolderSign() {
} }
} else { } else {
//adding new placeholder for selected signer in pos array (placeholder) //adding new placeholder for selected signer in pos array (placeholder)
// const signerData = signerPos;
let placeHolderPos; let placeHolderPos;
if (dragTypeValue === "label") { if (dragTypeValue === textWidget) {
placeHolderPos = { placeHolderPos = {
signerPtr: {}, signerPtr: {},
signerObjId: "", signerObjId: "",
@@ -484,14 +519,13 @@ function PlaceHolderSign() {
setShowDropdown(true); setShowDropdown(true);
} else if (dragTypeValue === "checkbox") { } else if (dragTypeValue === "checkbox") {
setIsCheckbox(true); setIsCheckbox(true);
} else if (dragTypeValue === "radio") { } else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true); setIsRadio(true);
} else if (dragTypeValue !== "label" && dragTypeValue !== "signature") {
setIsNameModal(true);
} }
setWidgetType(dragTypeValue); setWidgetType(dragTypeValue);
setSignKey(key); setSignKey(key);
setCurrWidgetsDetails({}); setCurrWidgetsDetails({});
setWidgetName(dragTypeValue);
} }
} }
}; };
@@ -736,7 +770,7 @@ function PlaceHolderSign() {
if (getPrefill && isLabel) { if (getPrefill && isLabel) {
const alert = { const alert = {
mssg: "label", mssg: textWidget,
alert: true alert: true
}; };
setIsSendAlert(alert); setIsSendAlert(alert);
@@ -1051,7 +1085,8 @@ function PlaceHolderSign() {
addOption, addOption,
deleteOption, deleteOption,
status, status,
defaultValue defaultValue,
isHideLabel
) => { ) => {
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
if (filterSignerPos.length > 0) { if (filterSignerPos.length > 0) {
@@ -1067,7 +1102,7 @@ function PlaceHolderSign() {
const getPosData = getXYdata; const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => { const addSignPos = getPosData.map((position) => {
if (position.key === signKey) { if (position.key === signKey) {
if (widgetType === "radio") { if (widgetType === radioButtonWidget) {
if (addOption) { if (addOption) {
return { return {
...position, ...position,
@@ -1089,7 +1124,8 @@ function PlaceHolderSign() {
...position.options, ...position.options,
name: dropdownName, name: dropdownName,
values: dropdownOptions, values: dropdownOptions,
isReadOnly: isReadOnly, isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false,
defaultValue: defaultValue defaultValue: defaultValue
} }
}; };
@@ -1121,7 +1157,8 @@ function PlaceHolderSign() {
maxRequiredCount: maxCount maxRequiredCount: maxCount
}, },
defaultValue: defaultValue, defaultValue: defaultValue,
isReadOnly: isReadOnly isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false
} }
}; };
} }
@@ -1186,7 +1223,7 @@ function PlaceHolderSign() {
const getPosData = getXYdata; const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => { const addSignPos = getPosData.map((position) => {
if (position.key === signKey) { if (position.key === signKey) {
if (position.type === "text") { if (position.type === textInputWidget) {
return { return {
...position, ...position,
options: { options: {
@@ -1406,13 +1443,13 @@ function PlaceHolderSign() {
<ModalUi <ModalUi
headerColor={ headerColor={
isSendAlert.mssg === "sure" || isSendAlert.mssg === "label" isSendAlert.mssg === "sure" || isSendAlert.mssg === textWidget
? "#dc3545" ? "#dc3545"
: isSendAlert.mssg === "confirm" && themeColor : isSendAlert.mssg === "confirm" && themeColor
} }
isOpen={isSendAlert.alert} isOpen={isSendAlert.alert}
title={ title={
isSendAlert.mssg === "sure" || isSendAlert.mssg === "label" isSendAlert.mssg === "sure" || isSendAlert.mssg === textWidget
? "Fields required" ? "Fields required"
: isSendAlert.mssg === "confirm" && "Send Mail" : isSendAlert.mssg === "confirm" && "Send Mail"
} }
@@ -1421,7 +1458,7 @@ function PlaceHolderSign() {
<div style={{ height: "100%", padding: 20 }}> <div style={{ height: "100%", padding: 20 }}>
{isSendAlert.mssg === "sure" ? ( {isSendAlert.mssg === "sure" ? (
<p>Please add field for all recipients.</p> <p>Please add field for all recipients.</p>
) : isSendAlert.mssg === "label" ? ( ) : isSendAlert.mssg === textWidget ? (
<p>Please confirm that you have filled label widget.</p> <p>Please confirm that you have filled label widget.</p>
) : ( ) : (
isSendAlert.mssg === "confirm" && ( isSendAlert.mssg === "confirm" && (
@@ -1553,11 +1590,14 @@ function PlaceHolderSign() {
allPages={allPages} allPages={allPages}
pageNumber={pageNumber} pageNumber={pageNumber}
signKey={signKey} signKey={signKey}
// signerObjId={signerObjId}
Id={uniqueId} Id={uniqueId}
widgetType={widgetType}
setUniqueId={setUniqueId}
tempSignerId={tempSignerId}
setTempSignerId={setTempSignerId}
/> />
<DropdownWidgetOption <DropdownWidgetOption
type="radio" type={radioButtonWidget}
title="Radio group" title="Radio group"
showDropdown={isRadio} showDropdown={isRadio}
setShowDropdown={setIsRadio} setShowDropdown={setIsRadio}
@@ -1639,6 +1679,8 @@ function PlaceHolderSign() {
setSelectWidgetId={setSelectWidgetId} setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId} selectWidgetId={selectWidgetId}
handleNameModal={setIsNameModal} handleNameModal={setIsNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
/> />
)} )}
</div> </div>
@@ -1748,7 +1790,33 @@ function PlaceHolderSign() {
</button> </button>
</div> </div>
</ModalUi> </ModalUi>
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlreadyPlace.status}
title={"Document Alert"}
showClose={false}
>
<div style={{ height: "100%", padding: 20 }}>
<p>{isAlreadyPlace.message}</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() => handleRecipientSign()}
type="button"
className="finishBtn cancelBtn"
>
View
</button>
</div>
</ModalUi>
<LinkUserModal <LinkUserModal
handleAddUser={handleAddUser} handleAddUser={handleAddUser}
isAddUser={isAddUser} isAddUser={isAddUser}
+13 -14
View File
@@ -3,7 +3,6 @@ import { PDFDocument } from "pdf-lib";
import "../styles/signature.css"; import "../styles/signature.css";
import { themeColor } from "../constant/const"; import { themeColor } from "../constant/const";
import axios from "axios"; import axios from "axios";
import moment from "moment";
import Loader from "../primitives/LoaderWithMsg"; import Loader from "../primitives/LoaderWithMsg";
import loader from "../assets/images/loader2.gif"; import loader from "../assets/images/loader2.gif";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
@@ -25,7 +24,9 @@ import {
onSaveSign, onSaveSign,
contractUsers, contractUsers,
contactBook, contactBook,
randomId randomId,
getDate,
textInputWidget
} from "../constant/Utils"; } from "../constant/Utils";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Tour from "reactour"; import Tour from "reactour";
@@ -308,13 +309,6 @@ function SignYourSelf() {
setIsLoading(loadObj); setIsLoading(loadObj);
} }
}; };
const getDate = () => {
const date = new Date();
const milliseconds = date.getTime();
const newDate = moment(milliseconds).format("MM/DD/YYYY");
return newDate;
};
const getWidgetValue = (type) => { const getWidgetValue = (type) => {
switch (type) { switch (type) {
case "name": case "name":
@@ -333,6 +327,7 @@ function SignYourSelf() {
return ""; return "";
} }
}; };
const addWidgetOptions = (type) => { const addWidgetOptions = (type) => {
switch (type) { switch (type) {
case "signature": case "signature":
@@ -348,7 +343,7 @@ function SignYourSelf() {
name: "checkbox" name: "checkbox"
}; };
case "text": case textInputWidget:
return { return {
name: "text" name: "text"
}; };
@@ -386,7 +381,8 @@ function SignYourSelf() {
case "date": case "date":
return { return {
name: "date", name: "date",
defaultValue: getDate() response: getDate(),
validation: { format: "MM/dd/yyyy", type: "date-format" }
}; };
case "image": case "image":
return { return {
@@ -596,7 +592,6 @@ function SignYourSelf() {
await signPdfFun(pdfBytes, documentId); await signPdfFun(pdfBytes, documentId);
} }
} }
//function for get digital signature //function for get digital signature
const signPdfFun = async (base64Url, documentId) => { const signPdfFun = async (base64Url, documentId) => {
let singleSign = { let singleSign = {
@@ -875,7 +870,10 @@ function SignYourSelf() {
maxCount, maxCount,
isReadOnly, isReadOnly,
addOption, addOption,
deleteOption deleteOption,
status,
defaultValue,
isHideLabel
) => { ) => {
const getPageNumer = xyPostion.filter( const getPageNumer = xyPostion.filter(
(data) => data.pageNumber === pageNumber (data) => data.pageNumber === pageNumber
@@ -907,7 +905,8 @@ function SignYourSelf() {
...position.options, ...position.options,
name: dropdownName, name: dropdownName,
values: dropdownOptions, values: dropdownOptions,
isReadOnly: isReadOnly isReadOnly: isReadOnly,
isHideLabel: isHideLabel || false
} }
}; };
} }
+16 -13
View File
@@ -21,7 +21,9 @@ import {
addZIndex, addZIndex,
createDocument, createDocument,
defaultWidthHeight, defaultWidthHeight,
addWidgetOptions addWidgetOptions,
textInputWidget,
radioButtonWidget
} from "../constant/Utils"; } from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf"; import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css"; import "../styles/AddUser.css";
@@ -442,11 +444,9 @@ const TemplatePlaceholder = () => {
setShowDropdown(true); setShowDropdown(true);
} else if (dragTypeValue === "checkbox") { } else if (dragTypeValue === "checkbox") {
setIsCheckbox(true); setIsCheckbox(true);
} else if (dragTypeValue === "radio") { } else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true); setIsRadio(true);
} else if (dragTypeValue !== "label" && dragTypeValue !== "signature") { }
setIsNameModal(true);
}
setCurrWidgetsDetails({}); setCurrWidgetsDetails({});
setWidgetType(dragTypeValue); setWidgetType(dragTypeValue);
setSignKey(key); setSignKey(key);
@@ -970,7 +970,8 @@ const TemplatePlaceholder = () => {
addOption, addOption,
deleteOption, deleteOption,
status, status,
defaultValue defaultValue,
isHideLabel
) => { ) => {
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
if (filterSignerPos.length > 0) { if (filterSignerPos.length > 0) {
@@ -986,11 +987,10 @@ const TemplatePlaceholder = () => {
const getPosData = getXYdata; const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => { const addSignPos = getPosData.map((position) => {
if (position.key === signKey) { if (position.key === signKey) {
if (widgetType === "radio") { if (widgetType === radioButtonWidget) {
if (addOption) { if (addOption) {
return { return {
...position, ...position,
Height: position.Height Height: position.Height
? position.Height + 15 ? position.Height + 15
: defaultWidthHeight(widgetType).height + 15 : defaultWidthHeight(widgetType).height + 15
@@ -1010,7 +1010,9 @@ const TemplatePlaceholder = () => {
name: dropdownName, name: dropdownName,
values: dropdownOptions, values: dropdownOptions,
status: status, status: status,
defaultValue: defaultValue defaultValue: defaultValue,
isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false
} }
}; };
} }
@@ -1040,8 +1042,9 @@ const TemplatePlaceholder = () => {
minRequiredCount: minCount, minRequiredCount: minCount,
maxRequiredCount: maxCount maxRequiredCount: maxCount
}, },
isReadOnly: isReadOnly, isReadOnly: isReadOnly || false,
defaultValue: defaultValue defaultValue: defaultValue,
isHideLabel: isHideLabel || false
} }
}; };
} }
@@ -1106,7 +1109,7 @@ const TemplatePlaceholder = () => {
const getPosData = getXYdata; const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => { const addSignPos = getPosData.map((position) => {
if (position.key === signKey) { if (position.key === signKey) {
if (position.type === "text") { if (position.type === textInputWidget) {
return { return {
...position, ...position,
options: { options: {
@@ -1305,7 +1308,7 @@ const TemplatePlaceholder = () => {
</div> </div>
</ModalUi> </ModalUi>
<DropdownWidgetOption <DropdownWidgetOption
type="radio" type={radioButtonWidget}
title="Radio group" title="Radio group"
showDropdown={isRadio} showDropdown={isRadio}
setShowDropdown={setIsRadio} setShowDropdown={setIsRadio}
+18
View File
@@ -0,0 +1,18 @@
import React from "react";
// import * as HoverCard from "@radix-ui/react-hover-card";
// import "../styles/opensigndrive.css";
import { Tooltip as ReactTooltip } from "react-tooltip";
const Tooltip = ({ children, message }) => (
<>
<a data-tooltip-id="my-tooltip" data-tooltip-content={message}>
{children ? (
children
) : (
<i className="fa-solid fa-question border-[1.5px] px-1.5 py-0.5 rounded-full border-[#33bbff] text-xs text-[#33bbff]"></i>
)}
</a>
<ReactTooltip id="my-tooltip" className="max-w-[200px]" />
</>
);
export default Tooltip;
+27
View File
@@ -60,6 +60,7 @@
color: #ffffff; color: #ffffff;
outline: none; outline: none;
} }
/* For classes: bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none */ /* For classes: bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none */
.resetbutton { .resetbutton {
background-color: #188ae2; background-color: #188ae2;
@@ -75,3 +76,29 @@
outline: none; outline: none;
margin-left: 8px; margin-left: 8px;
} }
.validationlist {
position: absolute;
left: 0;
z-index: 1;
appearance: none;
-moz-appearance: "none";
-webkit-appearance: "none";
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnUlEQVR4nO2QMQrCQBBFX51NbWMp2uox7Gw8h22uYEguIZNKyB0lMIElTNzdGBvdB8PC8Gces5DJ/Cxn4Aa4QM5pbsgncQF6rQYoZ3IFcPey11jByRsaqzYuctqfZo8xki3QBURuRtDpfBQ74GEsaYHN5It8wYFEhgExlj2NniwRhC7qP70gRSRrCN6JZE2BJZJvCEb2QKVvJvMvvAD2WzaK/35kGAAAAABJRU5ErkJggg==");
background-repeat: no-repeat;
background-position: right 0.7rem top 50%;
background-size: 1rem auto;
}
@media (max-width: 375px) {
.validationlist {
background-position: right 0.5rem top 50%;
}
}
@media (min-width:375px) and (max-width: 767px) {
.validationlist {
background-position: right 1rem top 50%;
}
}
+17 -5
View File
@@ -163,7 +163,6 @@
.signatureBtn { .signatureBtn {
border: 1.5px solid #47a3ad; border: 1.5px solid #47a3ad;
border-radius: 3px; border-radius: 3px;
display: flex; display: flex;
padding: 0px; padding: 0px;
@@ -838,10 +837,18 @@ option {
justify-content: flex-start; justify-content: flex-start;
} }
.labelTextArea { .labelWidthDesktop {
font-size: 12px;
min-width: 100%; min-width: 100%;
min-height: 100%; min-height: 100%;
}
.labelWidthMobile {
width: 100%;
height: 100%;
}
.labelTextArea {
font-size: 12px;
border: 1px solid #007bff; border: 1px solid #007bff;
outline: none; outline: none;
z-index: 999; z-index: 999;
@@ -945,7 +952,7 @@ option {
to prevent sudden quick movement (as the to prevent sudden quick movement (as the
navigation bar gets a new position at the top of the navigation bar gets a new position at the top of the
page (position:fixed and top:0) */ page (position:fixed and top:0) */
.stickyHead + .content { .stickyHead+.content {
padding-top: 60px; padding-top: 60px;
} }
@@ -1174,6 +1181,7 @@ option {
font-size: 12px; font-size: 12px;
margin-left: 5px; margin-left: 5px;
} }
.defaultOptions:focus { .defaultOptions:focus {
outline: none; outline: none;
} }
@@ -1193,6 +1201,10 @@ option {
overflow-y: auto; overflow-y: auto;
} }
.signatureBtn {
width: max-content;
}
.dropdownModal { .dropdownModal {
position: fixed; position: fixed;
top: 50%; top: 50%;
@@ -1222,4 +1234,4 @@ option {
min-width: 78%; min-width: 78%;
overflow-y: auto; overflow-y: auto;
} }
} }
@@ -165,29 +165,26 @@ export default async function createDocumentwithCoordinate(request, response) {
'Signers', 'Signers',
contact?.map(x => x.contactPtr) contact?.map(x => x.contactPtr)
); );
let updatePlaceholders = contact.map((signer, index) => { let updatePlaceholders = contact.map(signer => {
const placeHolder = []; const placeHolder = [];
for (const widget of signer.widgets) { for (const widget of signer.widgets) {
const pageNumber = widget.page; const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber); const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const widgetData = { const widgetData = {
xPosition: widget.x,
yPosition: widget.y,
isStamp: widget.type === 'stamp', isStamp: widget.type === 'stamp',
key: randomId(), key: randomId(),
isDrag: false, isDrag: false,
firstXPos: widget.x,
firstYPos: widget.y,
yBottom: 0,
scale: 1, scale: 1,
isMobile: false, isMobile: false,
zIndex: 1, zIndex: 1,
type: widget.type, type: widget.type,
widgetValue: '', options: widget.type === 'signature' ? signOpt : widget.options,
Width: widget.w, Width: widget.w,
Height: widget.h, Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
}; };
if (page) { if (page) {
@@ -123,7 +123,7 @@ export default async function createTemplate(request, response) {
} }
return response.json({ return response.json({
objectId: res.id, objectId: res.id,
url: baseUrl.origin + '/load/signmicroapp/template/' + res.id, url: baseUrl.origin + '/template/' + res.id,
}); });
} else { } else {
return response.status(405).json({ error: 'Invalid API Token!' }); return response.status(405).json({ error: 'Invalid API Token!' });
@@ -129,23 +129,20 @@ export default async function createTemplatewithCoordinate(request, response) {
for (const widget of signer.widgets) { for (const widget of signer.widgets) {
const pageNumber = widget.page; const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber); const page = placeHolder.find(page => page.pageNumber === pageNumber);
const signOpt = { name: 'signature', status: 'required' };
const widgetData = { const widgetData = {
xPosition: widget.x,
yPosition: widget.y,
isStamp: widget.type === 'stamp', isStamp: widget.type === 'stamp',
key: randomId(), key: randomId(),
isDrag: false, isDrag: false,
firstXPos: widget.x,
firstYPos: widget.y,
yBottom: 0,
scale: 1, scale: 1,
isMobile: false, isMobile: false,
zIndex: 1, zIndex: 1,
type: widget.type, type: widget.type,
widgetValue: '', options: widget.type === 'signature' ? signOpt : widget.options,
Width: widget.w, Width: widget.w,
Height: widget.h, Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
}; };
if (page) { if (page) {
@@ -14,6 +14,7 @@ export default async function draftDocument(request, response) {
const SendinOrder = request.body.sendInOrder || false; const SendinOrder = request.body.sendInOrder || false;
// console.log('fileData ', fileData); // console.log('fileData ', fileData);
const protocol = customAPIurl(); const protocol = customAPIurl();
const baseUrl = new URL(process.env.SERVER_URL);
try { try {
const reqToken = request.headers['x-api-token']; const reqToken = request.headers['x-api-token'];
@@ -135,7 +136,7 @@ export default async function draftDocument(request, response) {
} }
return response.json({ return response.json({
objectId: res.id, objectId: res.id,
url: protocol + '/load/signmicroapp/placeholdersign/' + res.id, url: `${baseUrl.origin}/placeholdersign/${res.id}`,
}); });
} else { } else {
if (request.posthog) { if (request.posthog) {
+87 -76
View File
@@ -1,7 +1,8 @@
import SignPDF from './SignPDF.min.cjs'; import SignPDF from './SignPDF.min.cjs';
import fs from 'node:fs'; import fs from 'node:fs';
import axios from 'axios'; import axios from 'axios';
import { plainAddPlaceholder } from 'node-signpdf/dist/helpers/index.js'; import { pdflibAddPlaceholder } from './customSignPdf/pdflibplaceholder.min.js';
import { PDFDocument } from 'pdf-lib';
const serverUrl = process.env.SERVER_URL, const serverUrl = process.env.SERVER_URL,
APPID = process.env.APP_ID, APPID = process.env.APP_ID,
masterKEY = process.env.MASTER_KEY; masterKEY = process.env.MASTER_KEY;
@@ -15,20 +16,20 @@ async function uploadFile(e, a) {
console.log('Err ', e), fs.unlinkSync(a); console.log('Err ', e), fs.unlinkSync(a);
} }
} }
async function updateDoc(t, s, r, i, n, o) { async function updateDoc(t, s, r, i, o, n) {
try { try {
var d = { var d = {
UserPtr: { __type: 'Pointer', className: o, objectId: r }, UserPtr: { __type: 'Pointer', className: n, objectId: r },
SignedUrl: s, SignedUrl: s,
Activity: 'Signed', Activity: 'Signed',
ipAddress: i, ipAddress: i,
}; };
let e; let e;
var l = (e = n.AuditTrail && 0 < n.AuditTrail.length ? [...n.AuditTrail, d] : [d]).filter( var l = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, d] : [d]).filter(
e => 'Signed' === e.Activity e => 'Signed' === e.Activity
); );
let a = !1; let a = !1;
!((n.Signers && 0 < n.Signers.length && l.length !== n.Signers.length) || !(a = !0)); !((o.Signers && 0 < o.Signers.length && l.length !== o.Signers.length) || !(a = !0));
var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a }; var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, { await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, {
headers: { headers: {
@@ -150,9 +151,8 @@ async function sendDoctoWebhook(t, e, a, s) {
} }
})); }));
} }
async function PDF(i, n) { async function PDF(i) {
try { try {
i.params.sign;
var e = i.params.docId, var e = i.params.docId,
a = i.params.userId, a = i.params.userId,
o = await axios.get( o = await axios.get(
@@ -173,125 +173,136 @@ async function PDF(i, n) {
}); });
if (!t.data || !t.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 d, var n,
d,
l, l,
c, c = JSON.stringify({ objectId: a });
p = JSON.stringify({ objectId: a });
let s, r; let s, r;
r = a r = a
? (d = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + p, { ? (n = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, {
headers: { headers: {
'X-Parse-Application-Id': APPID, 'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': i.headers.sessiontoken, 'X-Parse-Session-Token': i.headers.sessiontoken,
}, },
})).data && 0 < d.data.results.length })).data && 0 < n.data.results.length
? ((s = d), 'contracts_Contactbook') ? ((s = n), 'contracts_Contactbook')
: ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + p, { : ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY }, headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})), })),
'contracts_Users') 'contracts_Users')
: ((l = JSON.stringify({ : ((d = JSON.stringify({
UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId }, UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId },
})), })),
(c = await axios.get(serverUrl + '/classes/contracts_Users?where=' + l, { (l = await axios.get(serverUrl + '/classes/contracts_Users?where=' + d, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY }, headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})).data && 0 < c.data.results.length })).data && 0 < l.data.results.length
? ((s = c), 'contracts_Users') ? ((s = l), 'contracts_Users')
: ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + l, { : ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + d, {
headers: { headers: {
'X-Parse-Application-Id': APPID, 'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': i.headers.sessiontoken, 'X-Parse-Session-Token': i.headers.sessiontoken,
}, },
})), })),
'contracts_Contactbook')); 'contracts_Contactbook'));
var m = s.data.results[0].Name, var p = s.data.results[0].Name,
g = s.data.results[0].Email; m = s.data.results[0].Email;
if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' }; if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' };
{ {
let e = Buffer.from(i.params.pdfFile, 'base64'); let e = Buffer.from(i.params.pdfFile, 'base64');
var h = process.env.PFX_BASE64, var g = process.env.PFX_BASE64,
u = Buffer.from(h, 'base64'), h = Buffer.from(g, 'base64'),
f = { u = {
UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId }, UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId },
SignedUrl: '', SignedUrl: '',
Activity: 'Signed', Activity: 'Signed',
ipAddress: i.headers['x-real-ip'], ipAddress: i.headers['x-real-ip'],
}; };
let a; let a;
var y = (a = var f = (a =
o.data.AuditTrail && 0 < o.data.AuditTrail.length o.data.AuditTrail && 0 < o.data.AuditTrail.length
? [...o.data.AuditTrail, f] ? [...o.data.AuditTrail, u]
: [f]).filter(e => 'Signed' === e.Activity); : [u]).filter(e => 'Signed' === e.Activity);
let t = !1; let t = !1;
!( !(
(o.data.Signers && 0 < o.data.Signers.length && y.length !== o.data.Signers.length) || (o.data.Signers && 0 < o.data.Signers.length && f.length !== o.data.Signers.length) ||
!(t = !0) !(t = !0)
); );
var v, var y,
P, P,
b = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`, v,
x = './exports/' + b, b,
U = x,
w,
U,
I,
S,
A = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
D = './exports/' + A,
E =
(t (t
? ((v = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')), ? ((y = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
(e = (e =
v && 0 < v.length y && 0 < y.length
? plainAddPlaceholder({ ? ((P = await PDFDocument.load(e)),
pdfBuffer: e, pdflibAddPlaceholder({
reason: 'Digitally signed by OpenSign for ' + v?.join(', '), pdfDoc: P,
reason: 'Digitally signed by OpenSign for ' + y?.join(', '),
location: 'location', location: 'location',
signatureLength: 1e4, signatureLength: 15e3,
}) }),
: plainAddPlaceholder({ (v = await P.save()),
pdfBuffer: e, Buffer.from(v))
reason: 'Digitally signed by OpenSign for ' + m + ' <' + g + '>', : ((b = await PDFDocument.load(e)),
pdflibAddPlaceholder({
pdfDoc: b,
reason: 'Digitally signed by OpenSign for ' + p + ' <' + m + '>',
location: 'location', location: 'location',
signatureLength: 1e4, signatureLength: 15e3,
})), }),
(P = await new SignPDF(e, u).signPDF()), (x = await b.save()),
fs.writeFileSync(x, P)) Buffer.from(x))),
: fs.writeFileSync(x, e), (w = await new SignPDF(e, h).signPDF()),
await uploadFile(b, x)); fs.writeFileSync(D, w))
if (U && U.imageUrl) { : fs.writeFileSync(D, e),
const n = await updateDoc( await uploadFile(A, D));
i.params.docId, if (E && E.imageUrl)
U.imageUrl, return (
s.data.results[0].objectId, (U = await updateDoc(
i.headers['x-real-ip'], i.params.docId,
o.data, E.imageUrl,
r s.data.results[0].objectId,
); i.headers['x-real-ip'],
var I, o.data,
w = { r
url: U.imageUrl, )),
(I = {
url: E.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name }, sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name },
pdfName: o.data.Name, pdfName: o.data.Name,
receiver: g, receiver: m,
}; }),
return (
o.data.IsSendMail && !1 === o.data.IsSendMail o.data.IsSendMail && !1 === o.data.IsSendMail
? console.log("don't send mail") ? console.log("don't send mail")
: sendMail(w), : sendMail(I),
sendDoctoWebhook(o, U.imageUrl, 'signed', s?.data.results?.[0]), sendDoctoWebhook(o, E.imageUrl, 'signed', s?.data.results?.[0]),
n && U &&
n.isCompleted && U.isCompleted &&
((I = { ((S = {
url: U.imageUrl, url: E.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: 'OpenSign™' }, sender: { Mail: o.data.ExtUserPtr.Email, Name: 'OpenSign™' },
pdfName: o.data.Name, pdfName: o.data.Name,
receiver: o.data.ExtUserPtr.Email, receiver: o.data.ExtUserPtr.Email,
}), }),
o.data.IsSendMail && !1 === o.data.IsSendMail o.data.IsSendMail && !1 === o.data.IsSendMail
? console.log("don't send mail") ? console.log("don't send mail")
: sendCompletedMail(I), : sendCompletedMail(S),
sendDoctoWebhook(o, U.imageUrl, 'completed')), sendDoctoWebhook(o, E.imageUrl, 'completed')),
fs.unlinkSync(x), fs.unlinkSync(D),
console.log('New Signed PDF created called: ' + x), console.log('New Signed PDF created called: ' + D),
'success' === n.message 'success' === U.message
? { status: 'success', data: U.imageUrl } ? { status: 'success', data: E.imageUrl }
: { status: 'error', message: 'please provide required parameters!' } : { status: 'error', message: 'please provide required parameters!' }
); );
}
} }
} }
} catch (e) { } catch (e) {
@@ -1 +1,20 @@
const ERROR_TYPE_UNKNOWN=1,ERROR_TYPE_INPUT=2,ERROR_TYPE_PARSE=3,ERROR_VERIFY_SIGNATURE=4;class SignPdfError extends Error{constructor(R,E=ERROR_TYPE_UNKNOWN){super(R),this.type=E}}SignPdfError.TYPE_UNKNOWN=ERROR_TYPE_UNKNOWN,SignPdfError.TYPE_INPUT=ERROR_TYPE_INPUT,SignPdfError.TYPE_PARSE=ERROR_TYPE_PARSE,SignPdfError.VERIFY_SIGNATURE=ERROR_VERIFY_SIGNATURE;export default SignPdfError;export{ERROR_TYPE_UNKNOWN,ERROR_TYPE_INPUT,ERROR_TYPE_PARSE,ERROR_VERIFY_SIGNATURE}; const ERROR_TYPE_UNKNOWN = 1,
ERROR_TYPE_INPUT = 2,
ERROR_TYPE_PARSE = 3,
ERROR_VERIFY_SIGNATURE = 4;
class SignPdfError extends Error {
constructor(R, E = ERROR_TYPE_UNKNOWN) {
super(R), (this.type = E);
}
}
(SignPdfError.TYPE_UNKNOWN = ERROR_TYPE_UNKNOWN),
(SignPdfError.TYPE_INPUT = ERROR_TYPE_INPUT),
(SignPdfError.TYPE_PARSE = ERROR_TYPE_PARSE),
(SignPdfError.VERIFY_SIGNATURE = ERROR_VERIFY_SIGNATURE);
export {
ERROR_TYPE_UNKNOWN,
ERROR_TYPE_INPUT,
ERROR_TYPE_PARSE,
ERROR_VERIFY_SIGNATURE,
SignPdfError,
};
@@ -0,0 +1,87 @@
import {
DEFAULT_BYTE_RANGE_PLACEHOLDER,
DEFAULT_SIGNATURE_LENGTH,
SUBFILTER_ADOBE_PKCS7_DETACHED,
} from './const.min.js';
import { SignPdfError } from './SignPdfError.min.js';
import {
PDFArray,
PDFDict,
PDFHexString,
PDFName,
PDFNumber,
PDFInvalidObject,
PDFString,
} from 'pdf-lib';
const pdflibAddPlaceholder = ({
pdfDoc: e = void 0,
pdfPage: o = void 0,
reason: t,
contactInfo: r = 'emailfromp1289@gmail.com',
name: i = 'Name from p12',
location: n,
signingTime: a = void 0,
signatureLength: P = DEFAULT_SIGNATURE_LENGTH,
byteRangePlaceholder: F = DEFAULT_BYTE_RANGE_PLACEHOLDER,
subFilter: D = SUBFILTER_ADOBE_PKCS7_DETACHED,
widgetRect: f = [0, 0, 0, 0],
appName: g = void 0,
}) => {
if (void 0 === e && void 0 === o)
throw new SignPdfError('PDFDoc or PDFPage must be set.', SignPdfError.TYPE_INPUT);
var e = e ?? o.doc,
o = o ?? e.getPages()[0],
m = PDFArray.withContext(e.context),
F =
(m.push(PDFNumber.of(0)),
m.push(PDFName.of(F)),
m.push(PDFName.of(F)),
m.push(PDFName.of(F)),
PDFHexString.of(String.fromCharCode(0).repeat(P))),
P = g ? { App: { Name: g } } : {},
g = e.context.obj({
Type: 'Sig',
Filter: 'Adobe.PPKLite',
SubFilter: D,
ByteRange: m,
Contents: F,
Reason: PDFString.of(t),
M: PDFString.fromDate(a ?? new Date()),
ContactInfo: PDFString.of(r),
Name: PDFString.of(i),
Location: PDFString.of(n),
Prop_Build: { Filter: { Name: 'Adobe.PPKLite' }, ...P },
}),
D = new Uint8Array(g.sizeInBytes()),
m = (g.copyBytesInto(D, 0), PDFInvalidObject.of(D)),
F = e.context.register(m);
const s = PDFArray.withContext(e.context);
f.forEach(e => s.push(PDFNumber.of(e)));
(t = e.context.formXObject([], { BBox: f, Resources: {} })),
(a = e.context.obj({
Type: 'Annot',
Subtype: 'Widget',
FT: 'Sig',
Rect: s,
V: F,
T: PDFString.of('Signature1'),
F: 4,
P: o.ref,
AP: { N: e.context.register(t) },
})),
(r = e.context.register(a));
let c = o.node.lookupMaybe(PDFName.of('Annots'), PDFArray),
d =
((c = void 0 === c ? e.context.obj([]) : c).push(r),
o.node.set(PDFName.of('Annots'), c),
e.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict));
void 0 === d &&
((d = e.context.obj({ Fields: [] })),
(i = e.context.register(d)),
e.catalog.set(PDFName.of('AcroForm'), i));
let N;
N = d.has(PDFName.of('SigFlags')) ? d.get(PDFName.of('SigFlags')) : PDFNumber.of(0);
n = PDFNumber.of(3 | N.asNumber());
d.set(PDFName.of('SigFlags'), n), d.get(PDFName.of('Fields')).push(r);
};
export { pdflibAddPlaceholder };
@@ -0,0 +1,219 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
// TODO: set className here
const className = 'w_dashboard';
const dashboard = new Parse.Query(className);
const updatedashboard = await dashboard.get('35KBoSgoAK');
updatedashboard.set('rows', [
{
columns: [
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'Card',
icon: 'fas fa-signature',
bgColor: '#f0058e',
label: 'Need your Signature',
description: null,
data: {
id: 'e04ee9dc-932d-6b80-0202-703fd154eb74',
queryType: '',
class: 'contracts_Document',
query:
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: 'count',
Redirect_type: 'Report',
Redirect_id: '4Hhwbp482K',
tourSection: 'tourcard1',
tourMessage:
'Clicking on this card will take you to the list of documents awaiting your review.',
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'Card',
icon: 'fas fa-sign-out-alt',
bgColor: '#g0058e',
label: 'Out for signatures',
description: null,
data: {
id: 'c1935cc0-28d4-3d73-b72a-8c1e3d18a17f',
queryType: '',
class: 'contracts_Document',
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: 'count',
Redirect_type: 'Report',
Redirect_id: '1MwEuxLEkF',
tourSection: 'tourcard2',
tourMessage:
'Clicking on this card will take you to a list of documents awaiting signature.',
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'report',
reportId: '5Go51Q7T8r',
data: {
tourSection: 'tourreport1',
tourMessage: 'This is a list of documents that are waiting for your signature.',
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'report',
reportId: 'd9k3UfYHBc',
data: {
tourSection: 'tourreport2',
tourMessage:
"This is a list of documents you've sent to other parties for signature.",
},
},
},
{
colxs: '12',
colmd: '12',
collg: '12',
widget: {
type: 'report',
reportId: 'kC5mfynCi4',
data: {
tourSection: 'tourreport3',
tourMessage:
'This are documents you have started but have not finalized for sending.',
},
},
},
],
},
]);
return updatedashboard.save();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const className = 'w_dashboard';
const dashboard = new Parse.Query(className);
const revertdashboard = await dashboard.get('35KBoSgoAK');
revertdashboard.set('rows', [
{
columns: [
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'Card',
icon: 'fas fa-signature',
bgColor: '#f0058e',
label: 'Need your Signature',
description: null,
data: {
id: 'e04ee9dc-932d-6b80-0202-703fd154eb74',
queryType: '',
class: 'contracts_Document',
query:
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: 'count',
Redirect_type: 'Report',
Redirect_id: '4Hhwbp482K',
tourSection: 'tourcard1',
tourMessage:
'This card shows the aggregate count of documents that are awaiting your signature. Clicking on this card will seamlessly take you to the detailed list of such documents for your review and action.',
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'Card',
icon: 'fas fa-sign-out-alt',
bgColor: '#g0058e',
label: 'Out for signatures',
description: null,
data: {
id: 'c1935cc0-28d4-3d73-b72a-8c1e3d18a17f',
queryType: '',
class: 'contracts_Document',
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: 'count',
Redirect_type: 'Report',
Redirect_id: '1MwEuxLEkF',
tourSection: 'tourcard2',
tourMessage:
"This card indicates the total number of documents you've sent out for signatures. A single click on this card will navigate you to a comprehensive list of these documents, allowing you to monitor their status.",
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'report',
reportId: '5Go51Q7T8r',
data: {
tourSection: 'tourreport1',
tourMessage:
'Here lies a curated list of documents that are pending your signature. These are requests from other users that require your immediate attention for completion.',
},
},
},
{
colxs: '12',
colmd: '6',
collg: '6',
widget: {
type: 'report',
reportId: 'd9k3UfYHBc',
data: {
tourSection: 'tourreport2',
tourMessage:
"This area displays all the documents you've dispatched for signatures from other parties. It serves as a real-time tracker for documents pending external approval.",
},
},
},
{
colxs: '12',
colmd: '12',
collg: '12',
widget: {
type: 'report',
reportId: 'kC5mfynCi4',
data: {
tourSection: 'tourreport3',
tourMessage:
"In this section, you'll find your draft documents awaiting completion. These are the documents you've initiated but haven't yet finalized for sending.",
},
},
},
],
},
]);
return revertdashboard.save();
};
@@ -0,0 +1,690 @@
/**
*
* @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: '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: 'far fa-newspaper',
title: 'Templates',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-file-signature',
title: 'Create template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
{
icon: 'fa-solid fa-file-contract',
title: 'Manage templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
],
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: '',
description: '',
objectId: 'opensigndrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: '',
description: '',
objectId: 'managesign',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
const AdminMenu = new Parse.Query(className);
const updateAdminMenu = await AdminMenu.get('VPh91h0ZHk');
updateAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: '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: 'far fa-newspaper',
title: 'Templates',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-file-signature',
title: 'Create template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
{
icon: 'fa-solid fa-file-contract',
title: 'Manage templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
],
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: '',
description: '',
objectId: 'opensigndrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: '',
description: '',
objectId: 'managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [updateUserMenu, updateAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'w_menu';
const userMenu = new Parse.Query(className);
const revertUserMenu = await userMenu.get('H9vRfEYKhT');
revertUserMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: '',
description: '',
objectId: 'opensigndrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: '',
description: '',
objectId: 'managesign',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
const adminMenu = new Parse.Query(className);
const revertAdminMenu = await adminMenu.get('VPh91h0ZHk');
revertAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: '',
description: '',
objectId: 'opensigndrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: '',
description: '',
objectId: 'managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
const batch = [revertUserMenu, revertAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
+34 -20
View File
@@ -54,26 +54,39 @@ if (process.env.USE_LOCAL !== 'TRUE') {
let transporterMail; let transporterMail;
let mailgunClient; let mailgunClient;
let mailgunDomain; let mailgunDomain;
let isMailAdapter = false;
if (process.env.SMTP_ENABLE) { if (process.env.SMTP_ENABLE) {
transporterMail = createTransport({ try {
host: process.env.SMTP_HOST, transporterMail = createTransport({
port: process.env.SMTP_PORT || 465, host: process.env.SMTP_HOST,
secure: process.env.SMTP_SECURE || true, port: process.env.SMTP_PORT || 465,
auth: { secure: process.env.SMTP_SECURE || true,
user: process.env.SMTP_USER_EMAIL, auth: {
pass: process.env.SMTP_PASS, user: process.env.SMTP_USER_EMAIL,
}, pass: process.env.SMTP_PASS,
}); },
});
await transporterMail.verify();
isMailAdapter = true;
} catch (err) {
isMailAdapter = false;
console.log('Please provide valid SMTP credentials');
}
} else if (process.env.MAILGUN_API_KEY) { } else if (process.env.MAILGUN_API_KEY) {
const mailgun = new Mailgun(formData); try {
mailgunClient = mailgun.client({ const mailgun = new Mailgun(formData);
username: 'api', mailgunClient = mailgun.client({
key: process.env.MAILGUN_API_KEY, username: 'api',
}); key: process.env.MAILGUN_API_KEY,
});
mailgunDomain = process.env.MAILGUN_DOMAIN; mailgunDomain = process.env.MAILGUN_DOMAIN;
isMailAdapter = true;
} catch (error) {
isMailAdapter = false;
console.log('Please provide valid Mailgun credentials');
}
} }
export const config = { export const config = {
databaseURI: databaseURI:
process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev', process.env.DATABASE_URI || process.env.MONGODB_URI || 'mongodb://localhost:27017/dev',
@@ -82,17 +95,18 @@ export const config = {
}, },
appId: process.env.APP_ID || 'myAppId', appId: process.env.APP_ID || 'myAppId',
maxLimit: 500, maxLimit: 500,
maxUploadSize: '30mb',
masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret! masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret!
masterKeyIps: ['0.0.0.0/0', '::/0'], // '::1' masterKeyIps: ['0.0.0.0/0', '::/0'], // '::1'
serverURL: process.env.SERVER_URL || 'http://localhost:8080/app', // Don't forget to change to https if needed serverURL: 'http://localhost:8080/app', // Don't forget to change to https if needed
verifyUserEmails: process.env.SMTP_ENABLE || process.env.MAILGUN_API_KEY ? true : false, verifyUserEmails: isMailAdapter === true ? true : false,
publicServerURL: process.env.SERVER_URL || 'http://localhost:8080/app', publicServerURL: process.env.SERVER_URL || 'http://localhost:8080/app',
// Your apps name. This will appear in the subject and body of the emails that are sent. // Your apps name. This will appear in the subject and body of the emails that are sent.
appName: 'Open Sign', appName: 'Open Sign',
allowClientClassCreation: false, allowClientClassCreation: false,
allowExpiredAuthDataToken: false, allowExpiredAuthDataToken: false,
encodeParseObjectInCloudFunction: true, encodeParseObjectInCloudFunction: true,
...(process.env.SMTP_ENABLE || process.env.MAILGUN_API_KEY ...(isMailAdapter === true
? { ? {
emailAdapter: { emailAdapter: {
module: 'parse-server-api-mail-adapter', module: 'parse-server-api-mail-adapter',
+4 -4
View File
@@ -32,7 +32,7 @@
"parse-server-api-mail-adapter": "^3.1.1", "parse-server-api-mail-adapter": "^3.1.1",
"parse-server-fs-adapter": "1.0.1", "parse-server-fs-adapter": "1.0.1",
"parse-server-s3-adapter": "^1.2.0", "parse-server-s3-adapter": "^1.2.0",
"pdf-lib": "^1.16.0", "pdf-lib": "^1.17.1",
"pdfkit": "^0.13.0", "pdfkit": "^0.13.0",
"posthog-node": "^3.6.2", "posthog-node": "^3.6.2",
"razorpay": "^2.8.6", "razorpay": "^2.8.6",
@@ -7025,9 +7025,9 @@
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
}, },
"node_modules/pdf-lib": { "node_modules/pdf-lib": {
"version": "1.16.0", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.16.0.tgz", "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-P/1SSmElOBKrPlbc+Sn7UxikRQbzVA64+4Dh6/uczPscvq/NatP9eryoOguyBTpTnzICNiG8EnMH4Ziqp2TnFA==", "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"dependencies": { "dependencies": {
"@pdf-lib/standard-fonts": "^1.0.0", "@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1", "@pdf-lib/upng": "^1.0.1",
+1 -1
View File
@@ -41,7 +41,7 @@
"parse-server-api-mail-adapter": "^3.1.1", "parse-server-api-mail-adapter": "^3.1.1",
"parse-server-fs-adapter": "1.0.1", "parse-server-fs-adapter": "1.0.1",
"parse-server-s3-adapter": "^1.2.0", "parse-server-s3-adapter": "^1.2.0",
"pdf-lib": "^1.16.0", "pdf-lib": "^1.17.1",
"pdfkit": "^0.13.0", "pdfkit": "^0.13.0",
"posthog-node": "^3.6.2", "posthog-node": "^3.6.2",
"razorpay": "^2.8.6", "razorpay": "^2.8.6",