fix: implement feature to detect lagunage from browser, resolve conflict

This commit is contained in:
RaktimaNXG
2024-08-02 17:22:25 +05:30
78 changed files with 6601 additions and 9242 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
MASTER_KEY=XnAadwKxxByMr
# Mongodb URI to connect to
MONGODB_URI=mongodb://host.docker.internal:27018/OpenSignDB
MONGODB_URI=mongodb://mongo-container:27017/OpenSignDB
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
+2064 -5770
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -4,10 +4,10 @@
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.7.2",
"@lottiefiles/dotlottie-react": "^0.8.5",
"@radix-ui/themes": "^3.1.1",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.6",
"@reduxjs/toolkit": "^2.2.7",
"axios": "^1.7.2",
"file-saver": "^2.0.5",
"i18next": "^23.12.2",
@@ -23,8 +23,8 @@
"react": "^18.2.0",
"react-bootstrap": "^2.10.4",
"react-confetti": "^6.1.0",
"react-cookie": "^7.1.4",
"react-datepicker": "^7.2.0",
"react-cookie": "^7.2.0",
"react-datepicker": "^7.3.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
@@ -50,7 +50,7 @@
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"serve": "^14.2.3",
"styled-components": "^4.4.1",
"styled-components": "^5.3.0",
"web-vitals": "^4.2.2",
"ws": "^8.18.0"
},
@@ -92,10 +92,10 @@
}
},
"devDependencies": {
"@babel/core": "^7.24.6",
"@babel/preset-env": "^7.24.6",
"@babel/preset-react": "^7.24.6",
"@babel/runtime-corejs2": "^7.24.7",
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-react": "^7.24.7",
"@babel/runtime-corejs2": "^7.25.0",
"autoprefixer": "^10.4.19",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
@@ -106,14 +106,14 @@
"dotenv": "^16.4.5",
"dotenv-webpack": "^8.1.0",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.34.3",
"lint-staged": "^15.2.7",
"mini-css-extract-plugin": "^2.9.0",
"postcss": "^8.4.39",
"postcss": "^8.4.40",
"prettier": "^3.3.3",
"pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.6",
"tailwindcss": "^3.4.7",
"terser-webpack-plugin": "^5.3.10",
"webpack-cli": "^5.1.4"
},
@@ -35,10 +35,15 @@
"save":"Save",
"cancel":"Cancel",
"upgrade-now":"Upgrade now",
"upgrade-to":"Upgrade to",
"plan":"Plan",
"subscribe-card-plan":"Sign 100 docs using API. Only $0.15/doc after that.",
"tour-content":"Don't show this again",
"pro":"PRO",
"team":"TEAM",
"user":"User",
"docs":"Docs",
"session-expired":"Your session has expired.",
"public-profile-help":"This is your public URL. Copy or share it with the signer, and you will be able to see all your publicly set templates.",
"tagline-help":"This Tagline will appear on your OpenSign public profile for ex. https://opensign.me/alex",
"welcome":"Welcome Back!",
@@ -108,6 +113,7 @@
},
"btnLabel":{
"sign":"sign",
"Sign":"SIGN",
"Resend":"Resend",
"Revoke":"Revoke",
"Delete":"Delete",
@@ -134,7 +140,11 @@
"Active":"Active",
"Role":"Role",
"Team":"Team",
"Name":"Name"
"Name":"Name",
"Status":"Status",
"created-date":"Created Date",
"Type":"Type"
},
"report-help":{
"Draft Documents": "These are documents you have started but have not finalized for sending.",
@@ -270,27 +280,27 @@
"document-sent-alert" : "Document sent successfully!",
"template-share-alert" :"Template Shared successfully.",
"template-public-alert-1" :"You have successfully made the template public.",
"template-public-alert-2" :"You have successfully made the template private.",
"template-public-alert-3" :"You need to select a role for the public signers.",
"template-public-alert-2" :"You have successfully made the template private.",
"template-public-alert-3" :"You need to select a role for the public signers.",
"template-public-alert-4": "Please ensure there's at least one signature widget added for all signers.",
"template-public-alert-5" : "Since this template has send-in-order enabled, the public role must be positioned at the top.",
"template-public-alert-6" :"Please attach signers to all roles except for the public role user.",
"template-public-alert-7" : "Please assign at least one public role to make this template public.",
"loader":" This might take some time",
"pages":"Pages",
"document-signed":"Document signed",
"close":"Close",
"validation-alert":"Validation alert",
"validate-alert-mssg" :" The input does not meet the criteria set by the regular expression.",
"otp-verification":"OTP verification",
"enter-otp":"Enter OTP",
"verify":"Verify",
"resend":"Resend",
"verify-email":"Please verify your email !",
"send-otp":" Send OTP",
"otp-placeholder":"Enter OTP received over email",
"loading-doc":"Loading Document..",
"widgets-name":{
"template-public-alert-6" :"Please attach signers to all roles except for the public role user.",
"template-public-alert-7" : "Please assign at least one public role to make this template public.",
"loader":" This might take some time",
"pages":"Pages",
"document-signed":"Document signed",
"close":"Close",
"validation-alert":"Validation alert",
"validate-alert-mssg" :" The input does not meet the criteria set by the regular expression.",
"otp-verification":"OTP verification",
"enter-otp":"Enter OTP",
"verify":"Verify",
"resend":"Resend",
"verify-email":"Please verify your email !",
"send-otp":" Send OTP",
"otp-placeholder":"Enter OTP received over email",
"loading-doc":"Loading Document..",
"widgets-name":{
"signature":"signature",
"stamp":"stamp",
"initials":"initials",
@@ -331,6 +341,11 @@
"signature-tab":"Signature",
"your-signature":"Your signature",
"copy-to-all-pages":"Copy to all pages",
"contact-delete-alert":"Are you sure you want to delete this contact?",
"reset-password-alert-1":"Reset password link has been sent to your email id",
"reset-password-alert-2":"Please setup email adapter ",
"reset-password-alert-3":"Reset Your Password",
"faild-animation" :"Failed to load animation",
"apply":"Apply",
"copy-type":{
"All pages":"All pages",
@@ -493,6 +508,20 @@
"master-key":" Master key",
"profile-update-alert" :"Profile updated successfully.",
"date":"Date",
"report-not-found":"Report Not Found",
"please-wait":"please wait",
"select-foler":"Select Folder",
"generated-on":"Generated On",
"text-field":"Text field",
"font-size":"Font size",
"all-right":"All Rights Reserved",
"version":"version",
"social-media":{
"github":"Github",
"linked-in":"LinkedIn",
"twitter":"Twitter",
"discord":"Discord"
},
"tour-mssg":{
"home-layout-1":"You have logged in successfully! Let's take a look.",
"home-layout-2":"To upload documents for self-signing or to request others signatures, simply select the respective buttons.",
@@ -523,7 +552,14 @@
"template-placeholder-3":"Drag or click on a field to add it to the document.",
"template-placeholder-4":"Drag the placeholder for a role anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.",
"template-placeholder-5":"Clicking 'Next' will store the current template. After saving, youll be prompted to create a new document from this template if you wish.",
"webhook-1":"Upgrade now to set webhook"
"webhook-1":"Upgrade now to set webhook",
"Need your Signature":"Clicking on this card will take you to the list of documents awaiting your review.",
"Out for signatures":"Clicking on this card will take you to a list of documents awaiting signature.",
"Recent signature requests": "This is a list of documents that are waiting for your signature.",
"Recently sent for signatures":"This is a list of documents you've sent to other parties for signature.",
"Drafts":"This are documents you have started but have not finalized for sending."
}
@@ -35,9 +35,14 @@
"save":"Sauvegarder",
"cancel":"Annuler",
"upgrade-now":"Mettre à jour maintenant",
"upgrade-to" :"Mettre à niveau vers",
"pro":"PRO",
"plan":"Plan",
"subscribe-card-plan":"Signez 100 documents à l'aide de l'API. Seulement 0,15 $/doc par la suite.",
"tour-content":"Ne montre plus ça",
"team":"ÉQUIPE",
"docs":"Documents",
"session-expired" :"Votre session a expiré.",
"user":"Utilisateur",
"public-profile-help":"Il s'agit de votre URL publique. Copiez-le ou partagez-le avec le signataire et vous pourrez voir tous vos modèles définis publiquement.",
"tagline-help":"Ce slogan apparaîtra sur votre profil public OpenSign par exemple. https://opensign.me/alex",
@@ -121,7 +126,10 @@
"Active":"Actif",
"Role":"Rôle",
"Team":"Équipe",
"Name":"Nom"
"Name":"Nom",
"Status":"Statut",
"created-date":"Date de création",
"Type":"Taper"
},
"btnLabel":{
"sign":"signe",
@@ -331,6 +339,11 @@
"signature-tab":"Signature",
"your-signature" :"Votre signature",
"copy-to-all-pages" :"Copier sur toutes les pages",
"contact-delete-alert" :"Êtes-vous sûr de vouloir supprimer ce contact ?",
"reset-password" :"Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
"reset-password-alert-2" :"Veuillez configurer l'adaptateur de messagerie",
"reset-password-alert-3" :"Réinitialisez votre mot de passe",
"faild-animation" :"Échec du chargement de l'animation",
"apply" :"Appliquer",
"copy-type":{
"All pages":"Toutes les pages",
@@ -493,6 +506,20 @@
"master-key" :"La clef maitresse",
"profile-update-alert" :"Mise à jour du profil réussie.",
"date":"Date",
"report-not-found" :"Rapport introuvable",
"please-wait" :"S'il vous plaît, attendez",
"select-foler":"Sélectionner le dossier",
"generated-on" :"Généré le",
"text-field" :"Champ de texte",
"font-size" :"Taille de police",
"all-right" :"Tous droits réservés",
"version" :"version",
"social-media":{
"github":"Github",
"linked-in":"LinkedIn",
"twitter":"Twitter",
"discord":"Discorde"
},
"tour-mssg":{
"home-layout-1":"Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.",
"home-layout-2": "Pour télécharger des documents à auto-signature ou pour demander la signature d'autres personnes, sélectionnez simplement les boutons correspondants.",
@@ -523,8 +550,12 @@
"template-placeholder-3":"Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
"template-placeholder-4":"Faites glisser l'espace réservé pour un rôle n'importe où sur le document. N'oubliez pas qu'il apparaîtra dans la même couleur que le nom du destinataire pour une référence facile.",
"template-placeholder-5":"Cliquer sur 'Suivant' stockera le modèle actuel. Après l'enregistrement, vous serez invité à créer un nouveau document à partir de ce modèle si vous le souhaitez.",
"webhook-1":"Mettez à niveau maintenant pour définir le webhook"
"webhook-1":"Mettez à niveau maintenant pour définir le webhook",
"Need your Signature":"En cliquant sur cette carte, vous accéderez à la liste des documents en attente de révision.",
"Out for signatures":"En cliquant sur cette carte, vous accéderez à une liste de documents en attente de signature.",
"Recent signature requests": "Voici une liste de documents qui attendent votre signature.",
"Recently sent for signatures":"Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
"Drafts":"Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi."
}
File diff suppressed because one or more lines are too long
+5 -2
View File
@@ -2,7 +2,9 @@ import React, { useEffect, useState } from "react";
import Package from "../../package.json";
import axios from "axios";
import { openInNewTab } from "../constant/Utils";
import { useTranslation } from "react-i18next";
const Footer = () => {
const { t } = useTranslation();
const [showButton, setShowButton] = useState(false);
const [version, setVersion] = useState("");
useEffect(() => {
@@ -48,9 +50,10 @@ const Footer = () => {
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
<aside>
<p>
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
{t("all-right")} &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
{appName} ( {t("version")}:{" "}
{version ? version : `${Package.version} `})
</span>
</p>
</aside>
@@ -341,6 +341,7 @@ const DashboardCard = (props) => {
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="font-medium">
<div className="text-base lg:text-lg">
{t(`dashboard-card.${props.Label}`)}
@@ -354,7 +355,8 @@ const DashboardCard = (props) => {
<Tooltip
id={props.Label}
iconColor={"white"}
message={props?.Data?.tourMessage}
message={t(`tour-mssg.${props.Label}`)}
// {props?.Data?.tourMessage}
/>
</div>
</div>
@@ -4,7 +4,9 @@ import ReportTable from "../../primitives/GetReportDisplay";
import reportJson from "../../json/ReportJson";
import axios from "axios";
import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next";
function DashboardReport(props) {
const { t } = useTranslation();
// console.log("props ", props)
const [List, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true);
@@ -141,7 +143,7 @@ function DashboardReport(props) {
) : (
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
<div className="text-center">
<p className="text-xl text-black">Report Not Found</p>
<p className="text-xl text-black">{t("report-not-found")}</p>
</div>
</div>
)}
@@ -1,4 +1,5 @@
import React, { Suspense, lazy } from "react";
import { useTranslation } from "react-i18next";
const DashboardButton = lazy(() => import("./DashboardButton"));
const DashboardCard = lazy(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport"));
@@ -17,6 +18,7 @@ const buttonList = [
}
];
const GetDashboard = (props) => {
const { t } = useTranslation();
const Button = ({ label, redirectId, redirectType, icon }) => (
<DashboardButton
Icon={icon}
@@ -37,7 +39,7 @@ const GetDashboard = (props) => {
<Suspense
fallback={
<div className="h-[150px] w-full flex justify-center items-center">
loading...
{t("loading")}
</div>
}
>
@@ -506,7 +506,7 @@ function DriveBody(props) {
>
<i className={menu.icon}></i>
<span className="ml-[8px]">
{t(`context-menu${menu.type}`)}
{t(`context-menu.${menu.type}`)}
</span>
</ContextMenu.Item>
);
@@ -518,18 +518,26 @@ function DriveBody(props) {
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong className="text-[13px]">Title: </strong>
<strong className="text-[13px]">
{t("report-heading.Title")}:{" "}
</strong>
<span className="text-[12px] font-medium mb-0"> {data.Name}</span>
<br />
<strong className="text-[13px]">Status: </strong>
<strong className="text-[13px]">
{t("report-heading.Status")}:{" "}
</strong>
<span className="text-[12px] font-medium"> {status}</span>
<br />
<strong className="text-[13px]">Created Date: </strong>
<strong className="text-[13px]">
{t("report-heading.created-date")}:{" "}
</strong>
<span className="text-[12px] font-medium">{createddate}</span>
<br />
{signerExist && (
<>
<strong className="text-[13px]">Signers: </strong>
<strong className="text-[13px]">
{t("report-heading.Signers")}:{" "}
</strong>
{signersName()}
</>
)}
@@ -549,11 +557,11 @@ function DriveBody(props) {
<Table striped bordered hover>
<thead>
<tr>
<th>Name</th>
<th>Created Date</th>
<th>Type</th>
<th>Status</th>
<th>Action</th>
<th>{t("report-heading.Name")}</th>
<th>{t("report-heading.created-date")}</th>
<th>{t("report-heading.Type")}</th>
<th>{t("report-heading.Status")}</th>
<th>{t("action")}</th>
</tr>
</thead>
<tbody>
@@ -586,25 +594,25 @@ function DriveBody(props) {
)}
<ModalUi
isOpen={isDeleteDoc}
title={"Delete Document"}
title={t("delete-document")}
handleClose={() => setIsDeleteDoc(false)}
>
<div className="h-full p-[20px] text-base-content">
<p>Are you sure you want to delete this document?</p>
<p>{t("delete-document-alert")}</p>
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
<button
onClick={() => handleDeleteDocument(selectDoc)}
type="button"
className="op-btn op-btn-primary mr-2"
>
Yes
{t("yes")}
</button>
<button
onClick={() => setIsDeleteDoc(false)}
type="button"
className="op-btn op-btn-neutral"
>
No
{t("no")}
</button>
</div>
</ModalUi>
+11 -13
View File
@@ -419,19 +419,17 @@ function Header({
</button>
</div>
) : isPublicTemplate ? (
<>
<div className="flex">
<button
type="button"
className="op-btn op-btn-primary op-btn-sm shadow"
onClick={() => {
embedWidgetsData();
}}
>
{t("sign-now")}
</button>
</div>
</>
<div className="flex">
<button
type="button"
className="op-btn op-btn-primary op-btn-sm shadow"
onClick={() => {
embedWidgetsData();
}}
>
{t("sign-now")}
</button>
</div>
) : (
<div className="flex">
<button
@@ -16,7 +16,7 @@ import RegexParser from "regex-parser";
import { emailRegex } from "../../constant/const";
import { useTranslation } from "react-i18next";
const textWidgetCls =
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap";
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
const selectWidgetCls =
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
function PlaceholderType(props) {
@@ -433,7 +433,7 @@ function PlaceholderType(props) {
case "dropdown":
return props.data?.signerObjId === props.signerObjId ? (
<select
className={`${selectWidgetCls} text-[12px]`}
className={`${selectWidgetCls} text-[12px] bg-inherit`}
id="myDropdown"
value={selectOption}
onChange={(e) => {
@@ -579,7 +579,7 @@ function RenderPdf({
{/* this component for render pdf document is in middle of the component */}
<Document
onLoadError={() => {
pdfLoad(false);
setPdfLoad(false);
}}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
@@ -0,0 +1,39 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
function SelectLanguage() {
const { i18n } = useTranslation();
const languages = [
{ value: "en", text: "English" },
{ value: "fr", text: "Français" }
];
const [lang, setLang] = useState("en");
// This function put query that helps to
// change the language
const handleChangeLang = (e) => {
setLang(e.target.value);
i18n.changeLanguage(e.target.value);
};
return (
<div className={`flex justify-center items-center mt-4`}>
<select
value={lang}
onChange={handleChangeLang}
className="op-select op-select-bordered w-[15%] bg-white pt-0"
>
<option disabled selected>
select
</option>
{languages.map((item) => {
return (
<option key={item.value} value={item.value}>
{item.text}
</option>
);
})}
</select>
</div>
);
}
export default SelectLanguage;
+10 -5
View File
@@ -1,7 +1,6 @@
import React, { useRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import SignatureCanvas from "react-signature-canvas";
function SignPad({
isSignPad,
isStamp,
@@ -101,7 +100,12 @@ function SignPad({
} else {
if (isTab === "type") {
setIsSignImg("");
onSaveSign(null, false, textWidth, textHeight);
onSaveSign(
null,
false,
!isInitial && textWidth > 150 ? 150 : textWidth,
!isInitial && textHeight > 35 ? 35 : textHeight
);
} else {
setIsSignImg("");
canvasRef.current.clear();
@@ -213,11 +217,11 @@ function SignPad({
: fontSelect
? fontSelect
: "Fasthand";
const fontSizeValue = "40px";
//creating span for getting text content width
const span = document.createElement("span");
span.textContent = textContent;
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
span.style.font = `${fontSizeValue} ${fontfamily}`; // here put your text size and font family
span.style.color = color ? color : penColor;
span.style.display = "hidden";
document.body.appendChild(span); // Replace 'container' with the ID of the container element
@@ -227,7 +231,8 @@ function SignPad({
// Draw the text content on the canvas
const ctx = canvasElement.getContext("2d");
const pixelRatio = window.devicePixelRatio || 1;
const width = span.offsetWidth;
const addExtraWidth = isInitial ? 10 : 50;
const width = span.offsetWidth + addExtraWidth;
const height = span.offsetHeight;
setTextWidth(width);
setTextHeight(height);
@@ -1,19 +1,21 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function TextFontSetting(props) {
const { t } = useTranslation();
return (
<ModalUi
headerColor={"#dc3545"}
isOpen={props.isTextSetting}
reduceWidth={"max-w-[350px]"}
title={"Text field"}
title={t("text-field")}
handleClose={() => props.setIsTextSetting(false)}
>
<div className="h-full p-[20px]">
<div className="flex items-center">
<span>Font size:</span>
<span>{t("font-size")} :</span>
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
@@ -32,7 +34,7 @@ function TextFontSetting(props) {
})}
</select>
<div className="flex flex-row gap-1 items-center ml-4">
<span>color: </span>
<span>{t("color")}: </span>
<select
value={
props.fontColor ||
@@ -67,7 +69,7 @@ function TextFontSetting(props) {
type="button"
className="op-btn op-btn-primary mt-2"
>
save
{t("save")}
</button>
</div>
</ModalUi>
@@ -54,7 +54,7 @@ export default function DropboxChooser({ children, onSuccess, onCancel }) {
return (
<div onClick={handleChoose}>
{children || (
<button className="px-2 py-[2px] text-2xl rounded border-[1px] text-blue-400 border-gray-300 w-full">
<button className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[35px] max-w-[45px] flex justify-center items-center">
<i className="fa-brands fa-dropbox"></i>
</button>
)}
@@ -2,8 +2,10 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
const FolderModal = (props) => {
const { t } = useTranslation();
const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
@@ -147,7 +149,7 @@ const FolderModal = (props) => {
return (
<div className="text-xs mt-2">
<ModalUi
title={"Select Folder"}
title={t("select-foler")}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
@@ -158,7 +160,7 @@ const FolderModal = (props) => {
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
{t("OpenSign-drive")} /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
@@ -214,7 +216,7 @@ const FolderModal = (props) => {
))
: !isLoader && (
<div className="text-base-content text-center my-2">
No data found
{t("no-data")}
</div>
)}
</div>
@@ -236,27 +238,28 @@ const FolderModal = (props) => {
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="op-btn op-btn-seconday op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleCreate}
>
{isAdd ? (
<>
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
<span className="text-xs">Back</span>
<span className="text-xs">{t("back")}</span>
</>
) : (
<>
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
<span className="">Add folder</span>
<span className="">{t("add-folder")}</span>
</>
)}
</div>
<div
className="op-btn op-btn-primary op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleSubmit}
>
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
<i className="fa-light fa-save" aria-hidden="true"></i>
{t("save-here")}
</div>
</div>
</ModalUi>
@@ -1,27 +1,54 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
const SocialMedia = () => {
return (
<React.Fragment>
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">OpenSign&apos;s Github</span>
</NavLink>
<NavLink to="https://www.linkedin.com/company/opensign%E2%84%A2/" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">OpenSign&apos;s LinkedIn</span>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">OpenSign&apos;s Twitter</span>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">OpenSign&apos;s Discord</span>
</NavLink>
</React.Fragment>
)
}
const { t } = useTranslation();
export default SocialMedia;
return (
<React.Fragment>
<NavLink
to="https://github.com/opensignlabs/opensign"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.github")}
</span>
</NavLink>
<NavLink
to="https://www.linkedin.com/company/opensign%E2%84%A2/"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.linked-in")}
</span>
</NavLink>
<NavLink
to="https://www.twitter.com/opensignlabs"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.twitter")}
</span>
</NavLink>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.discord")}
</span>
</NavLink>
</React.Fragment>
);
};
export default SocialMedia;
+28 -40
View File
@@ -549,20 +549,17 @@ export const signPdfFun = async (
base64Url,
documentId,
signerObjectId,
setIsAlert,
objectId,
isSubscribed,
activeMailAdapter,
xyPosition
widgets
) => {
let singleSign,
isCustomCompletionMail = false;
let isCustomCompletionMail = false;
try {
//get tenant details
const tenantDetails = await getTenantDetails(objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert("User does not exist");
return { status: "error", message: "User does not exist." };
} else {
if (
tenantDetails?.CompletionBody &&
@@ -576,7 +573,7 @@ export const signPdfFun = async (
// below for loop is used to get first signature of user to send if to signpdf
// for adding it in completion certificate
let getSignature;
for (let item of xyPosition) {
for (let item of widgets) {
if (!getSignature) {
const typeExist = item.pos.some((data) => data?.type);
if (typeExist) {
@@ -595,14 +592,15 @@ export const signPdfFun = async (
base64Sign = await fetchImageBase64(base64Sign);
} catch (e) {
console.log("error", e);
return { status: "error", message: "something went wrong." };
}
}
//change image width and height to 104/44 in png base64
//change image width and height to 100/40 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
singleSign = {
const params = {
mailProvider: activeMailAdapter,
pdfFile: base64Url,
docId: documentId,
@@ -610,34 +608,25 @@ export const signPdfFun = async (
isCustomCompletionMail: isCustomCompletionMail,
signature: suffixbase64
};
const response = await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// sessionToken: localStorage.getItem("accesstoken")
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
})
.then((Listdata) => {
const json = Listdata.data;
const res = json.result;
return res;
})
.catch((err) => {
console.log("Err in signPdf cloud function ", err);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
return response;
const resSignPdf = await Parse.Cloud.run("signPdf", params);
if (resSignPdf) {
const signedPdf = JSON.parse(JSON.stringify(resSignPdf));
return signedPdf;
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
console.log("Err in signPdf cloud function ", e.message);
if (e && e?.message?.includes("is encrypted.")) {
return {
status: "error",
message: "Currently encrypted pdf files are not supported."
};
} else if (
e.message === "PKCS#12 MAC could not be verified. Invalid password?"
) {
return { status: "error", message: "PFX file password is invalid." };
} else {
return { status: "error", message: "something went wrong." };
}
}
};
@@ -1237,9 +1226,8 @@ export const changeImageWH = async (base64Image) => {
const ctx = canvas.getContext("2d");
canvas.width = newWidth;
canvas.height = newHeight;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0, 0, newWidth, newHeight);
const resizedBase64 = canvas.toDataURL("image/png", 1);
const resizedBase64 = canvas.toDataURL("image/png", 1.0);
resolve(resizedBase64);
};
img.onerror = (error) => {
@@ -1259,14 +1247,14 @@ const calculateFontSize = (position, containerScale, signyourself) => {
};
//function for embed multiple signature using pdf-lib
export const multiSignEmbed = async (
xyPositionArray,
widgets,
pdfDoc,
signyourself,
scale,
pdfOriginalWH,
containerWH
) => {
for (let item of xyPositionArray) {
for (let item of widgets) {
const containerScale = getContainerScale(
pdfOriginalWH,
item?.pageNumber,
+7 -2
View File
@@ -5,11 +5,16 @@ import LanguageDetector from "i18next-browser-languagedetector";
i18n
.use(Backend)
.use(LanguageDetector)
.use(LanguageDetector) // Use LanguageDetector directly without creating an instance
.use(initReactI18next)
.init({
lng: "en", // Set English as the default language
fallbackLng: "en", // Fallback to English if no other language is detected
detection: {
// Order and from where user language should be detected
order: ["navigator", "htmlTag", "path", "subdomain"],
// Cache user language on
caches: ["localStorage", "cookie"]
},
debug: true,
interpolation: {
escapeValue: false // Not needed for react as it escapes by default
+1
View File
@@ -18,6 +18,7 @@ import TagManager from "react-gtm-module";
import Parse from "parse";
import "./polyfills";
import "./i18n";
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
+8 -15
View File
@@ -19,9 +19,7 @@ const dashboardJson = [
key: "count",
Redirect_type: "Report",
Redirect_id: "4Hhwbp482K",
tourSection: "tourcard1",
tourMessage:
"Clicking on this card will take you to the list of documents awaiting your review."
tourSection: "tourcard1"
}
}
},
@@ -42,9 +40,7 @@ const dashboardJson = [
key: "count",
Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF",
tourSection: "tourcard2",
tourMessage:
"Clicking on this card will take you to a list of documents awaiting signature."
tourSection: "tourcard2"
}
}
},
@@ -53,10 +49,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "5Go51Q7T8r",
label: "Recent signature requests",
data: {
tourSection: "tourreport1",
tourMessage:
"This is a list of documents that are waiting for your signature."
tourSection: "tourreport1"
}
}
},
@@ -65,10 +60,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "d9k3UfYHBc",
label: "Recently sent for signatures",
data: {
tourSection: "tourreport2",
tourMessage:
"This is a list of documents you've sent to other parties for signature."
tourSection: "tourreport2"
}
}
},
@@ -77,10 +71,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "kC5mfynCi4",
label: "Drafts",
data: {
tourSection: "tourreport3",
tourMessage:
"This are documents you have started but have not finalized for sending."
tourSection: "tourreport3"
}
}
}
+1 -1
View File
@@ -300,7 +300,7 @@ const HomeLayout = () => {
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-medium">Your session has expired.</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
Login
{t("login")}
</button>
</div>
</ModalUi>
+1 -1
View File
@@ -251,7 +251,7 @@ const AddAdmin = () => {
};
return (
<div className="h-screen flex justify-center">
<Title title={t("add-admin")} />
<Title title="Add admin" />
{state.loading ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
+3 -1
View File
@@ -6,8 +6,10 @@ import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const Dashboard = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const dispatch = useDispatch();
const { id } = useParams();
@@ -40,7 +42,7 @@ const Dashboard = () => {
.map((col) => {
return {
selector: `[data-tut=${col.widget.data.tourSection}]`,
content: col.widget.data.tourMessage,
content: t(`tour-mssg.${col.widget.data.label}`),
position: "top"
// style: { backgroundColor: "#abd4d2" },
};
+9 -9
View File
@@ -9,8 +9,10 @@ import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo } from "../constant/Utils";
import { useTranslation } from "react-i18next";
function ForgotPassword() {
const { t } = useTranslation();
const dispatch = useDispatch();
const navigate = useNavigate();
const [state, setState] = useState({
@@ -75,12 +77,10 @@ function ForgotPassword() {
<div>
<Title title="Forgot password page" />
{sentStatus === "success" && (
<Alert type="success">
Reset password link has been sent to your email id
</Alert>
<Alert type="success">{t("reset-password-alert-1")}</Alert>
)}
{sentStatus === "failed" && (
<Alert type={"danger"}>Please setup email adapter </Alert>
<Alert type={"danger"}>{t("reset-password-alert-2")}</Alert>
)}
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
@@ -96,13 +96,13 @@ function ForgotPassword() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
<h2 className="text-[30px] mt-6">{t("welcome")}</h2>
<span className="text-[12px] text-[#878787]">
Reset Your Password
{t("reset-password-alert-3")}
</span>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="px-6 py-4">
<label className="block text-xs">Email</label>
<label className="block text-xs">{t("email")}</label>
<input
type="email"
name="email"
@@ -116,13 +116,13 @@ function ForgotPassword() {
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
<button
onClick={() => navigate("/", { replace: true })}
className="op-btn op-btn-secondary"
>
Login
{t("login")}
</button>
</div>
</form>
+3 -3
View File
@@ -643,9 +643,9 @@ const Forms = (props) => {
<span className="text-red-500 text-[13px]">*</span>
</label>
{fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex gap-1 justify-center items-center">
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-[13px]">
<div className="break-all">
<div className="break-all cursor-default">
{t("file-selected")}: {getFileName(fileupload)}
</div>
<div
@@ -663,7 +663,7 @@ const Forms = (props) => {
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center ">
<div className="flex gap-1 justify-center items-center">
<input
type="file"
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
+4 -1
View File
@@ -17,6 +17,7 @@ import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchSubscription, getAppLogo, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
function Login() {
const { t } = useTranslation();
const navigate = useNavigate();
@@ -605,9 +606,10 @@ function Login() {
}
}
};
return (
<div>
<Title title={t("login-page")} />
<Title title="Login Page" />
{state.loading && (
<div
aria-live="assertive"
@@ -766,6 +768,7 @@ function Login() {
)}
</div>
</div>
<SelectLanguage />
{state.alertMsg && (
<Alert type={state.alertType}>
<div className="ml-3">{state.alertMsg}</div>
+160 -179
View File
@@ -820,8 +820,8 @@ function PdfRequestFiles(props) {
setWidgetsTour(true);
} else {
setIsUiLoading(true);
const pngUrl = checkUser[0].placeHolder;
// `widgets` is Used to return widgets details with page number of current user
const widgets = checkUser?.[0]?.placeHolder;
let pdfArrBuffer;
//`contractDocument` function used to get updated SignedUrl
//resolved issue of sign document by multiple signers simultaneously
@@ -859,14 +859,14 @@ function PdfRequestFiles(props) {
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
widgets,
pdfDoc,
isSignYourSelfFlow,
scale,
pdfOriginalWH,
containerWH
);
// console.log("pdfte", pdfBytes);
// console.log("pdfte", pdfBytes);
//get ExistUserPtr object id of user class to get tenantDetails
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
//get ExistUserPtr email to get userDetails
@@ -875,178 +875,166 @@ function PdfRequestFiles(props) {
let activeMailAdapter = "";
if (res === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg"));
setIsLoading({
isLoad: false
});
setIsLoading({ isLoad: false });
} else if (!res || res?.length === 0) {
activeMailAdapter = "";
} else if (res[0] && res.length) {
activeMailAdapter = res[0]?.active_mail_adapter;
}
//function for call to embed signature in pdf and get digital signature pdf
try {
const res = await signPdfFun(
pdfBytes,
documentId,
signerObjectId,
setIsAlert,
objectId,
isSubscribed,
activeMailAdapter,
pngUrl
const resSign = await signPdfFun(
pdfBytes,
documentId,
signerObjectId,
objectId,
isSubscribed,
activeMailAdapter,
widgets
);
if (resSign && resSign.status === "success") {
setPdfUrl(res.data);
setIsSigned(true);
setSignedSigners([]);
setUnSignedSigners([]);
getDocumentDetails(true);
const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === currentUser?.email
);
if (res && res.status === "success") {
setPdfUrl(res.data);
setIsSigned(true);
setSignedSigners([]);
setUnSignedSigners([]);
getDocumentDetails(true);
const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === currentUser?.email
);
const newIndex = index + 1;
const usermail = {
Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || ""
};
const user = usermail?.Email
? usermail
: pdfDetails?.[0]?.Signers[newIndex];
if (sendmail !== "false" && sendInOrder) {
const requestBody = pdfDetails?.[0]?.RequestBody;
const requestSubject = pdfDetails?.[0]?.RequestSubject;
if (user) {
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString(
"en-US",
{
day: "numeric",
month: "long",
year: "numeric"
}
);
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem(
"baseUrl"
)}functions/sendmailv3`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const objectId = user?.objectId;
const hostUrl = window.location.origin;
//encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function
let encodeBase64;
if (objectId) {
encodeBase64 = btoa(
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}`
);
} else {
encodeBase64 = btoa(
`${pdfDetails?.[0].objectId}/${user.Email}`
);
}
const hostPublicUrl = isStaging
? "https://staging-app.opensignlabs.com"
: "https://app.opensignlabs.com";
let signPdf = props?.templateId
? `${hostPublicUrl}/login/${encodeBase64}`
: `${hostUrl}/login/${encodeBase64}`;
const openSignUrl =
"https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company
: "";
const themeBGcolor = themeColor;
let replaceVar;
if (requestBody && requestSubject && isSubscribed) {
const replacedRequestBody = requestBody.replace(
/"/g,
"'"
);
const htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
"</body> </html>";
const variables = {
document_title: pdfDetails?.[0].Name,
sender_name: senderName,
sender_mail: senderEmail,
sender_phone: senderPhone,
receiver_name: user.Name,
receiver_email: user.Email,
receiver_phone: user.Phone,
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf}>Sign here</a>`
};
replaceVar = replaceMailVaribles(
requestSubject,
htmlReqBody,
variables
);
}
let params = {
mailProvider: activeMailAdapter,
extUserId: extUserId,
recipient: user.Email,
subject: requestSubject
? replaceVar?.subject
: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`,
from: senderEmail,
html: requestBody
? replaceVar?.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=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
pdfDetails?.[0].ExtUserPtr.Name +
" has requested you to review and sign <strong> " +
pdfDetails?.[0].Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
senderEmail +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
senderEmail +
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
openSignUrl +
" target=_blank>here</a>.</p> </div></div></body> </html>"
};
await axios.post(url, params, {
headers: headers
});
} catch (error) {
console.log("error", error);
const newIndex = index + 1;
const usermail = {
Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || ""
};
const user = usermail?.Email
? usermail
: pdfDetails?.[0]?.Signers[newIndex];
if (sendmail !== "false" && sendInOrder) {
const requestBody = pdfDetails?.[0]?.RequestBody;
const requestSubject = pdfDetails?.[0]?.RequestSubject;
if (user) {
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString(
"en-US",
{
day: "numeric",
month: "long",
year: "numeric"
}
);
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem(
"baseUrl"
)}functions/sendmailv3`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const objectId = user?.objectId;
const hostUrl = window.location.origin;
//encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function
let encodeBase64;
if (objectId) {
encodeBase64 = btoa(
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}`
);
} else {
encodeBase64 = btoa(
`${pdfDetails?.[0].objectId}/${user.Email}`
);
}
const hostPublicUrl = isStaging
? "https://staging-app.opensignlabs.com"
: "https://app.opensignlabs.com";
let signPdf = props?.templateId
? `${hostPublicUrl}/login/${encodeBase64}`
: `${hostUrl}/login/${encodeBase64}`;
const openSignUrl =
"https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company
: "";
const themeBGcolor = themeColor;
let replaceVar;
if (requestBody && requestSubject && isSubscribed) {
const replacedRequestBody = requestBody.replace(
/"/g,
"'"
);
const htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
"</body> </html>";
const variables = {
document_title: pdfDetails?.[0].Name,
sender_name: senderName,
sender_mail: senderEmail,
sender_phone: senderPhone,
receiver_name: user.Name,
receiver_email: user.Email,
receiver_phone: user.Phone,
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf}>Sign here</a>`
};
replaceVar = replaceMailVaribles(
requestSubject,
htmlReqBody,
variables
);
}
let params = {
mailProvider: activeMailAdapter,
extUserId: extUserId,
recipient: user.Email,
subject: requestSubject
? replaceVar?.subject
: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`,
from: senderEmail,
html: requestBody
? replaceVar?.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=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
pdfDetails?.[0].ExtUserPtr.Name +
" has requested you to review and sign <strong> " +
pdfDetails?.[0].Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
senderEmail +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
senderEmail +
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
openSignUrl +
" target=_blank>here</a>.</p> </div></div></body> </html>"
};
await axios.post(url, params, {
headers: headers
});
} catch (error) {
console.log("error", error);
}
}
} else {
setIsAlert({
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
} catch (err) {
setIsAlert({
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
} else {
setIsUiLoading(false);
setIsAlert({ isShow: true, alertMessage: resSign.message });
}
} catch (err) {
setIsUiLoading(false);
@@ -1659,25 +1647,18 @@ function PdfRequestFiles(props) {
<ModalUi
isOpen={isAlert.isShow}
title={t("alert-message")}
handleClose={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
handleClose={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
>
<div className="h-full p-[20px]">
<p>{isAlert.alertMessage}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
onClick={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
type="button"
className="op-btn op-btn-primary"
className="op-btn op-btn-primary mt-3 px-4"
>
{t("ok")}
</button>
+3 -1
View File
@@ -14,6 +14,7 @@ import { isEnableSubscription } from "../constant/const";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
const Signup = () => {
const { width } = useWindowSize();
const { t } = useTranslation();
@@ -322,7 +323,7 @@ const Signup = () => {
<Loader />
</div>
)}
<Title title={t("signup-page")} />
<Title title="Signup page" />
{appInfo && appInfo.applogo ? (
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
@@ -513,6 +514,7 @@ const Signup = () => {
)}
</div>
</div>
<SelectLanguage />
<Alert type={state.alertType}>{state.alertMsg}</Alert>
</div>
) : (
+31 -36
View File
@@ -710,15 +710,28 @@ function SignYourSelf() {
setIsUiLoading(false);
if (err && err.message.includes("is encrypted.")) {
setIsAlert({
header: "Error",
isShow: true,
alertMessage: t("encrypted-pdf-alert")
});
} else {
console.log("err in signing", err);
setIsAlert({
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
console.log("err in signing", err.message);
if (
err.message ===
"PKCS#12 MAC could not be verified. Invalid password?"
) {
setIsAlert({
header: "Error",
isShow: true,
alertMessage: `PFX file password is invalid.`
});
} else {
setIsAlert({
header: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
}
}
}
@@ -726,6 +739,7 @@ function SignYourSelf() {
console.log("err in embedselfsign ", err);
setIsUiLoading(false);
setIsAlert({
header: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
@@ -772,40 +786,24 @@ function SignYourSelf() {
alert(t("something-went-wrong-mssg"));
}
}
//change image width and height to 104/44 in png base64
//change image width and height to 100/40 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
let singleSign = {
const params = {
pdfFile: base64Url,
docId: documentId,
isCustomCompletionMail: isCustomCompletionMail,
mailProvider: activeMailAdapter,
signature: suffixbase64
};
await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// sessionToken: localStorage.getItem("accesstoken")
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
})
.then((Listdata) => {
const json = Listdata.data;
setPdfUrl(json.result.data);
if (json.result.data) {
getDocumentDetails(false);
}
})
.catch((err) => {
console.log("axois err ", err);
alert(t("something-went-wrong-mssg"));
});
const resSignPdf = await Parse.Cloud.run("signPdf", params);
if (resSignPdf) {
const signedpdf = JSON.parse(JSON.stringify(resSignPdf));
setPdfUrl(signedpdf);
getDocumentDetails(false);
}
};
//function for save x and y position and show signature tab on that position
@@ -1233,16 +1231,13 @@ function SignYourSelf() {
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
/>
<div className=" w-full md:w-[95%] ">
<div className="w-full md:w-[95%]">
<ModalUi
isOpen={isAlert.isShow}
title={isAlert?.header || t("alert")}
handleClose={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
handleClose={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
>
<div className="p-[20px] h-full">
<p>{isAlert.alertMessage}</p>
+5 -14
View File
@@ -1319,7 +1319,7 @@ const TemplatePlaceholder = () => {
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
/>
<div className=" w-full md:w-[95%] ">
<div className="w-full md:w-[95%]">
{/* this modal is used show alert set placeholder for all signers before send mail */}
<ModalUi
isOpen={isSendAlert}
@@ -1351,9 +1351,7 @@ const TemplatePlaceholder = () => {
{currentEmail.length > 0 && (
<>
<button
onClick={() => {
handleCreateDocModal();
}}
onClick={() => handleCreateDocModal()}
type="button"
className="op-btn op-btn-primary"
>
@@ -1377,17 +1375,13 @@ const TemplatePlaceholder = () => {
<ModalUi
isOpen={isShowEmail}
title={t("signers-alert")}
handleClose={() => {
setIsShowEmail(false);
}}
handleClose={() => setIsShowEmail(false)}
>
<div className="h-full p-[20px]">
<p>{t("template-creation-alert-1")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => {
setIsShowEmail(false);
}}
onClick={() => setIsShowEmail(false)}
type="button"
className="op-btn op-btn-primary"
>
@@ -1436,7 +1430,6 @@ const TemplatePlaceholder = () => {
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
// signerObjId={signerObjId}
Id={uniqueId}
/>
{/* pdf header which contain funish back button */}
@@ -1548,9 +1541,7 @@ const TemplatePlaceholder = () => {
/>
</div>
) : (
<div
className={`w-[23%] bg-[#FFFFFF] min-h-screen overflow-y-auto hide-scrollbar`}
>
<div className="w-[23%] bg-[#FFFFFF] min-h-screen overflow-y-auto hide-scrollbar">
<div className={`max-h-screen`}>
<SignerListPlace
isMailSend={isMailSend}
@@ -65,7 +65,7 @@ const UpdateExistUserAdmin = () => {
};
return (
<div className="h-screen flex justify-center">
<Title title={t("add-admin")} />
<Title title="Add admin" />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
+3 -39
View File
@@ -20,7 +20,7 @@ import { useTranslation } from "react-i18next";
function UserProfile() {
const navigate = useNavigate();
const { t, i18n } = useTranslation();
const { t } = useTranslation();
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
@@ -53,11 +53,6 @@ function UserProfile() {
extendUser && extendUser?.[0]?.Tagline
);
const [isTeam, setIsTeam] = useState(false);
const languages = [
{ value: "en", text: "English" },
{ value: "fr", text: "French" }
];
const [lang, setLang] = useState("en");
useEffect(() => {
getUserDetail();
@@ -311,12 +306,7 @@ function UserProfile() {
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
};
// This function put query that helps to
// change the language
const handleChangeLang = (e) => {
setLang(e.target.value);
i18n.changeLanguage(e.target.value);
};
return (
<React.Fragment>
<Title title={"Profile"} />
@@ -369,29 +359,6 @@ function UserProfile() {
</div>
</div>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">{t("language")}:</span>{" "}
<select
value={lang}
onChange={handleChangeLang}
className="select select-bordered w-[30%] border-[1px] rounded-lg p-2 max-w-xs"
>
<option disabled selected>
select
</option>
{languages.map((item) => {
return (
<option key={item.value} value={item.value}>
{item.text}
</option>
);
})}
</select>
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
@@ -406,10 +373,7 @@ function UserProfile() {
onChange={(e) => SetName(e.target.value)}
/>
) : (
<span>
{/* {localStorage.getItem("username")} */}
<p>{t("greeting", { name })}</p>
</span>
<span>{localStorage.getItem("username")}</span>
)}
</li>
<li
@@ -1,8 +1,10 @@
import React, { useState, useEffect } from "react";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import Loader from "./Loader";
import { useTranslation } from "react-i18next";
const LottieWithLoader = () => {
const { t } = useTranslation();
const [isLoaded, setIsLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
const [animationSrc, setAnimationSrc] = useState(null);
@@ -34,7 +36,7 @@ const LottieWithLoader = () => {
<Loader />
</div>
)}
{hasError && <div className="error">Failed to load animation</div>}
{hasError && <div className="error">{t("faild-animation")}</div>}
{isLoaded && animationSrc && (
<DotLottieReact
src={animationSrc}
@@ -1137,7 +1137,7 @@ const ReportTable = (props) => {
<button
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
title={t(`btnLabel.${act.hoverLabel}`)}
className={`${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm`}
@@ -1153,7 +1153,7 @@ const ReportTable = (props) => {
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this contact?
{t("contact-delete-alert")}
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
@@ -1161,13 +1161,13 @@ const ReportTable = (props) => {
onClick={() => handleDelete(item)}
className="op-btn op-btn-primary"
>
Yes
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
{t("no")}
</button>
</div>
</div>
@@ -1363,7 +1363,7 @@ const ReportTable = (props) => {
onClick={() =>
handleActionBtn(act, item)
}
title={t(`btnLabel.${act.btnLabel}`)}
title={t(`btnLabel.${act.hoverLabel}`)}
className={
act.action !== "option"
? `${
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import "../styles/signature.css";
import { useTranslation } from "react-i18next";
function CustomModal({
show,
@@ -9,6 +10,7 @@ function CustomModal({
declineDoc,
setIsDecline
}) {
const { t } = useTranslation();
const [reason, setReason] = useState("");
return (
show && (
@@ -41,7 +43,7 @@ function CustomModal({
declineDoc(reason);
}}
>
Yes
{t("yes")}
</button>
<button
type="button"
@@ -51,7 +53,7 @@ function CustomModal({
setIsDecline({ isDeclined: false });
}}
>
Close
{t("close")}
</button>
</div>
</>
@@ -1,22 +1,25 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
const SubscribeCard = ({ plan, price }) => {
const { t } = useTranslation();
const navigate = useNavigate();
return (
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body">
<h2 className="op-card-title">Upgrade to {plan ? plan : "PRO"} Plan</h2>
<h2 className="op-card-title">
{t("upgrade-to")} {plan ? plan : "PRO"} {t("plan")}
</h2>
<p>
${price ? price : "29.99"}/month: Sign 100 docs using API. Only
$0.15/doc after that.
${price ? price : "29.99"}/month: {t("subscribe-card-plan")}
</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
className="op-btn op-btn-accent"
>
Upgrade Now
{t("upgrade-now")}
</button>
</div>
</div>
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
export default function TourContentWithBtn({ message, isChecked }) {
const { t } = useTranslation();
const [isCheck, setIsCheck] = useState(false);
const handleCheck = () => {
@@ -19,7 +20,7 @@ export default function TourContentWithBtn({ message, isChecked }) {
checked={isCheck}
onChange={handleCheck}
/>
<span className="#787878 text-[12px]">Don&apos;t show this again</span>
<span className="#787878 text-[12px]">{t("tour-content")}</span>
</label>
</div>
);
+4 -2
View File
@@ -2,8 +2,10 @@ import React, { useState, useEffect } from "react";
import Parse from "parse";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import ModalUi from "./ModalUi";
import { useTranslation } from "react-i18next";
const Validate = () => {
const navigate = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const [isUserValid, setIsUserValid] = useState(true);
useEffect(() => {
@@ -51,9 +53,9 @@ const Validate = () => {
) : (
<ModalUi showHeader={false} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-medium">Your session has expired.</p>
<p className="text-xl font-medium">{this("session-expired")}</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
Login
{t("login")}
</button>
</div>
</ModalUi>
+15 -9
View File
@@ -20,15 +20,21 @@ const ValidateRoute = () => {
})();
}, []);
const handlelogout = async () => {
try {
// if (Parse?.User?.current()) {
// Parse?.User?.logOut();
// }
localStorage.removeItem("accesstoken");
} catch (err) {
console.log("err ", err);
localStorage.removeItem("accesstoken");
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding");
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
localStorage.clear();
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
};
return <div>{<Outlet />}</div>;
};
-31
View File
@@ -8,38 +8,7 @@ module.exports = {
daisyui: {
// themes: true,
themes: [
"light",
"dark",
"cupcake",
"bumblebee",
"emerald",
"corporate",
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"garden",
"forest",
"aqua",
"lofi",
"pastel",
"fantasy",
"wireframe",
"black",
"luxury",
"dracula",
"cmyk",
"autumn",
"business",
"acid",
"lemonade",
"night",
"coffee",
"winter",
"dim",
"nord",
"sunset",
{
opensigncss: {
primary: "#002864",
+2 -1
View File
@@ -4,8 +4,9 @@ dotenv.config();
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
export const cloudServerUrl = 'http://localhost:8080/app';
export function customAPIurl() {
const url = new URL(process.env.SERVER_URL);
const url = new URL(cloudServerUrl);
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
}
@@ -1,4 +1,5 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
// `replaceMailVaribles` is used to replace variables from mail with there actual values
function replaceMailVaribles(subject, body, variables) {
@@ -26,7 +27,7 @@ function replaceMailVaribles(subject, body, variables) {
async function sendMail(doc, signer) {
const subject = `{{sender_name}} has requested you to sign "{{document_title}}"`;
const body = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}}&nbsp;has requested you to review and sign&nbsp;<b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`;
const url = `${process.env.SERVER_URL}/functions/sendmailv3`;
const url = `${cloudServerUrl}/functions/sendmailv3`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
export default async function gooogleauth(request, response) {
const code = request.body.code;
const baseUrl = new URL(process.env.SERVER_URL);
@@ -3,7 +3,7 @@ import multer from 'multer';
import multerS3 from 'multer-s3';
import aws from 'aws-sdk';
import dotenv from 'dotenv';
import { useLocal } from '../../Utils.js';
import { cloudServerUrl, useLocal } from '../../Utils.js';
dotenv.config();
function sanitizeFileName(fileName) {
@@ -49,7 +49,7 @@ async function uploadFile(req, res) {
const DO_SECRET_ACCESS_KEY = process.env.DO_SECRET_ACCESS_KEY;
const DO_SPACE = process.env.DO_SPACE;
const parseBaseUrl = process.env.SERVER_URL;
const parseBaseUrl = cloudServerUrl; //process.env.SERVER_URL;
const parseAppId = process.env.APP_ID;
let fileStorage;
if (useLocal === 'true') {
@@ -1,5 +1,5 @@
import axios from 'axios';
import { customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
import { cloudServerUrl, customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
@@ -220,7 +220,7 @@ export default async function createDocumentWithTemplate(request, response) {
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
@@ -6,6 +6,7 @@ import {
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
cloudServerUrl,
} from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
@@ -281,7 +282,7 @@ export default async function createDocumentwithCoordinate(request, response) {
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
@@ -1,12 +1,13 @@
import axios from 'axios';
import reportJson from '../../../parsefunction/reportsJson.js';
import dotenv from 'dotenv';
import { cloudServerUrl } from '../../../../Utils.js';
dotenv.config();
export default async function getDocumentList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
@@ -1,10 +1,11 @@
import axios from 'axios';
import dotenv from 'dotenv';
import { cloudServerUrl } from '../../../../Utils.js';
dotenv.config();
export default async function getTemplatetList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
@@ -1,5 +1,5 @@
import axios from 'axios';
import { replaceMailVaribles } from '../../../../Utils.js';
import { cloudServerUrl, replaceMailVaribles } from '../../../../Utils.js';
export default async function resendMail(request, response) {
try {
@@ -44,7 +44,7 @@ export default async function resendMail(request, response) {
if (contact) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
@@ -1,5 +1,6 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
async function addTeamAndOrg(extUser) {
@@ -1,8 +1,9 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
async function AuthLoginAsMail(request) {
try {
//function for login user using user objectId without touching user's password
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
@@ -4,7 +4,8 @@ async function DocumentAftersave(request) {
console.log('new entry is insert in contracts_Document');
const createdAt = request.object.get('createdAt');
const Folder = request.object.get('Type');
const ip = request?.headers?.['x-real-ip'] || request.object?.get('OriginIp') || '';
const ip = request?.headers?.['x-real-ip'] || '';
const originIp = request?.object?.get('OriginIp') || '';
if (createdAt && Folder === undefined) {
// console.log("IN If condition")
const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
@@ -13,7 +14,9 @@ async function DocumentAftersave(request) {
const documentQuery = new Parse.Query('contracts_Document');
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
if (!originIp) {
updateQuery.set('OriginIp', ip);
}
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
if (AutoReminder) {
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
@@ -29,7 +32,9 @@ async function DocumentAftersave(request) {
const documentQuery = new Parse.Query('contracts_Document');
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
if (!originIp) {
updateQuery.set('OriginIp', ip);
}
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
if (AutoReminder) {
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
@@ -1,7 +1,8 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function GetTemplate(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
@@ -1,10 +1,11 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function callWebhook(request) {
const event = request.params.event;
const body = request.params.body;
const docId = body.objectId;
const contactId = request.params.contactId;
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
async function sendMail(document, sessionToken) {
@@ -1,7 +1,8 @@
import { generateApiKey } from 'generate-api-key';
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function generateApiToken(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
@@ -1,7 +1,8 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function getDocument(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId;
try {
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function getDrive(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const limit = request.params.limit;
const skip = request.params.skip;
@@ -1,5 +1,6 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getInvoices(request) {
const limit = request.params.limit || 100;
@@ -1,5 +1,6 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getPayments(request) {
const limit = request.params.limit || 100;
@@ -1,3 +1,4 @@
import { cloudServerUrl } from '../../Utils.js';
import reportJson from './reportsJson.js';
import axios from 'axios';
@@ -6,7 +7,7 @@ export default async function getReport(request) {
const limit = request.params.limit;
const skip = request.params.skip;
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const masterKey = process.env.MASTER_KEY;
try {
@@ -1,5 +1,6 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getSubscription(request) {
const extUserId = request.params.extUserId || '';
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function getapitoken(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
@@ -4,9 +4,9 @@ import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { PDFDocument } from 'pdf-lib';
import { replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
import { cloudServerUrl, replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js';
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
const eSignName = 'opensign';
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function saveSubscription(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const subscription = request.params.subscription;
const SubscriptionId = subscription.data.subscription.subscription_id;
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function savewebhook(request) {
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
@@ -2,6 +2,8 @@ import axios from 'axios';
import { google } from 'googleapis';
import fs from 'node:fs';
import https from 'https';
import http from 'http';
import { useLocal } from '../../Utils.js';
const clientId = process.env.GOOGLE_CLIENT_ID;
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
// Function to create a Gmail client
@@ -26,6 +28,7 @@ const refreshAccessToken = async refreshToken => {
// Function to create a raw email message
const makeEmail = async (to, from, subject, html, url, pdfName) => {
const protocol = new URL(process.env.SERVER_URL);
const htmlContent = html;
const boundary = 'boundary_' + Date.now().toString(16);
let str;
@@ -34,10 +37,17 @@ const makeEmail = async (to, from, subject, html, url, pdfName) => {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
https.get(url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
if (useLocal !== 'true' && protocol.hostname !== 'localhost') {
https.get(url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
} else {
http.get(url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
}
});
};
// `writeToLocalDisk` is used to create pdf file from doc url
@@ -130,7 +140,7 @@ export default async function sendMailGmailProvider(_extRes, template) {
raw: email,
},
});
// console.log('response ', response);
// console.log('response ', response);
return { code: 200, message: 'Email sent successfully' };
} catch (error) {
@@ -1,11 +1,13 @@
import fs from 'node:fs';
import https from 'https';
import http from 'http';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
import { smtpenable, smtpsecure, updateMailCount } from '../../Utils.js';
import { smtpenable, smtpsecure, updateMailCount, useLocal } from '../../Utils.js';
import sendMailGmailProvider from './sendMailGmailProvider.js';
import { createTransport } from 'nodemailer';
async function sendMailProvider(req) {
const protocol = new URL(process.env.SERVER_URL);
try {
let transporterSMTP;
let mailgunClient;
@@ -32,10 +34,17 @@ async function sendMailProvider(req) {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
https.get(req.params.url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
if (useLocal !== 'true' && protocol.hostname !== 'localhost') {
https.get(req.params.url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
} else {
http.get(req.params.url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
}
});
};
// `writeToLocalDisk` is used to create pdf file from doc url
@@ -1,6 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = process.env.SERVER_URL;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
const clientUrl = process.env.PUBLIC_URL;
@@ -1,5 +1,6 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
+2447 -2638
View File
File diff suppressed because it is too large Load Diff
+12 -8
View File
@@ -24,39 +24,43 @@
"@signpdf/placeholder-pdf-lib": "^3.2.4",
"@signpdf/signer-p12": "^3.2.4",
"@signpdf/signpdf": "^3.2.4",
"aws-sdk": "^2.1659.0",
"aws-sdk": "^2.1665.0",
"axios": "^1.7.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-sse": "^0.5.3",
"form-data": "^4.0.0",
"generate-api-key": "^1.0.2",
"googleapis": "^140.0.1",
"jsonschema": "^1.4.1",
"mailgun.js": "^10.2.3",
"mongoose": "^8.5.1",
"mongoose": "^8.5.2",
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^6.9.14",
"parse": "^5.3.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^7.1.0",
"parse-server": "^7.2.0",
"parse-server-api-mail-adapter": "^4.0.0",
"pdf-lib": "^1.17.1",
"posthog-node": "^4.0.1"
"posthog-node": "^4.0.1",
"ws": "^8.18.0"
},
"type": "module",
"devDependencies": {
"@babel/eslint-parser": "^7.24.8",
"eslint": "^9.7.0",
"jasmine": "^5.1.0",
"@babel/eslint-parser": "^7.25.1",
"eslint": "^9.8.0",
"jasmine": "^5.2.0",
"mongodb-runner": "^5.6.4",
"nodemon": "^3.1.4",
"nyc": "^17.0.0",
"prettier": "^3.3.3"
},
"overrides": {
"ws": "$ws",
"parse": "$parse"
},
"engines": {
"node": ">=12.22.10 <21"
}