Compare commits

..
Author SHA1 Message Date
prafull-opensignlabs 2e3f92f96f change serverurl to work on DO 2025-03-21 17:56:50 +05:30
prafull-opensignlabsandGitHub 4fe87f41a0 Update Caddyfile 2025-03-21 17:49:03 +05:30
prafull-opensignlabsandGitHub ad165914c1 update server route in docker compose file 2025-03-21 17:48:22 +05:30
prafull-opensignlabsandGitHub 9658761ede Merge pull request #1646 from OpenSignLabs/prafull-opensignlabs-patch-2
add deprecated warning for env
2025-03-20 16:03:28 +05:30
prafull-opensignlabsandGitHub 6aad832e37 refacctor: add deprecated warning for env 2025-03-20 11:52:18 +05:30
prafull-opensignlabsandGitHub e4b6be51d9 Merge pull request #1644 from OpenSignLabs/updates-13949958694
Merge pull request #395 from OpenSignLabs
2025-03-19 20:41:10 +05:30
prafull-opensignlabs a3b2c797fb Merge pull request #395 from OpenSignLabs/fix_dailyemailquota
fix: dailyemailquota not updating for user and default value not updating of checkbox in API
2025-03-19 15:08:50 +00:00
24 changed files with 759 additions and 1018 deletions
+4 -3
View File
@@ -8,13 +8,11 @@ PUBLIC_URL=https://localhost:3001
GENERATE_SOURCEMAP=false GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead) # Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
# REACT_APP_SERVERURL=http://localhost:8080/app # REACT_APP_SERVERURL=http://localhost:8080/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API. # (DEPRECATED) This should not be changed if provided; it should be 'opensign'.
REACT_APP_APPID=opensign REACT_APP_APPID=opensign
# Backend ExpressJS config **************************************************************************************************************************************************************************************** # Backend ExpressJS config ****************************************************************************************************************************************************************************************
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
APP_ID=opensign
# Name of the app. It will be visible in the verification emails sent out. # Name of the app. It will be visible in the verification emails sent out.
appName=open_sign_server 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.
@@ -111,3 +109,6 @@ CRUxFgQUDYlgGVxSxuOknhQc256x3++7BDwwMTAhMAkGBSsOAwIaBQAEFFjASdYl
# Provide Pass pharse of above PFX or p12 document # Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=opensign PASS_PHRASE=opensign
# (DEPRECATED) This should not be changed if provided; it should be 'opensign'.
APP_ID=opensign
+2 -2
View File
@@ -1,7 +1,7 @@
{$HOST_URL} { {$HOST_URL} {
reverse_proxy client:3000 reverse_proxy client:3000
handle_path /app/* { handle_path /api/* {
reverse_proxy server:8080 reverse_proxy server:8080
rewrite * /app{uri} rewrite * {uri}
} }
} }
+1
View File
@@ -16,6 +16,7 @@ COPY apps/OpenSign/.husky .
# Define environment variables if needed # Define environment variables if needed
ENV NODE_ENV=production ENV NODE_ENV=production
ENV GENERATE_SOURCEMAP=false
# build # build
RUN npm run build RUN npm run build
+119 -130
View File
@@ -2,13 +2,8 @@ import React, { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import Title from "./Title"; import Title from "./Title";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { import { copytoData, usertimezone } from "../constant/Utils";
copytoData, import { emailRegex } from "../constant/const";
usertimezone
} from "../constant/Utils";
import {
emailRegex,
} from "../constant/const";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function generatePassword(length) { function generatePassword(length) {
const characters = const characters =
@@ -46,9 +41,9 @@ const AddUser = (props) => {
if (teamRes.length > 0) { if (teamRes.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamRes)); const _teamRes = JSON.parse(JSON.stringify(teamRes));
setTeamList(_teamRes); setTeamList(_teamRes);
const allUserId = const allUserId =
_teamRes.find((x) => x.Name === "All Users")?.objectId || ""; _teamRes.find((x) => x.Name === "All Users")?.objectId || "";
setFormdata((prev) => ({ ...prev, team: allUserId })); setFormdata((prev) => ({ ...prev, team: allUserId }));
} }
}; };
const checkUserExist = async () => { const checkUserExist = async () => {
@@ -269,126 +264,120 @@ const AddUser = (props) => {
<Loader /> <Loader />
</div> </div>
)} )}
<div className="w-full mx-auto"> <div className="w-full mx-auto">
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="mb-3"> <div className="mb-3">
<label <label
htmlFor="name" htmlFor="name"
className="block text-xs text-gray-700 font-semibold" className="block text-xs text-gray-700 font-semibold"
> >
{t("name")} {t("name")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
</label> </label>
<input <input
type="text" type="text"
name="name" name="name"
value={formdata.name} value={formdata.name}
onChange={(e) => handleChange(e)} onChange={(e) => handleChange(e)}
onInvalid={(e) => onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
e.target.setCustomValidity(t("input-required")) onInput={(e) => e.target.setCustomValidity("")}
} required
onInput={(e) => e.target.setCustomValidity("")} className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
required />
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" </div>
/> <div className="mb-3">
</div> <label
<div className="mb-3"> htmlFor="email"
<label className="block text-xs text-gray-700 font-semibold"
htmlFor="email" >
className="block text-xs text-gray-700 font-semibold" {t("email")}
> <span className="text-[red] text-[13px]"> *</span>
{t("email")} </label>
<span className="text-[red] text-[13px]"> *</span> <input
</label> type="email"
<input name="email"
type="email" value={formdata.email}
name="email" onChange={(e) => handleChange(e)}
value={formdata.email} required
onChange={(e) => handleChange(e)} onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
required onInput={(e) => e.target.setCustomValidity("")}
onInvalid={(e) => className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
e.target.setCustomValidity(t("input-required")) />
} </div>
onInput={(e) => e.target.setCustomValidity("")} <div className="mb-3">
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" <label className="block text-xs text-gray-700 font-semibold">
/> {t("password")}
</div> </label>
<div className="mb-3"> <div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<label className="block text-xs text-gray-700 font-semibold"> <div className="break-all">{formdata?.password}</div>
{t("password")} <i
</label> onClick={() => copytoclipboard(formdata?.password)}
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]"> className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
<div className="break-all">{formdata?.password}</div> ></i>
<i </div>
onClick={() => copytoclipboard(formdata?.password)} <div className="text-[12px] ml-2 mb-0 text-[red] select-none">
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer " {t("password-generateed")}
></i> </div>
</div> </div>
<div className="text-[12px] ml-2 mb-0 text-[red] select-none"> <div className="mb-3">
{t("password-generateed")} <label
</div> htmlFor="phone"
</div> className="block text-xs text-gray-700 font-semibold"
<div className="mb-3"> >
<label {t("phone")}
htmlFor="phone" {/* <span className="text-[red] text-[13px]"> *</span> */}
className="block text-xs text-gray-700 font-semibold" </label>
> <input
{t("phone")} type="text"
{/* <span className="text-[red] text-[13px]"> *</span> */} name="phone"
</label> placeholder={t("phone-optional")}
<input value={formdata.phone}
type="text" onChange={(e) => handleChange(e)}
name="phone" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder={t("phone-optional")} />
value={formdata.phone} </div>
onChange={(e) => handleChange(e)} <div className="mb-3">
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" <label
/> htmlFor="phone"
</div> className="block text-xs text-gray-700 font-semibold"
<div className="mb-3"> >
<label {t("Role")}
htmlFor="phone" <span className="text-[red] text-[13px]"> *</span>
className="block text-xs text-gray-700 font-semibold" </label>
> <select
{t("Role")} value={formdata.role}
<span className="text-[red] text-[13px]"> *</span> onChange={(e) => handleChange(e)}
</label> name="role"
<select className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formdata.role} onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onChange={(e) => handleChange(e)} onInput={(e) => e.target.setCustomValidity("")}
name="role" required
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" >
onInvalid={(e) => <option defaultValue={""} value={""}>
e.target.setCustomValidity(t("input-required")) {t("Select")}
} </option>
onInput={(e) => e.target.setCustomValidity("")} {role.length > 0 &&
required role.map((x) => (
> <option key={x} value={x}>
<option defaultValue={""} value={""}> {x}
{t("Select")} </option>
</option> ))}
{role.length > 0 && </select>
role.map((x) => ( </div>
<option key={x} value={x}> <div className="flex items-center mt-3 gap-2 text-white">
{x} <button type="submit" className="op-btn op-btn-primary">
</option> {t("submit")}
))} </button>
</select> <div
</div> type="button"
<div className="flex items-center mt-3 gap-2 text-white"> onClick={() => handleReset()}
<button type="submit" className="op-btn op-btn-primary"> className="op-btn op-btn-secondary"
{t("submit")} >
</button> {t("cancel")}
<div </div>
type="button" </div>
onClick={() => handleReset()} </form>
className="op-btn op-btn-secondary" </div>
>
{t("cancel")}
</div>
</div>
</form>
</div>
</div> </div>
); );
}; };
@@ -1,19 +1,11 @@
import React, { import React, { useState } from "react";
useState, import { getFileName } from "../../constant/Utils";
} from "react";
import {
getFileName
} from "../../constant/Utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Tooltip } from "react-tooltip"; import { Tooltip } from "react-tooltip";
import SignersInput from "../shared/fields/SignersInput"; import SignersInput from "../shared/fields/SignersInput";
const EditTemplate = ({ const EditTemplate = ({ template, onSuccess }) => {
template, const appName = "OpenSign™";
onSuccess,
}) => {
const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
Name: template?.Name || "", Name: template?.Name || "",
@@ -254,11 +246,7 @@ const EditTemplate = ({
</Tooltip> </Tooltip>
</label> </label>
<div className="flex flex-col md:flex-row md:gap-4"> <div className="flex flex-col md:flex-row md:gap-4">
<div <div className={`flex items-center gap-2 ml-2 mb-1`}>
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -267,11 +255,7 @@ const EditTemplate = ({
/> />
<div className="text-center">{t("yes")}</div> <div className="text-center">{t("yes")}</div>
</div> </div>
<div <div className={`flex items-center gap-2 ml-2 mb-1`}>
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -35,8 +35,7 @@ function EmailComponent({
setEmailList([]); setEmailList([]);
}, 1500); }, 1500);
setIsLoading(false); setIsLoading(false);
} } else {
else {
setIsLoading(false); setIsLoading(false);
setIsEmail(false); setIsEmail(false);
setIsAlert({ setIsAlert({
@@ -91,7 +91,7 @@ const getDefaultDate = (dateStr, format) => {
return date; return date;
} }
}; };
function Placeholder(props) { function Placeholder(props) {
//'isTouchDevice' is used to detect whether a device has a touchscreen or is mouse-based //'isTouchDevice' is used to detect whether a device has a touchscreen or is mouse-based
const isTouchDevice = navigator.maxTouchPoints > 0; const isTouchDevice = navigator.maxTouchPoints > 0;
@@ -117,7 +117,6 @@ const WidgetNameModal = (props) => {
} }
} }
const handleCheckboxChange = (index) => { const handleCheckboxChange = (index) => {
// Update the state with the modified array // Update the state with the modified array
setSignatureType((prev) => setSignatureType((prev) =>
+51 -83
View File
@@ -7,9 +7,7 @@ import { appInfo } from "./appinfo";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
import printModule from "print-js"; import printModule from "print-js";
import fontkit from "@pdf-lib/fontkit"; import fontkit from "@pdf-lib/fontkit";
import { import { themeColor } from "./const";
themeColor
} from "./const";
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]; export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
export const fontColorArr = ["red", "black", "blue", "yellow"]; export const fontColorArr = ["red", "black", "blue", "yellow"];
@@ -136,14 +134,12 @@ export const pdfNewWidthFun = (divRef) => {
}; };
//`contractUsers` function is used to get contract_User details //`contractUsers` function is used to get contract_User details
export const contractUsers = async ( export const contractUsers = async () => {
) => {
try { try {
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`; const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken"); const accesstoken = localStorage.getItem("accesstoken");
const token = const token = { "X-Parse-Session-Token": accesstoken };
{ "X-Parse-Session-Token": accesstoken };
const headers = { const headers = {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -495,7 +491,7 @@ export const signPdfFun = async (
documentId, documentId,
signerObjectId, signerObjectId,
objectId, objectId,
widgets, widgets
) => { ) => {
let isCustomCompletionMail = false; let isCustomCompletionMail = false;
try { try {
@@ -504,10 +500,7 @@ export const signPdfFun = async (
if (tenantDetails && tenantDetails === "user does not exist!") { if (tenantDetails && tenantDetails === "user does not exist!") {
return { status: "error", message: "User does not exist." }; return { status: "error", message: "User does not exist." };
} else { } else {
if ( if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject
) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
} }
@@ -1011,8 +1004,7 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
//function for embed document id //function for embed document id
export const embedDocId = async (pdfDoc, documentId, allPages) => { export const embedDocId = async (pdfDoc, documentId, allPages) => {
const appName = const appName = "OpenSign™";
"OpenSign™";
// `fontBytes` is used to embed custom font in pdf // `fontBytes` is used to embed custom font in pdf
const fontBytes = await fileasbytes( const fontBytes = await fileasbytes(
"https://cdn.opensignlabs.com/webfonts/times.ttf" "https://cdn.opensignlabs.com/webfonts/times.ttf"
@@ -1874,12 +1866,9 @@ export const contactBook = async (objectId) => {
}; };
//function for getting document details from contract_Documents class //function for getting document details from contract_Documents class
export const contractDocument = async ( export const contractDocument = async (documentId) => {
documentId,
) => {
const data = { docId: documentId }; const data = { docId: documentId };
const token = const token = { sessionToken: localStorage.getItem("accesstoken") };
{ sessionToken: localStorage.getItem("accesstoken") };
const documentDeatils = await axios const documentDeatils = await axios
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, { .post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
headers: { headers: {
@@ -2063,36 +2052,31 @@ export const getFileName = (fileUrl) => {
//fetch tenant app logo from `partners_Tenant` class by domain name //fetch tenant app logo from `partners_Tenant` class by domain name
export const getAppLogo = async () => { export const getAppLogo = async () => {
const domain = window.location.host; const domain = window.location.host;
try { try {
const tenant = await Parse.Cloud.run("getlogobydomain", { const tenant = await Parse.Cloud.run("getlogobydomain", {
domain: domain domain: domain
}); });
if (tenant) { if (tenant) {
localStorage.setItem("appname", "OpenSign™"); localStorage.setItem("appname", "OpenSign™");
return { logo: tenant?.logo, user: tenant?.user }; return { logo: tenant?.logo, user: tenant?.user };
}
} catch (err) {
console.log("err in getlogo ", err);
if (err?.message?.includes("valid JSON")) {
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
} else {
return { logo: appInfo.applogo, user: "exist" };
}
} }
} catch (err) {
console.log("err in getlogo ", err);
if (err?.message?.includes("valid JSON")) {
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
} else {
return { logo: appInfo.applogo, user: "exist" };
}
}
}; };
export const getTenantDetails = async ( export const getTenantDetails = async (objectId, contactId) => {
objectId,
contactId
) => {
try { try {
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`; const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken"); const accesstoken = localStorage.getItem("accesstoken");
const token = const token = { "X-Parse-Session-Token": accesstoken };
{ "X-Parse-Session-Token": accesstoken }; const data = { userId: objectId, contactId: contactId };
const data =
{ userId: objectId, contactId: contactId };
const res = await axios.post(url, data, { const res = await axios.post(url, data, {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -2179,8 +2163,7 @@ export const handleSendOTP = async (email) => {
} }
}; };
export const fetchUrl = async (url, pdfName) => { export const fetchUrl = async (url, pdfName) => {
const appName = const appName = "OpenSign™";
"OpenSign™";
try { try {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
@@ -2194,11 +2177,7 @@ export const fetchUrl = async (url, pdfName) => {
console.error("Error downloading the file:", error); console.error("Error downloading the file:", error);
} }
}; };
export const getSignedUrl = async ( export const getSignedUrl = async (pdfUrl, docId, templateId) => {
pdfUrl,
docId,
templateId
) => {
//use only axios here due to public template sign //use only axios here due to public template sign
const axiosRes = await axios.post( const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
@@ -2259,10 +2238,7 @@ export const handleDownloadPdf = async (
setIsDownloading && setIsDownloading("pdf"); setIsDownloading && setIsDownloading("pdf");
const docId = pdfDetails?.[0]?.objectId || ""; const docId = pdfDetails?.[0]?.objectId || "";
try { try {
const url = await getSignedUrl( const url = await getSignedUrl(pdfUrl, docId);
pdfUrl,
docId,
);
await fetchUrl(url, pdfName); await fetchUrl(url, pdfName);
setIsDownloading && setIsDownloading(""); setIsDownloading && setIsDownloading("");
} catch (err) { } catch (err) {
@@ -2292,7 +2268,7 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ {
url: pdfUrl, url: pdfUrl,
docId: docId, docId: docId
}, },
{ {
headers: { headers: {
@@ -2336,8 +2312,7 @@ export const handleDownloadCertificate = async (
setIsDownloading, setIsDownloading,
isZip isZip
) => { ) => {
const appName = const appName = "OpenSign™";
"OpenSign™";
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) { if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
try { try {
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl); await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
@@ -2421,14 +2396,13 @@ export const handleDownloadCertificate = async (
export function escapeRegExp(string) { export function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
} }
export async function findContact( export async function findContact(value) {
value,
) {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsigners`; const url = `${baseURL}functions/getsigners`;
const token = const token = {
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; "X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
@@ -2640,23 +2614,20 @@ export function base64ToArrayBuffer(base64) {
return bytes.buffer; return bytes.buffer;
} }
export const convertBase64ToFile = async ( export const convertBase64ToFile = async (pdfName, pdfBase64) => {
pdfName,
pdfBase64,
) => {
const fileName = sanitizeFileName(pdfName) + ".pdf"; const fileName = sanitizeFileName(pdfName) + ".pdf";
try { try {
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 }); const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
// Save the Parse File if needed // Save the Parse File if needed
const pdfData = await pdfFile.save(); const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url(); const pdfUrl = pdfData.url();
const fileRes = await getSecureUrl(pdfUrl); const fileRes = await getSecureUrl(pdfUrl);
if (fileRes?.url) { if (fileRes?.url) {
return fileRes.url; return fileRes.url;
}
} catch (e) {
console.log("error in convertbase64tofile", e);
} }
} catch (e) {
console.log("error in convertbase64tofile", e);
}
}; };
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => { export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
setScale(scale + 0.1 * scale); setScale(scale + 0.1 * scale);
@@ -2955,13 +2926,10 @@ export const flattenPdf = async (pdfFile) => {
}; };
export const mailTemplate = (param) => { export const mailTemplate = (param) => {
const appName = const appName = "OpenSign™";
"OpenSign™"; const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
const logo =
`<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
const opurl = const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
const subject = `${param.senderName} has requested you to sign "${param.title}"`; const subject = `${param.senderName} has requested you to sign "${param.title}"`;
const body = const body =
+1 -1
View File
@@ -4,7 +4,7 @@ export function serverUrl_fn() {
let baseUrl; let baseUrl;
baseUrl = process.env.REACT_APP_SERVERURL baseUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL ? process.env.REACT_APP_SERVERURL
: window.location.origin + "/app"; : window.location.origin + "/api/app";
return baseUrl; return baseUrl;
} }
+187 -207
View File
@@ -36,8 +36,7 @@ function Form() {
} }
const Forms = (props) => { const Forms = (props) => {
const appName = const appName = "OpenSign™";
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const maxFileSize = 20; const maxFileSize = 20;
const abortController = new AbortController(); const abortController = new AbortController();
@@ -101,12 +100,12 @@ const Forms = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const initializeValues = async () => { const initializeValues = async () => {
setFormData((obj) => ({ setFormData((obj) => ({
...obj, ...obj,
NotifyOnSignatures: true, NotifyOnSignatures: true,
SendinOrder: sendinorder, SendinOrder: sendinorder,
IsTourEnabled: istourenabled IsTourEnabled: istourenabled
})); }));
}; };
function getFileAsArrayBuffer(file) { function getFileAsArrayBuffer(file) {
@@ -144,17 +143,88 @@ const Forms = (props) => {
const name = generatePdfName(16); const name = generatePdfName(16);
const pdfName = `${name?.split(".")[0]}.pdf`; const pdfName = `${name?.split(".")[0]}.pdf`;
setfileload(true); setfileload(true);
try { try {
const res = await getFileAsArrayBuffer(files[0]); const res = await getFileAsArrayBuffer(files[0]);
const flatPdf = await flattenPdf(res); const flatPdf = await flattenPdf(res);
const parseFile = new Parse.File( const parseFile = new Parse.File(
pdfName, pdfName,
[...flatPdf], [...flatPdf],
"application/pdf" "application/pdf"
); );
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
if (response.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
removeFile(e);
}
} else {
removeFile(e);
}
} catch (error) {
removeFile(e);
console.error("Error uploading file:", error);
}
} catch (err) {
if (err?.message?.includes("is encrypted")) {
try { try {
const response = await parseFile.save({ await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
}
try {
setIsDecrypting(true);
const size = files?.[0].size;
const name = generatePdfName(16);
const url = "https://ai.nxglabs.in/decryptpdf"; //
let formData = new FormData();
formData.append("file", files[0]);
formData.append("password", "");
const config = {
headers: { "content-type": "multipart/form-data" },
responseType: "blob"
};
const response = await axios.post(url, formData, config);
const pdfBlob = new Blob([response.data], {
type: "application/pdf"
});
const pdfFile = new File([pdfBlob], name, {
type: "application/pdf"
});
setIsDecrypting(false);
setfileload(true);
const res = await getFileAsArrayBuffer(pdfFile);
const flatPdf = await flattenPdf(res);
// Upload the file to Parse Server
const parseFile = new Parse.File(
name,
[...flatPdf],
"application/pdf"
);
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => { progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) { if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round( const percentCompleted = Math.round(
@@ -164,16 +234,16 @@ const Forms = (props) => {
} }
} }
}); });
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url() // Retrieve the URL of the uploaded file
if (response.url()) { if (parseFile.url()) {
const fileRes = await getSecureUrl(response.url()); const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) { if (fileRes.url) {
setFileUpload(fileRes.url); setFileUpload(fileRes.url);
setfileload(false); removeFile();
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(files?.[0]?.name); const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title })); setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId); SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url; return fileRes.url;
} else { } else {
@@ -182,95 +252,22 @@ const Forms = (props) => {
} else { } else {
removeFile(e); removeFile(e);
} }
} catch (error) { } catch (err) {
removeFile(e); removeFile();
console.error("Error uploading file:", error); if (err?.response?.status === 401) {
} setIsPassword(true);
} catch (err) { } else {
if (err?.message?.includes("is encrypted")) { console.log("Error uploading file: ", err?.response);
try {
await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
}
try {
setIsDecrypting(true);
const size = files?.[0].size;
const name = generatePdfName(16);
const url = "https://ai.nxglabs.in/decryptpdf"; //
let formData = new FormData();
formData.append("file", files[0]);
formData.append("password", "");
const config = {
headers: { "content-type": "multipart/form-data" },
responseType: "blob"
};
const response = await axios.post(url, formData, config);
const pdfBlob = new Blob([response.data], {
type: "application/pdf"
});
const pdfFile = new File([pdfBlob], name, {
type: "application/pdf"
});
setIsDecrypting(false); setIsDecrypting(false);
setfileload(true); e.target.value = "";
const res = await getFileAsArrayBuffer(pdfFile);
const flatPdf = await flattenPdf(res);
// Upload the file to Parse Server
const parseFile = new Parse.File(
name,
[...flatPdf],
"application/pdf"
);
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
removeFile();
const title = generateTitleFromFilename(
files?.[0]?.name
);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
removeFile(e);
}
} else {
removeFile(e);
}
} catch (err) {
removeFile();
if (err?.response?.status === 401) {
setIsPassword(true);
} else {
console.log("Error uploading file: ", err?.response);
setIsDecrypting(false);
e.target.value = "";
}
} }
} else {
console.log("err ", err);
setFileUpload("");
removeFile(e);
} }
} else {
console.log("err ", err);
setFileUpload("");
removeFile(e);
} }
}
} else { } else {
const isImage = files?.[0]?.type.includes("image/"); const isImage = files?.[0]?.type.includes("image/");
if (isImage) { if (isImage) {
@@ -295,50 +292,50 @@ const Forms = (props) => {
}); });
const size = files?.[0]?.size; const size = files?.[0]?.size;
const name = generatePdfName(16); const name = generatePdfName(16);
const getFile = await pdfDoc.save({ const getFile = await pdfDoc.save({
useObjectStreams: false useObjectStreams: false
}); });
setfileload(true); setfileload(true);
const pdfName = `${name?.split(".")[0]}.pdf`; const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File( const parseFile = new Parse.File(
pdfName, pdfName,
[...getFile], [...getFile],
"application/pdf" "application/pdf"
); );
try { try {
const response = await parseFile.save({ const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => { progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) { if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round( const percentCompleted = Math.round(
(loaded * 100) / total (loaded * 100) / total
); );
setpercentage(percentCompleted); setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
if (response.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
removeFile(e);
} }
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
if (response.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else { } else {
removeFile(e); removeFile(e);
} }
} catch (error) { } else {
removeFile(e); removeFile(e);
console.error("Error uploading file:", error);
} }
} catch (error) {
removeFile(e);
console.error("Error uploading file:", error);
}
} }
} }
} }
@@ -401,11 +398,11 @@ const Forms = (props) => {
object.set("RemindOnceInEvery", remindOnceInEvery); object.set("RemindOnceInEvery", remindOnceInEvery);
object.set("IsTourEnabled", isTourEnabled); object.set("IsTourEnabled", isTourEnabled);
object.set("TimeToCompleteDays", TimeToCompleteDays); object.set("TimeToCompleteDays", TimeToCompleteDays);
object.set("AllowModifications", false); object.set("AllowModifications", false);
object.set("IsEnableOTP", false); object.set("IsEnableOTP", false);
if (formData.NotifyOnSignatures !== undefined) { if (formData.NotifyOnSignatures !== undefined) {
object.set("NotifyOnSignatures", formData.NotifyOnSignatures); object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
} }
if (formData?.RedirectUrl) { if (formData?.RedirectUrl) {
object.set("RedirectUrl", formData.RedirectUrl); object.set("RedirectUrl", formData.RedirectUrl);
} }
@@ -444,10 +441,9 @@ const Forms = (props) => {
setSigners([]); setSigners([]);
setBcc([]); setBcc([]);
setFolder({ ObjectId: "", Name: "" }); setFolder({ ObjectId: "", Name: "" });
const notifySign = const notifySign = extUserData?.NotifyOnSignatures
extUserData?.NotifyOnSignatures ? extUserData?.NotifyOnSignatures
? extUserData?.NotifyOnSignatures : true;
: true;
setFormData({ setFormData({
Name: "", Name: "",
Description: "", Description: "",
@@ -523,10 +519,9 @@ const Forms = (props) => {
setSigners([]); setSigners([]);
setBcc([]); setBcc([]);
setFolder({ ObjectId: "", Name: "" }); setFolder({ ObjectId: "", Name: "" });
const notifySign = const notifySign = extUserData?.NotifyOnSignatures
extUserData?.NotifyOnSignatures ? extUserData?.NotifyOnSignatures
? extUserData?.NotifyOnSignatures : true;
: true;
let obj = { let obj = {
Name: "", Name: "",
Description: "", Description: "",
@@ -580,36 +575,28 @@ const Forms = (props) => {
type: "application/pdf" type: "application/pdf"
}); });
setIsDecrypting(false); setIsDecrypting(false);
const res = await getFileAsArrayBuffer(pdfFile); const res = await getFileAsArrayBuffer(pdfFile);
const flatPdf = await flattenPdf(res); const flatPdf = await flattenPdf(res);
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf"); const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
await parseFile.save({ await parseFile.save({
progress: (progressValue, loaded, total, { type }) => { progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) { if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total); const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted); setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
removeFile();
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, password: "", Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
removeFile();
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
} }
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
removeFile();
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, password: "", Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else { } else {
removeFile(); removeFile();
setFormData((prev) => ({ ...prev, password: "" })); setFormData((prev) => ({ ...prev, password: "" }));
@@ -618,6 +605,14 @@ const Forms = (props) => {
inputFileRef.current.value = ""; // Set file input value to empty string inputFileRef.current.value = ""; // Set file input value to empty string
} }
} }
} else {
removeFile();
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
} catch (err) { } catch (err) {
removeFile(); removeFile();
if (err?.response?.status === 401) { if (err?.response?.status === 401) {
@@ -739,9 +734,7 @@ const Forms = (props) => {
)} )}
<div className="text-xs"> <div className="text-xs">
<label className="block"> <label className="block">
{`${`${t("report-heading.File")} (${t("file-type")}`}${ {`${`${t("report-heading.File")} (${t("file-type")}`}${")"}`}
")"
}`}
<span className="text-red-500 text-[13px]">*</span> <span className="text-red-500 text-[13px]">*</span>
</label> </label>
{fileupload.length > 0 ? ( {fileupload.length > 0 ? (
@@ -765,9 +758,7 @@ const Forms = (props) => {
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={(e) => handleFileInput(e)} onChange={(e) => handleFileInput(e)}
ref={inputFileRef} ref={inputFileRef}
accept={ accept={"application/pdf,image/png,image/jpeg"}
"application/pdf,image/png,image/jpeg"
}
onInvalid={(e) => onInvalid={(e) =>
e.target.setCustomValidity(t("input-required")) e.target.setCustomValidity(t("input-required"))
} }
@@ -1057,10 +1048,7 @@ const Forms = (props) => {
{isAdvanceOpt && ( {isAdvanceOpt && (
<div <div
style={{ style={{
height: height: props.title === "New Template" ? "100px" : "280px"
props.title === "New Template"
? "100px"
: "280px"
}} }}
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block" className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
></div> ></div>
@@ -1169,11 +1157,7 @@ const Forms = (props) => {
</Tooltip> </Tooltip>
</label> </label>
<div className="flex flex-col md:flex-row md:gap-4"> <div className="flex flex-col md:flex-row md:gap-4">
<div <div className={`flex items-center gap-2 ml-2 mb-1`}>
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -1182,11 +1166,7 @@ const Forms = (props) => {
/> />
<div className="text-center">{t("yes")}</div> <div className="text-center">{t("yes")}</div>
</div> </div>
<div <div className={`flex items-center gap-2 ml-2 mb-1`}>
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
+41 -79
View File
@@ -69,8 +69,7 @@ import AddContact from "../primitives/AddContact";
function PlaceHolderSign() { function PlaceHolderSign() {
const { t } = useTranslation(); const { t } = useTranslation();
const copyUrlRef = useRef(null); const copyUrlRef = useRef(null);
const appName = const appName = "OpenSign™";
"OpenSign™";
const editorRef = useRef(); const editorRef = useRef();
const { state } = useLocation(); const { state } = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -864,11 +863,7 @@ function PlaceHolderSign() {
scale scale
); );
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile( const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, "");
pdfName,
pdfBase64,
"",
);
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBase64); const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId); SaveFileSize(buffer.length, pdfUrl, tenantId);
@@ -880,11 +875,7 @@ function PlaceHolderSign() {
} else if (pdfBase64Url) { } else if (pdfBase64Url) {
try { try {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile( const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
pdfName,
pdfBase64Url,
"",
);
return pdfUrl; return pdfUrl;
} catch (err) { } catch (err) {
console.log("error to convertBase64ToFile in placeholder flow", err); console.log("error to convertBase64ToFile in placeholder flow", err);
@@ -990,11 +981,7 @@ function PlaceHolderSign() {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile( pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
pdfName,
pdfBase64Url,
"",
);
} }
try { try {
const docCls = new Parse.Object("contracts_Document"); const docCls = new Parse.Object("contracts_Document");
@@ -1153,8 +1140,7 @@ function PlaceHolderSign() {
year: "numeric" year: "numeric"
}); });
let senderEmail = let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
pdfDetails?.[0]?.ExtUserPtr?.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
let signerMail = signersdata.slice(); let signerMail = signersdata.slice();
@@ -1180,16 +1166,11 @@ function PlaceHolderSign() {
const orgName = pdfDetails[0]?.ExtUserPtr.Company const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company
: ""; : "";
const senderName = const senderName = pdfDetails?.[0].ExtUserPtr.Name;
pdfDetails?.[0].ExtUserPtr.Name;
const documentName = `${pdfDetails?.[0].Name}`; const documentName = `${pdfDetails?.[0].Name}`;
let replaceVar; let replaceVar;
if ( if (requestBody && requestSubject && isCustomize) {
requestBody &&
requestSubject &&
isCustomize
) {
const replacedRequestBody = requestBody.replace(/"/g, "'"); const replacedRequestBody = requestBody.replace(/"/g, "'");
htmlReqBody = htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
@@ -1213,10 +1194,7 @@ function PlaceHolderSign() {
htmlReqBody, htmlReqBody,
variables variables
); );
} else if ( } else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
tenantMailTemplate?.body &&
tenantMailTemplate?.subject
) {
const mailBody = tenantMailTemplate?.body; const mailBody = tenantMailTemplate?.body;
const mailSubject = tenantMailTemplate?.subject; const mailSubject = tenantMailTemplate?.subject;
const replacedRequestBody = mailBody.replace(/"/g, "'"); const replacedRequestBody = mailBody.replace(/"/g, "'");
@@ -1253,8 +1231,7 @@ function PlaceHolderSign() {
? replaceVar?.subject ? replaceVar?.subject
: mailTemplate(mailparam).subject, : mailTemplate(mailparam).subject,
replyto: senderEmail, replyto: senderEmail,
from: from: senderEmail,
senderEmail,
html: replaceVar?.body html: replaceVar?.body
? replaceVar?.body ? replaceVar?.body
: mailTemplate(mailparam).body : mailTemplate(mailparam).body
@@ -1269,20 +1246,13 @@ function PlaceHolderSign() {
setMailStatus("success"); setMailStatus("success");
try { try {
let data; let data;
if ( if (requestBody && requestSubject && isCustomize) {
requestBody &&
requestSubject &&
isCustomize
) {
data = { data = {
RequestBody: htmlReqBody, RequestBody: htmlReqBody,
RequestSubject: requestSubject, RequestSubject: requestSubject,
SendMail: true SendMail: true
}; };
} else if ( } else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
tenantMailTemplate?.body &&
tenantMailTemplate?.subject
) {
data = { data = {
RequestBody: tenantMailTemplate?.body, RequestBody: tenantMailTemplate?.body,
RequestSubject: tenantMailTemplate?.subject, RequestSubject: tenantMailTemplate?.subject,
@@ -1320,8 +1290,7 @@ function PlaceHolderSign() {
setIsSend(true); setIsSend(true);
setIsMailSend(true); setIsMailSend(true);
setIsUiLoading(false); setIsUiLoading(false);
} } else {
else {
setMailStatus("failed"); setMailStatus("failed");
setIsSend(true); setIsSend(true);
setIsMailSend(true); setIsMailSend(true);
@@ -1562,8 +1531,7 @@ function PlaceHolderSign() {
status: defaultdata?.status || "required", status: defaultdata?.status || "required",
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: validation: {},
{},
fontSize: fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12, fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor: fontColor:
@@ -2038,30 +2006,26 @@ function PlaceHolderSign() {
{!isCustomize && ( {!isCustomize && (
<span>{t("placeholder-alert-3")}</span> <span>{t("placeholder-alert-3")}</span>
)} )}
{ {isCustomize && (
isCustomize && ( <>
<> <EmailBody
<EmailBody editorRef={editorRef}
editorRef={editorRef} requestBody={requestBody}
requestBody={requestBody} requestSubject={requestSubject}
requestSubject={requestSubject} handleOnchangeRequest={handleOnchangeRequest}
handleOnchangeRequest={ setRequestSubject={setRequestSubject}
handleOnchangeRequest />
} <div
setRequestSubject={setRequestSubject} className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
/> onClick={() => {
<div setRequestBody(defaultBody);
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary" setRequestSubject(defaultSubject);
onClick={() => { }}
setRequestBody(defaultBody); >
setRequestSubject(defaultSubject); <span>{t("reset-to-default")}</span>
}} </div>
> </>
<span>{t("reset-to-default")}</span> )}
</div>
</>
)
}
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2"> <div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<button <button
@@ -2079,16 +2043,14 @@ function PlaceHolderSign() {
</button> </button>
)} )}
</div> </div>
{ {!isCustomize && (
!isCustomize && ( <span
<span className="op-link op-link-accent text-sm"
className="op-link op-link-accent text-sm" onClick={() => setIsCustomize(!isCustomize)}
onClick={() => setIsCustomize(!isCustomize)} >
> {t("cutomize-email")}
{t("cutomize-email")} </span>
</span> )}
)
}
</div> </div>
</> </>
)} )}
+24 -19
View File
@@ -22,8 +22,7 @@ import EditorToolbar, {
} from "../components/pdf/EditorToolbar"; } from "../components/pdf/EditorToolbar";
const Preferences = () => { const Preferences = () => {
const appName = const appName = "OpenSign™";
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const editorRef = useRef(); const editorRef = useRef();
const editorRefCom = useRef(); const editorRefCom = useRef();
@@ -342,7 +341,9 @@ const Preferences = () => {
onClick={() => setactiveTab(ind)} onClick={() => setactiveTab(ind)}
key={ind} key={ind}
role="tab" role="tab"
className={`${activeTab === ind ? "op-tab-active" : ""} op-tab text-xs md:text-base pb-2 md:pb-0 }`} className={`${
activeTab === ind ? "op-tab-active" : ""
} op-tab text-xs md:text-base pb-2 md:pb-0 }`}
> >
<i className={tabData.icon}></i> <i className={tabData.icon}></i>
<span className="ml-1 md:ml-2">{tabData.title}</span> <span className="ml-1 md:ml-2">{tabData.title}</span>
@@ -450,11 +451,7 @@ const Preferences = () => {
</ReactTooltip> </ReactTooltip>
</label> </label>
<div className="flex flex-col md:flex-row md:gap-4"> <div className="flex flex-col md:flex-row md:gap-4">
<div <div className={"flex items-center gap-2 ml-2 mb-1"}>
className={
"flex items-center gap-2 ml-2 mb-1"
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -465,11 +462,7 @@ const Preferences = () => {
{t("yes")} {t("yes")}
</div> </div>
</div> </div>
<div <div className={"flex items-center gap-2 ml-2 mb-1"}>
className={
"flex items-center gap-2 ml-2 mb-1"
}
>
<input <input
className="mr-[2px] op-radio op-radio-xs" className="mr-[2px] op-radio op-radio-xs"
type="radio" type="radio"
@@ -642,7 +635,9 @@ const Preferences = () => {
{t("subject")} {t("subject")}
<Tooltip <Tooltip
id={"request-sub-tooltip"} id={"request-sub-tooltip"}
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`} message={`${t(
"variables-use"
)}: {{sender_name}} {{document_title}}`}
/> />
</label> </label>
<input <input
@@ -651,7 +646,9 @@ const Preferences = () => {
onChange={(e) => onChange={(e) =>
setRequestSubject(e.target.value) setRequestSubject(e.target.value)
} }
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`} placeholder={`{{sender_name}} ${t(
"send-to-sign"
)} {{document_title}}`}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
/> />
</div> </div>
@@ -660,7 +657,9 @@ const Preferences = () => {
{t("body")} {t("body")}
<Tooltip <Tooltip
id={"request-body-tooltip"} id={"request-body-tooltip"}
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`} message={`${t(
"variables-use"
)}: {{sender_name}} {{document_title}}`}
/> />
</label> </label>
<EditorToolbar containerId="toolbar1" /> <EditorToolbar containerId="toolbar1" />
@@ -706,7 +705,9 @@ const Preferences = () => {
{t("subject")} {t("subject")}
<Tooltip <Tooltip
id={"complete-sub-tooltip"} id={"complete-sub-tooltip"}
message={`${t("variables-use")}:{{sender_name}} {{document_title}}`} message={`${t(
"variables-use"
)}:{{sender_name}} {{document_title}}`}
/> />
</label> </label>
<input <input
@@ -715,7 +716,9 @@ const Preferences = () => {
onChange={(e) => onChange={(e) =>
setCompletionSubject(e.target.value) setCompletionSubject(e.target.value)
} }
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`} placeholder={`{{sender_name}} ${t(
"send-to-sign"
)} {{document_title}}`}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
/> />
</div> </div>
@@ -724,7 +727,9 @@ const Preferences = () => {
{t("body")} {t("body")}
<Tooltip <Tooltip
id={"complete-body-tooltip"} id={"complete-body-tooltip"}
message={`${t("variables-use")}:{{sender_name}} {{document_title}} {{signing_url}}`} message={`${t(
"variables-use"
)}:{{sender_name}} {{document_title}} {{signing_url}}`}
/> />
</label> </label>
<EditorToolbar containerId="toolbar2" /> <EditorToolbar containerId="toolbar2" />
+5 -14
View File
@@ -66,8 +66,7 @@ import LoaderWithMsg from "../primitives/LoaderWithMsg";
function SignYourSelf() { function SignYourSelf() {
const { t } = useTranslation(); const { t } = useTranslation();
const { docId } = useParams(); const { docId } = useParams();
const appName = const appName = "OpenSign™";
"OpenSign™";
const divRef = useRef(null); const divRef = useRef(null);
const nodeRef = useRef(null); const nodeRef = useRef(null);
const imageRef = useRef(null); const imageRef = useRef(null);
@@ -562,11 +561,7 @@ function SignYourSelf() {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile( pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
pdfName,
pdfBase64Url,
"",
);
} }
const widgetsType = ["signature", "stamp", "image", "initials"]; const widgetsType = ["signature", "stamp", "image", "initials"];
let updatedXYPosition; let updatedXYPosition;
@@ -752,10 +747,7 @@ function SignYourSelf() {
if (tenantDetails && tenantDetails === "user does not exist!") { if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist")); alert(t("user-not-exist"));
} else { } else {
if ( if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject
) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
} }
@@ -791,7 +783,7 @@ function SignYourSelf() {
pdfFile: base64Url, pdfFile: base64Url,
docId: documentId, docId: documentId,
isCustomCompletionMail: isCustomCompletionMail, isCustomCompletionMail: isCustomCompletionMail,
signature: suffixbase64, signature: suffixbase64
}; };
const resSignPdf = await Parse.Cloud.run("signPdf", params); const resSignPdf = await Parse.Cloud.run("signPdf", params);
if (resSignPdf) { if (resSignPdf) {
@@ -1366,7 +1358,7 @@ function SignYourSelf() {
setCurrWidgetsDetails={setCurrWidgetsDetails} setCurrWidgetsDetails={setCurrWidgetsDetails}
/> />
)} )}
{/*render email component to send email after finish signature on document {/*render email component to send email after finish signature on document */}
<EmailComponent <EmailComponent
isEmail={isEmail} isEmail={isEmail}
setIsEmail={setIsEmail} setIsEmail={setIsEmail}
@@ -1375,7 +1367,6 @@ function SignYourSelf() {
setIsAlert={setIsAlert} setIsAlert={setIsAlert}
setIsDownloadModal={setIsDownloadModal} setIsDownloadModal={setIsDownloadModal}
/> />
*/}
{/* pdf header which contain funish back button */} {/* pdf header which contain funish back button */}
<Header <Header
pageNumber={pageNumber} pageNumber={pageNumber}
+4 -18
View File
@@ -165,7 +165,6 @@ const TemplatePlaceholder = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]); }, [divRef.current, isHeader]);
//function to fetch tenant Details //function to fetch tenant Details
const fetchTenantDetails = async () => { const fetchTenantDetails = async () => {
const user = JSON.parse( const user = JSON.parse(
@@ -778,11 +777,7 @@ const TemplatePlaceholder = () => {
let pdfUrl; let pdfUrl;
if (isUploadPdf) { if (isUploadPdf) {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
pdfUrl = await convertBase64ToFile( pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
pdfName,
pdfBase64Url,
"",
);
} }
if (signersdata?.length > 0) { if (signersdata?.length > 0) {
signersdata.forEach((x) => { signersdata.forEach((x) => {
@@ -849,11 +844,7 @@ const TemplatePlaceholder = () => {
scale scale
); );
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile( const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, "");
pdfName,
pdfBase64,
"",
);
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBase64); const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId); SaveFileSize(buffer.length, pdfUrl, tenantId);
@@ -865,11 +856,7 @@ const TemplatePlaceholder = () => {
} else if (pdfBase64Url) { } else if (pdfBase64Url) {
try { try {
const pdfName = generatePdfName(16); const pdfName = generatePdfName(16);
const pdfUrl = await convertBase64ToFile( const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
pdfName,
pdfBase64Url,
"",
);
return pdfUrl; return pdfUrl;
} catch (err) { } catch (err) {
console.log("error to convertBase64ToFile in placeholder flow", err); console.log("error to convertBase64ToFile in placeholder flow", err);
@@ -1469,8 +1456,7 @@ const TemplatePlaceholder = () => {
status: defaultdata?.status || "required", status: defaultdata?.status || "required",
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: validation: {},
{},
fontSize: fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12, fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor: fontColor:
+177 -187
View File
@@ -206,197 +206,187 @@ const UserList = () => {
</div> </div>
)} )}
{ {!isLoader && (
!isLoader && ( <>
<> {isAdmin ? (
{isAdmin ? ( <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> {isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{isAlert.msg && ( <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<Alert type={isAlert.type}>{isAlert.msg}</Alert> <div className="font-light">
)} {t("report-name.Users")}{" "}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <span className="text-xs md:text-[13px] font-normal">
<div className="font-light"> <Tooltip message={t("users-from-teams")} />
{t("report-name.Users")}{" "} </span>
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={t("users-from-teams")} />
</span>
</div>
<div className="flex flex-row gap-2 items-center">
<div
className="cursor-pointer"
onClick={() => handleModal("form")}
>
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
</div>
</div>
</div>
<div className="w-full overflow-x-auto">
<table className="op-table border-collapse w-full mb-[50px]">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<th key={index} className="px-4 py-2">
{t(`report-heading.${item}`)}
</th>
))}
</tr>
</thead>
{userList?.length > 0 && (
<tbody className="text-[12px]">
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
<td className="px-4 py-2 font-semibold">
{item?.Name}{" "}
</td>
<td className="px-4 py-2 ">
{item?.Email || "-"}
</td>
<td className="px-4 py-2">
{item?.Phone || "-"}
</td>
<td className="px-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<td className="px-4 py-2">
{formatRow(item.TeamIds)}
</td>
{item.UserRole !== "contracts_Admin" ? (
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsDisabled !== true}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={t("user-status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
{t("are-you-sure")}{" "}
{item?.IsDisabled
? t("activate")
: t("deactivate")}{" "}
{t("this-user")}?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() =>
handleToggleSubmit(item)
}
className="op-btn op-btn-primary"
>
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
{t("no")}
</button>
</div>
</div>
</ModalUi>
)}
</td>
) : (
<td className="px-4 py-2 font-semibold"></td>
)}
</tr>
))}
</tbody>
)}
</table>
</div>
<div className="flex flex-row justify-between items-center text-xs font-medium">
<div className="op-join flex flex-wrap items-center p-2">
{userList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
{t("prev")}
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{userList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
{t("next")}
</button>
)}
</div>
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
isOpen={isModal.form}
title={formHeader}
handleClose={() => handleModal("form")}
>
<AddUser
showAlert={showAlert}
handleUserData={handleUserData}
closePopup={() => handleModal("form")}
setFormHeader={setFormHeader}
/>
</ModalUi>
</div> </div>
) : ( <div className="flex flex-row gap-2 items-center">
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box"> <div
<div className="text-center"> className="cursor-pointer"
<h1 className="text-[60px] lg:text-[120px] font-semibold"> onClick={() => handleModal("form")}
404 >
</h1> <i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
<p className="text-[30px] lg:text-[50px]"> </div>
{t("page-not-found")} </div>
</p> </div>
<div className="w-full overflow-x-auto">
<table className="op-table border-collapse w-full mb-[50px]">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<th key={index} className="px-4 py-2">
{t(`report-heading.${item}`)}
</th>
))}
</tr>
</thead>
{userList?.length > 0 && (
<tbody className="text-[12px]">
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
</th>
)}
<td className="px-4 py-2 font-semibold">
{item?.Name}{" "}
</td>
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
<td className="px-4 py-2">{item?.Phone || "-"}</td>
<td className="px-4 py-2">
{item?.UserRole?.split("_").pop() || "-"}
</td>
<td className="px-4 py-2">
{formatRow(item.TeamIds)}
</td>
{item.UserRole !== "contracts_Admin" ? (
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<input
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
checked={item?.IsDisabled !== true}
onChange={() => handleToggleBtn(item)}
/>
</label>
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={t("user-status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
{t("are-you-sure")}{" "}
{item?.IsDisabled
? t("activate")
: t("deactivate")}{" "}
{t("this-user")}?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleToggleSubmit(item)}
className="op-btn op-btn-primary"
>
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
{t("no")}
</button>
</div>
</div>
</ModalUi>
)}
</td>
) : (
<td className="px-4 py-2 font-semibold"></td>
)}
</tr>
))}
</tbody>
)}
</table>
</div>
<div className="flex flex-row justify-between items-center text-xs font-medium">
<div className="op-join flex flex-wrap items-center p-2">
{userList.length > recordperPage && (
<button
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
{t("prev")}
</button>
)}
{pageNumbers.map((x, i) => (
<button
key={i}
onClick={() => setCurrentPage(x)}
disabled={x === "..."}
className={`${
x === currentPage ? "op-btn-active" : ""
} op-join-item op-btn op-btn-sm`}
>
{x}
</button>
))}
{userList.length > recordperPage && (
<button
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
{t("next")}
</button>
)}
</div>
</div>
{userList?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div> </div>
</div> </div>
)} )}
</> <ModalUi
) isOpen={isModal.form}
} title={formHeader}
handleClose={() => handleModal("form")}
>
<AddUser
showAlert={showAlert}
handleUserData={handleUserData}
closePopup={() => handleModal("form")}
setFormHeader={setFormHeader}
/>
</ModalUi>
</div>
) : (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold">
404
</h1>
<p className="text-[30px] lg:text-[50px]">
{t("page-not-found")}
</p>
</div>
</div>
)}
</>
)}
</div> </div>
); );
}; };
@@ -4,9 +4,7 @@ import { useLocation, useNavigate } from "react-router";
import axios from "axios"; import axios from "axios";
import ModalUi from "./ModalUi"; import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner"; import AddSigner from "../components/AddSigner";
import { import { emailRegex } from "../constant/const";
emailRegex,
} from "../constant/const";
import Alert from "./Alert"; import Alert from "./Alert";
import Tooltip from "./Tooltip"; import Tooltip from "./Tooltip";
import { RWebShare } from "react-web-share"; import { RWebShare } from "react-web-share";
@@ -39,8 +37,7 @@ import EditContactForm from "../components/EditContactForm";
const ReportTable = (props) => { const ReportTable = (props) => {
const copyUrlRef = useRef(null); const copyUrlRef = useRef(null);
const appName = const appName = "OpenSign™";
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -157,14 +154,14 @@ const ReportTable = (props) => {
const teamtRes = await Parse.Cloud.run("getteams", { active: true }); const teamtRes = await Parse.Cloud.run("getteams", { active: true });
if (teamtRes.length > 0) { if (teamtRes.length > 0) {
const _teamRes = JSON.parse(JSON.stringify(teamtRes)); const _teamRes = JSON.parse(JSON.stringify(teamtRes));
const selected = _teamRes.map( const selected = _teamRes.map(
(x) => (x) =>
x.Name === "All Users" && { x.Name === "All Users" && {
label: x.Name, label: x.Name,
value: x.objectId value: x.objectId
} }
); );
setSelectedTeam(selected); setSelectedTeam(selected);
} }
} }
} catch (err) { } catch (err) {
@@ -225,7 +222,7 @@ const ReportTable = (props) => {
const handleURL = async (item, act) => { const handleURL = async (item, act) => {
if (props.ReportName === "Templates") { if (props.ReportName === "Templates") {
if (act.hoverLabel === "Edit") { if (act.hoverLabel === "Edit") {
navigate(`/${act.redirectUrl}/${item.objectId}`); navigate(`/${act.redirectUrl}/${item.objectId}`);
} else { } else {
setActLoader({ [`${item.objectId}_${act.btnId}`]: true }); setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
try { try {
@@ -411,8 +408,7 @@ const ReportTable = (props) => {
setSelectedTeam(formatedList); setSelectedTeam(formatedList);
} }
setIsShareWith({ [item.objectId]: true }); setIsShareWith({ [item.objectId]: true });
} } else if (act.action === "duplicate") {
else if (act.action === "duplicate") {
setIsModal({ [`duplicate_${item.objectId}`]: true }); setIsModal({ [`duplicate_${item.objectId}`]: true });
} else if (act.action === "rename") { } else if (act.action === "rename") {
setIsModal({ [`rename_${item.objectId}`]: true }); setIsModal({ [`rename_${item.objectId}`]: true });
@@ -496,8 +492,7 @@ const ReportTable = (props) => {
setActLoader({}); setActLoader({});
} }
}; };
const handleClose = ( const handleClose = () => {
) => {
setIsRevoke({}); setIsRevoke({});
setIsDeleteModal({}); setIsDeleteModal({});
setReason(""); setReason("");
@@ -656,11 +651,7 @@ const ReportTable = (props) => {
if (isCompleted) { if (isCompleted) {
setIsDownloadModal({ [item.objectId]: true }); setIsDownloadModal({ [item.objectId]: true });
} else { } else {
const signedUrl = await getSignedUrl( const signedUrl = await getSignedUrl(url, docId, templateId);
url,
docId,
templateId
);
await fetchUrl(signedUrl, pdfName); await fetchUrl(signedUrl, pdfName);
} }
setActLoader({}); setActLoader({});
@@ -687,10 +678,8 @@ const ReportTable = (props) => {
const signPdf = `${window.location.origin}/login/${encodeBase64}`; const signPdf = `${window.location.origin}/login/${encodeBase64}`;
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
sender_name: sender_name: doc.ExtUserPtr.Name,
doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails?.Name || "", receiver_name: userDetails?.Name || "",
receiver_email: userDetails?.Email, receiver_email: userDetails?.Email,
@@ -718,10 +707,8 @@ const ReportTable = (props) => {
const signPdf = `${window.location.origin}/login/${encodeBase64}`; const signPdf = `${window.location.origin}/login/${encodeBase64}`;
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
sender_name: sender_name: doc.ExtUserPtr.Name,
doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails?.Name || "", receiver_name: userDetails?.Name || "",
receiver_email: userDetails?.Email || "", receiver_email: userDetails?.Email || "",
@@ -761,10 +748,8 @@ const ReportTable = (props) => {
const signPdf = `${window.location.origin}/login/${encodeBase64}`; const signPdf = `${window.location.origin}/login/${encodeBase64}`;
const variables = { const variables = {
document_title: doc.Name, document_title: doc.Name,
sender_name: sender_name: doc.ExtUserPtr.Name,
doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email,
sender_mail:
doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: user?.signerPtr?.Name || "", receiver_name: user?.signerPtr?.Name || "",
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email, receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
@@ -794,14 +779,11 @@ const ReportTable = (props) => {
sessionToken: localStorage.getItem("accesstoken") sessionToken: localStorage.getItem("accesstoken")
}; };
let params = { let params = {
replyto: replyto: doc?.ExtUserPtr?.Email || "",
doc?.ExtUserPtr?.Email ||
"",
extUserId: doc?.ExtUserPtr?.objectId, extUserId: doc?.ExtUserPtr?.objectId,
recipient: userDetails?.Email, recipient: userDetails?.Email,
subject: mail.subject, subject: mail.subject,
from: from: doc?.ExtUserPtr?.Email,
doc?.ExtUserPtr?.Email,
html: mail.body html: mail.body
}; };
try { try {
@@ -810,8 +792,7 @@ const ReportTable = (props) => {
setIsAlert(true); setIsAlert(true);
setAlertMsg({ type: "success", message: t("mail-sent-alert") }); setAlertMsg({ type: "success", message: t("mail-sent-alert") });
setIsResendMail({}); setIsResendMail({});
} } else {
else {
setIsAlert(true); setIsAlert(true);
setAlertMsg({ setAlertMsg({
type: "danger", type: "danger",
@@ -1745,11 +1726,7 @@ const ReportTable = (props) => {
<i className={act.btnIcon}></i> <i className={act.btnIcon}></i>
{act.btnLabel && ( {act.btnLabel && (
<span className="uppercase font-medium"> <span className="uppercase font-medium">
{ {`${t(`btnLabel.${act.btnLabel}`)}`}
`${t(
`btnLabel.${act.btnLabel}`
)}`
}
</span> </span>
)} )}
{/* template report */} {/* template report */}
@@ -1829,7 +1806,9 @@ const ReportTable = (props) => {
title={t(`btnLabel.${act.hoverLabel}`)} title={t(`btnLabel.${act.hoverLabel}`)}
className={ className={
act.action !== "option" act.action !== "option"
? `${act?.btnColor || ""} op-btn op-btn-sm mr-1` ? `${
act?.btnColor || ""
} op-btn op-btn-sm mr-1`
: "text-base-content focus:outline-none text-lg mr-2 relative" : "text-base-content focus:outline-none text-lg mr-2 relative"
} }
> >
@@ -1916,28 +1895,26 @@ const ReportTable = (props) => {
{isShareWith[item.objectId] && ( {isShareWith[item.objectId] && (
<div className="op-modal op-modal-open"> <div className="op-modal op-modal-open">
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative"> <div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{t("share-with")} {t("share-with")}
</h3> </h3>
<div <div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40" className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
onClick={() => setIsShareWith({})} onClick={() => setIsShareWith({})}
> >
</div> </div>
<div className="px-2 mt-3 w-full h-full"> <div className="px-2 mt-3 w-full h-full">
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all"> <div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
{selectedTeam?.[0]?.label} {selectedTeam?.[0]?.label}
</div> </div>
</div> </div>
<button <button
onClick={(e) => onClick={(e) => handleShareWith(e, item)}
handleShareWith(e, item) className="op-btn op-btn-primary ml-[10px] my-3"
} >
className="op-btn op-btn-primary ml-[10px] my-3" {t("submit")}
> </button>
{t("submit")}
</button>
</div> </div>
</div> </div>
)} )}
@@ -2030,9 +2007,7 @@ const ReportTable = (props) => {
{isBulkSend[item.objectId] && ( {isBulkSend[item.objectId] && (
<ModalUi <ModalUi
isOpen isOpen
title={ title={t("quick-send")}
t("quick-send")
}
handleClose={() => setIsBulkSend({})} handleClose={() => setIsBulkSend({})}
> >
{isLoader[item.objectId] ? ( {isLoader[item.objectId] ? (
+3 -7
View File
@@ -5,8 +5,7 @@ import { PDFDocument } from 'pdf-lib';
dotenv.config(); dotenv.config();
export const cloudServerUrl = 'http://localhost:8080/app'; export const cloudServerUrl = 'http://localhost:8080/app';
export const appName = export const appName = 'OpenSign™';
'OpenSign™';
export function customAPIurl() { export function customAPIurl() {
const url = new URL(cloudServerUrl); const url = new URL(cloudServerUrl);
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin; return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
@@ -151,7 +150,6 @@ export const smtpsecure = process.env.SMTP_PORT && process.env.SMTP_PORT !== '46
export const smtpenable = export const smtpenable =
process.env.SMTP_ENABLE && process.env.SMTP_ENABLE.toLowerCase() === 'true' ? true : false; process.env.SMTP_ENABLE && process.env.SMTP_ENABLE.toLowerCase() === 'true' ? true : false;
// `generateId` is used to unique Id for fileAdapter // `generateId` is used to unique Id for fileAdapter
export function generateId(length) { export function generateId(length) {
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
@@ -231,11 +229,9 @@ export const flattenPdf = async pdfFile => {
export const mailTemplate = param => { export const mailTemplate = param => {
const themeColor = '#47a3ad'; const themeColor = '#47a3ad';
const subject = `${param.senderName} has requested you to sign "${param.title}"`; const subject = `${param.senderName} has requested you to sign "${param.title}"`;
const logo = const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`;
`<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`;
const opurl = const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
` <a href='www.opensignlabs.com' target=_blank>here</a>`;
const body = const body =
"<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'><div style='padding:10px'>" + "<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'><div style='padding:10px'>" +
-2
View File
@@ -1,4 +1,3 @@
import PDF from './parsefunction/pdf/PDF.js'; import PDF from './parsefunction/pdf/PDF.js';
import sendmailv3 from './parsefunction/sendMailv3.js'; import sendmailv3 from './parsefunction/sendMailv3.js';
import usersignup from './parsefunction/usersignup.js'; import usersignup from './parsefunction/usersignup.js';
@@ -53,7 +52,6 @@ import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import editContact from './parsefunction/editContact.js'; import editContact from './parsefunction/editContact.js';
import forwardDoc from './parsefunction/ForwardDoc.js'; import forwardDoc from './parsefunction/ForwardDoc.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
@@ -28,20 +28,15 @@ export default async function forwardDoc(request) {
const docName = _docRes.Name; const docName = _docRes.Name;
const fileAdapterId = _docRes?.FileAdapterId || ''; const fileAdapterId = _docRes?.FileAdapterId || '';
const extUserId = _docRes?.ExtUserPtr?.objectId; const extUserId = _docRes?.ExtUserPtr?.objectId;
const from = const from = _docRes?.ExtUserPtr?.Email;
_docRes?.ExtUserPtr?.Email; const replyTo = _docRes?.ExtUserPtr?.Email;
const replyTo = const senderName = _docRes?.ExtUserPtr?.Name;
_docRes?.ExtUserPtr?.Email;
const senderName =
_docRes?.ExtUserPtr?.Name;
try { try {
let mailRes; let mailRes;
for (let i = 0; i < recipients.length; i++) { for (let i = 0; i < recipients.length; i++) {
const logo = const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>`;
`<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>`; const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
const opurl =
` <a href='www.opensignlabs.com' target=_blank>here</a>`;
const themeColor = '#47a3ad'; const themeColor = '#47a3ad';
let params = { let params = {
@@ -4,7 +4,7 @@ export default async function Newsletter(request) {
const email = request.params.email; const email = request.params.email;
const domain = request.params.domain; const domain = request.params.domain;
try { try {
const envAppId = process.env.REACT_APP_APPID || 'legadranaxn'; const envAppId = process.env.REACT_APP_APPID || 'opensign';
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId }; const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId };
const envProdServer = process.env.REACT_APP_SERVERURL || 'https://app.opensignlabs.com/api/app'; const envProdServer = process.env.REACT_APP_SERVERURL || 'https://app.opensignlabs.com/api/app';
const newsletter = await axios.post( const newsletter = await axios.post(
@@ -12,17 +12,14 @@ const eSignName = 'OpenSign';
const eSigncontact = 'hello@opensignlabs.com'; const eSigncontact = 'hello@opensignlabs.com';
// `uploadFile` is used to create url in from pdfFile // `uploadFile` is used to create url in from pdfFile
async function uploadFile( async function uploadFile(pdfName, filepath) {
pdfName,
filepath,
) {
try { try {
const filedata = fs.readFileSync(filepath); const filedata = fs.readFileSync(filepath);
let fileUrl; let fileUrl;
const file = new Parse.File(pdfName, [...filedata], 'application/pdf'); const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
await file.save({ useMasterKey: true }); await file.save({ useMasterKey: true });
const fileRes = getSecureUrl(file.url()); const fileRes = getSecureUrl(file.url());
fileUrl = fileRes.url; fileUrl = fileRes.url;
return { imageUrl: fileUrl }; return { imageUrl: fileUrl };
} catch (err) { } catch (err) {
console.log('Err ', err); console.log('Err ', err);
@@ -86,10 +83,7 @@ export default async function generateCertificatebydocId(req) {
//below is used to save signed certificate in exports folder //below is used to save signed certificate in exports folder
fs.writeFileSync(certificatePath, signedCertificate); fs.writeFileSync(certificatePath, signedCertificate);
const file = await uploadFile( const file = await uploadFile('certificate.pdf', certificatePath);
'certificate.pdf',
certificatePath,
);
const updateDoc = new Parse.Object('contracts_Document'); const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = doc.objectId; updateDoc.id = doc.objectId;
updateDoc.set('CertificateUrl', file.imageUrl); updateDoc.set('CertificateUrl', file.imageUrl);
@@ -9,28 +9,29 @@ import {
appName, appName,
} from '../../../Utils.js'; } from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js'; import GenerateCertificate from './GenerateCertificate.js';
import DigitalSign, { retryAsync } from './DigitalSign.js'; import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { Placeholder } from './Placeholder.js';
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
const serverUrl = cloudServerUrl; // process.env.SERVER_URL; const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
const APPID = process.env.APP_ID; const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY; const masterKEY = process.env.MASTER_KEY;
const eSignName = 'OpenSign'; const eSignName = 'OpenSign';
const eSigncontact = 'hello@opensignlabs.com';
const logo = const logo =
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>"; "<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
const opurl = const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`;
` <a href=www.opensignlabs.com target=_blank>here</a>`;
// `updateDoc` is used to create url in from pdfFile // `updateDoc` is used to create url in from pdfFile
async function uploadFile( async function uploadFile(pdfName, filepath) {
pdfName,
filepath,
) {
try { try {
const filedata = fs.readFileSync(filepath); const filedata = fs.readFileSync(filepath);
let fileUrl; let fileUrl;
const file = new Parse.File(pdfName, [...filedata], 'application/pdf'); const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
await file.save({ useMasterKey: true }); await file.save({ useMasterKey: true });
const fileRes = getSecureUrl(file.url()); const fileRes = getSecureUrl(file.url());
fileUrl = fileRes.url; fileUrl = fileRes.url;
return { imageUrl: fileUrl }; return { imageUrl: fileUrl };
} catch (err) { } catch (err) {
@@ -195,8 +196,7 @@ async function sendCompletedMail(obj) {
const variables = { const variables = {
document_title: pdfName, document_title: pdfName,
sender_name: sender_name: sender.Name,
sender.Name,
sender_mail: doc?.SenderMail || sender.Email, sender_mail: doc?.SenderMail || sender.Email,
sender_phone: sender?.Phone || '', sender_phone: sender?.Phone || '',
receiver_name: sender.Name, receiver_name: sender.Name,
@@ -213,11 +213,8 @@ async function sendCompletedMail(obj) {
const params = { const params = {
extUserId: sender.objectId, extUserId: sender.objectId,
url: url, url: url,
from: from: appName,
appName, replyto: doc?.ExtUserPtr?.Email || '',
replyto:
doc?.ExtUserPtr?.Email ||
'',
recipient: recipient, recipient: recipient,
subject: subject, subject: subject,
pdfName: pdfName, pdfName: pdfName,
@@ -244,82 +241,48 @@ async function sendCompletedMail(obj) {
} }
} }
// `sendMailsaveCertifcate` is used send completion mail and update complete status of document // `sendMailsaveCertifcate` is used send completion mail and update complete status of document
async function sendMailsaveCertifcate( async function sendMailsaveCertifcate(doc, pfx, isCustomMail, mailProvider, filename) {
doc,
pfx,
isCustomMail,
mailProvider,
filename,
) {
const certificate = await GenerateCertificate(doc); const certificate = await GenerateCertificate(doc);
const certificatePdf = await PDFDocument.load(certificate); const certificatePdf = await PDFDocument.load(certificate);
const P12Buffer = fs.readFileSync(pfx.name);
const p12 = new P12Signer(P12Buffer, { passphrase: pfx.passphrase || null });
// `pdflibAddPlaceholder` is used to add code of only digitial sign in certificate
pdflibAddPlaceholder({
pdfDoc: certificatePdf,
reason: `Digitally signed by ${eSignName}.`,
location: 'n/a',
name: eSignName,
contactInfo: eSigncontact,
signatureLength: 15000,
});
const pdfWithPlaceholderBytes = await certificatePdf.save(); const pdfWithPlaceholderBytes = await certificatePdf.save();
const CertificateBuffer = Buffer.from(pdfWithPlaceholderBytes); const CertificateBuffer = Buffer.from(pdfWithPlaceholderBytes);
//`new signPDF` create new instance of CertificateBuffer and p12Buffer
const certificateOBJ = new SignPdf();
// `signedCertificate` is used to sign certificate digitally
const signedCertificate = await certificateOBJ.sign(CertificateBuffer, p12);
const certificatePath = `./exports/signed_certificate_${doc.objectId}.pdf`;
let certificatePath = `./exports/certificate_${doc.objectId}.pdf`;
//below is used to save signed certificate in exports folder //below is used to save signed certificate in exports folder
fs.writeFileSync(certificatePath, CertificateBuffer); fs.writeFileSync(certificatePath, signedCertificate);
const details = { reason: `Digitally signed by ${eSignName}.`, location: 'n/a', name: eSignName }; const file = await uploadFile('certificate.pdf', certificatePath);
const body = { CertificateUrl: file.imageUrl };
const res = await retryAsync(DigitalSign, [certificatePath, pfx, details]); await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
// console.log('res', res); headers: {
// const res = await DigitalSign(certificatePath, pfx, details); 'Content-Type': 'application/json',
if (res) { 'X-Parse-Application-Id': APPID,
const CreatedBy = { 'X-Parse-Master-Key': masterKEY,
__type: 'Pointer', },
className: '_User', });
objectId: doc.CreatedBy.objectId, // used in API only
}; if (doc.IsSendMail === false) {
let logbody = { console.log("don't send mail");
documentId: doc.objectId, } else {
attempt: res.attempt, sendCompletedMail({ isCustomMail, doc, mailProvider, filename });
createdBy: CreatedBy,
email: doc.ExtUserPtr.Email,
};
if (fs.existsSync(certificatePath)) {
fs.unlinkSync(certificatePath);
certificatePath = `./exports/signed_certificate_${doc.objectId}.pdf`;
const file = await uploadFile(
'certificate.pdf',
certificatePath,
);
const body = { CertificateUrl: file.imageUrl };
await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
// used in API only
if (doc.IsSendMail === false) {
console.log("don't send mail");
} else {
sendCompletedMail({ isCustomMail, doc, mailProvider, filename });
}
saveFileUsage(CertificateBuffer.length, file.imageUrl, doc?.CreatedBy?.objectId);
fs.unlinkSync(pfx.name);
if (res.attempt > 1) {
logbody = { ...logbody, status: 'success' };
axios.post(`${cloudServerUrl}/classes/logs_SignRetry`, logbody, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
});
}
} else {
fs.unlinkSync(certificatePath);
fs.unlinkSync(pfx.name);
const error = new Error('certificate: ' + res.error);
error.code = 400; // Set the error code (e.g., 400 for bad request)
logbody = { ...logbody, status: 'failure', stackTrace: 'certificate: ' + res.error };
axios.post(`${cloudServerUrl}/classes/logs_SignRetry`, logbody, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
});
throw error;
}
} }
saveFileUsage(CertificateBuffer.length, file.imageUrl, doc?.CreatedBy?.objectId);
fs.unlinkSync(pfx.name);
} }
/** /**
* *
@@ -420,54 +383,27 @@ async function PDF(req) {
form.updateFieldAppearances(); form.updateFieldAppearances();
// Flattens the form, converting all form fields into non-editable, static content // Flattens the form, converting all form fields into non-editable, static content
form.flatten(); form.flatten();
const pdfWithPlaceholderBytes = await pdfDoc.save(); const p12Cert = new P12Signer(P12Buffer, { passphrase: passphrase || null });
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes); signedFilePath = `./exports/signed_${name}`;
Placeholder({
// `saveUrl` is used to save signed pdf in exports folder pdfDoc: pdfDoc,
fs.writeFileSync(filePath, PdfBuffer);
const details = {
reason: `Digitally signed by ${eSignName} for ${reason}`, reason: `Digitally signed by ${eSignName} for ${reason}`,
location: 'n/a', location: 'n/a',
name: eSignName, name: eSignName,
}; contactInfo: eSigncontact,
const res = await retryAsync(DigitalSign, [filePath, pfx, details]); signatureLength: 15000,
// const res = await DigitalSign(filePath, pfx, details); });
// console.log('res', res); const pdfWithPlaceholderBytes = await pdfDoc.save();
if (res) { PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
signedFilePath = `./exports/signed_${name}`; //`new signPDF` create new instance of pdfBuffer and p12Buffer
const CreatedBy = { const OBJ = new SignPdf();
__type: 'Pointer', // `signedDocs` is used to signpdf digitally
className: '_User', const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
objectId: _resDoc.CreatedBy.objectId,
}; //`saveUrl` is used to save signed pdf in exports folder
let logbody = { fs.writeFileSync(signedFilePath, signedDocs);
documentId: _resDoc.objectId, pdfSize = signedDocs.length;
attempt: res.attempt, console.log(`✅ PDF digitally signed created: ${signedFilePath} \n`);
createdBy: CreatedBy,
email: _resDoc.ExtUserPtr.Email,
};
if (fs.existsSync(signedFilePath)) {
const signedPdf = fs.readFileSync(signedFilePath);
pdfSize = signedPdf.length;
console.log(`✅ New Signed PDF created called: ${signedFilePath} \n`);
fs.unlinkSync(filePath);
if (res.attempt > 1) {
logbody = { ...logbody, status: 'success' };
axios.post(`${cloudServerUrl}/classes/logs_SignRetry`, logbody, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
});
}
} else {
fs.unlinkSync(filePath);
const error = new Error(res.error);
error.code = 400; // Set the error code (e.g., 400 for bad request)
logbody = { ...logbody, status: 'failure', stackTrace: res.error };
axios.post(`${cloudServerUrl}/classes/logs_SignRetry`, logbody, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
});
throw error;
}
}
} else { } else {
//`saveUrl` is used to save signed pdf in exports folder //`saveUrl` is used to save signed pdf in exports folder
fs.writeFileSync(signedFilePath, PdfBuffer); fs.writeFileSync(signedFilePath, PdfBuffer);
@@ -476,10 +412,7 @@ async function PDF(req) {
} }
// `uploadFile` is used to upload pdf to aws s3 and get it's url // `uploadFile` is used to upload pdf to aws s3 and get it's url
const data = await uploadFile( const data = await uploadFile(`signed_${name}`, signedFilePath);
`signed_${name}`,
signedFilePath,
);
if (data && data.imageUrl) { if (data && data.imageUrl) {
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId // `axios` is used to update signed pdf url in contracts_Document classes for given DocId
@@ -496,13 +429,7 @@ async function PDF(req) {
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId); saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
if (updatedDoc && updatedDoc.isCompleted) { if (updatedDoc && updatedDoc.isCompleted) {
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl }; const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl };
sendMailsaveCertifcate( sendMailsaveCertifcate(doc, pfx, isCustomMail, mailProvider, `signed_${name}`);
doc,
pfx,
isCustomMail,
mailProvider,
`signed_${name}`,
);
} else { } else {
fs.unlinkSync(pfxname); fs.unlinkSync(pfxname);
} }
+2 -1
View File
@@ -11,7 +11,8 @@ services:
env_file: .env.prod env_file: .env.prod
environment: environment:
- NODE_ENV=production - NODE_ENV=production
- SERVER_URL=${HOST_URL:-https://localhost:3001}/app - SERVER_URL=${HOST_URL:-https://localhost:3001}/api/app
- PUBLIC_URL=${HOST_URL:-https://localhost:3001}
networks: networks:
- app-network - app-network
mongo: mongo: