Compare commits

..
3 Commits
80 changed files with 5777 additions and 5490 deletions
+789 -269
View File
File diff suppressed because it is too large Load Diff
+23 -23
View File
@@ -4,17 +4,17 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.2", "@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.13.2", "@lottiefiles/dotlottie-react": "^0.13.0",
"@pdf-lib/fontkit": "^1.1.1", "@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.6", "@radix-ui/themes": "^3.1.6",
"@react-pdf/renderer": "^4.1.6",
"@reduxjs/toolkit": "^2.5.1", "@reduxjs/toolkit": "^2.5.1",
"axios": "^1.8.4", "axios": "^1.7.9",
"css-minimizer-webpack-plugin": "^7.0.2", "css-minimizer-webpack-plugin": "^7.0.0",
"date-fns-tz": "^3.2.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^23.16.8", "i18next": "^23.16.8",
"i18next-browser-languagedetector": "^8.0.4", "i18next-browser-languagedetector": "^8.0.2",
"i18next-http-backend": "^3.0.2", "i18next-http-backend": "^3.0.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"moment": "^2.30.1", "moment": "^2.30.1",
@@ -25,7 +25,7 @@
"radix-ui": "^1.0.1", "radix-ui": "^1.0.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.10.9", "react-bootstrap": "^2.10.9",
"react-confetti": "^6.4.0", "react-confetti": "^6.2.2",
"react-cookie": "^7.2.2", "react-cookie": "^7.2.2",
"react-datepicker": "^7.6.0", "react-datepicker": "^7.6.0",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
@@ -35,16 +35,16 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-gtm-module": "^2.0.11", "react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-i18next": "^15.4.1", "react-i18next": "^15.4.0",
"react-konva": "^18.2.10", "react-konva": "^18.2.10",
"react-pdf": "^9.2.1", "react-pdf": "^9.2.1",
"react-quill-new": "^3.4.6", "react-quill-new": "^3.3.3",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-rnd": "^10.5.2", "react-rnd": "^10.4.14",
"react-router": "^7.1.5", "react-router": "^7.1.5",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-scrollbars-custom": "^4.1.1", "react-scrollbars-custom": "^4.1.1",
"react-select": "^5.10.1", "react-select": "^5.10.0",
"react-signature-canvas": "^1.0.7", "react-signature-canvas": "^1.0.7",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"react-timezone-select": "^3.2.8", "react-timezone-select": "^3.2.8",
@@ -53,11 +53,11 @@
"reactour": "^1.19.4", "reactour": "^1.19.4",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-thunk": "^3.1.0", "redux-thunk": "^3.1.0",
"regex-parser": "^2.3.1", "regex-parser": "^2.3.0",
"serve": "^14.2.4", "serve": "^14.2.4",
"styled-components": "^5.3.0", "styled-components": "^5.3.0",
"web-vitals": "^4.2.4", "web-vitals": "^4.2.4",
"ws": "^8.18.1", "ws": "^8.18.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
}, },
"scripts": { "scripts": {
@@ -96,26 +96,26 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.26.10", "@babel/core": "^7.26.9",
"@babel/preset-env": "^7.26.9", "@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3", "@babel/preset-react": "^7.26.3",
"@babel/runtime-corejs2": "^7.27.0", "@babel/runtime-corejs2": "^7.26.9",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.20",
"babel-loader": "^10.0.0", "babel-loader": "^9.2.1",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"daisyui": "^4.12.23", "daisyui": "^4.12.23",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"dotenv-webpack": "^8.1.0", "dotenv-webpack": "^8.1.0",
"eslint": "^9.23.0", "eslint": "^9.20.0",
"eslint-plugin-prettier": "^5.2.5", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"lint-staged": "^15.5.0", "lint-staged": "^15.4.3",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"postcss": "^8.5.3", "postcss": "^8.5.1",
"prettier": "^3.5.3", "prettier": "^3.5.0",
"pretty-quick": "^4.1.1", "pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"terser-webpack-plugin": "^5.3.11", "terser-webpack-plugin": "^5.3.11",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
@@ -151,8 +151,7 @@
"extend-expiry-date": "Ablaufdatum verlängern", "extend-expiry-date": "Ablaufdatum verlängern",
"Duplicate Template": "Vorlage duplizieren", "Duplicate Template": "Vorlage duplizieren",
"Duplicate": "Duplikat", "Duplicate": "Duplikat",
"daily-mail-quota": "Tägliches E-Mail-Kontingent", "daily-mail-quota": "Tägliches E-Mail-Kontingent"
"Save as template": "Als Vorlage speichern"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nr.", "Sr.No": "Nr.",
@@ -248,7 +247,7 @@
"generate-token-alert": "Sind Sie sicher, dass Sie das Token neu generieren möchten? Das alte Token wird ablaufen.", "generate-token-alert": "Sind Sie sicher, dass Sie das Token neu generieren möchten? Das alte Token wird ablaufen.",
"yes": "Ja", "yes": "Ja",
"copied": "Kopiert", "copied": "Kopiert",
"something-went-wrong-mssg": "Etwas ist schiefgelaufen, Das Aktualisieren dieser Seite kann das Problem lösen.", "something-went-wrong-mssg": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später erneut.",
"token-generated": "Token erfolgreich generiert.", "token-generated": "Token erfolgreich generiert.",
"webhook": "Webhook", "webhook": "Webhook",
"update-webhook": "Webhook aktualisieren", "update-webhook": "Webhook aktualisieren",
@@ -284,7 +283,6 @@
"make-template-public": "Vorlage öffentlich machen", "make-template-public": "Vorlage öffentlich machen",
"make-template-private": "Vorlage privat machen", "make-template-private": "Vorlage privat machen",
"make-template-public-alert": "Sind Sie sicher, dass Sie diese Vorlage öffentlich machen möchten?", "make-template-public-alert": "Sind Sie sicher, dass Sie diese Vorlage öffentlich machen möchten?",
"make-template-private-alert-non": "Sind Sie sicher, dass Sie diese Vorlage privat machen möchten?",
"make-template-private-alert": "Sind Sie sicher, dass Sie diese Vorlage privat machen möchten? Dadurch wird sie aus Ihrem öffentlichen Profil entfernt.", "make-template-private-alert": "Sind Sie sicher, dass Sie diese Vorlage privat machen möchten? Dadurch wird sie aus Ihrem öffentlichen Profil entfernt.",
"public-role": "Öffentliche Rolle", "public-role": "Öffentliche Rolle",
"public-url": "Öffentliches Profil", "public-url": "Öffentliches Profil",
@@ -310,7 +308,7 @@
"revoke-document": "Dokument widerrufen", "revoke-document": "Dokument widerrufen",
"revoke-document-alert": "Sind Sie sicher, dass Sie dieses Dokument widerrufen möchten?", "revoke-document-alert": "Sind Sie sicher, dass Sie dieses Dokument widerrufen möchten?",
"resend-mail": "E-Mail erneut senden", "resend-mail": "E-Mail erneut senden",
"resend-mail-help": "Sie können folgende Variablen verwenden, die durch ihre tatsächlichen Werte ersetzt werden: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "resend-mail-help": "Sie können folgende Variablen verwenden, die durch ihre tatsächlichen Werte ersetzt werden: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Betreff", "subject": "Betreff",
"body": "Inhalt", "body": "Inhalt",
"add-contact": "Kontakt hinzufügen", "add-contact": "Kontakt hinzufügen",
@@ -460,7 +458,6 @@
"placeholder-alert-3": "Sind Sie sicher, dass Sie dieses Dokument zur Unterzeichnung senden möchten?", "placeholder-alert-3": "Sind Sie sicher, dass Sie dieses Dokument zur Unterzeichnung senden möchten?",
"placeholder-alert-4": "Sie haben erfolgreich E-Mails an alle Empfänger gesendet!", "placeholder-alert-4": "Sie haben erfolgreich E-Mails an alle Empfänger gesendet!",
"placeholder-mail-alert": "Sie haben erfolgreich eine E-Mail an {{name}} gesendet. Die nachfolgenden Unterzeichner erhalten eine E-Mail, sobald {{name}} das Dokument unterzeichnet.", "placeholder-mail-alert": "Sie haben erfolgreich eine E-Mail an {{name}} gesendet. Die nachfolgenden Unterzeichner erhalten eine E-Mail, sobald {{name}} das Dokument unterzeichnet.",
"placeholder-mail-alert-you": "Nachfolgende Unterzeichner erhalten E-Mails, sobald Sie das Dokument unterschreiben.",
"placeholder-alert-5": "Möchten Sie die Dokumente jetzt unterschreiben?", "placeholder-alert-5": "Möchten Sie die Dokumente jetzt unterschreiben?",
"placeholder-alert-6": "Bitte richten Sie den E-Mail-Adapter ein, um E-Mails zu senden!", "placeholder-alert-6": "Bitte richten Sie den E-Mail-Adapter ein, um E-Mails zu senden!",
"placeholder-alert-7": "Bitte wählen Sie einen Unterzeichner aus, um Platzhalter hinzuzufügen!", "placeholder-alert-7": "Bitte wählen Sie einen Unterzeichner aus, um Platzhalter hinzuzufügen!",
@@ -524,7 +521,7 @@
"correct-password": "Bitte korrektes Passwort angeben", "correct-password": "Bitte korrektes Passwort angeben",
"decrypting-pdf": "PDF wird entschlüsselt, bitte warten...", "decrypting-pdf": "PDF wird entschlüsselt, bitte warten...",
"invalid-otp": "Ungültiger OTP", "invalid-otp": "Ungültiger OTP",
"user-not-found": "Benutzer nicht gefunden", "user-not-found": "Benutzer nicht gefunden!",
"enter-otp-alert": "Bitte OTP eingeben!", "enter-otp-alert": "Bitte OTP eingeben!",
"get-verification-code": "Verifizierungscode erhalten", "get-verification-code": "Verifizierungscode erhalten",
"get-verification-code-2": "Sie erhalten einen Verifizierungscode per E-Mail", "get-verification-code-2": "Sie erhalten einen Verifizierungscode per E-Mail",
@@ -834,7 +831,7 @@
"initial-type": "Ihre Initialen", "initial-type": "Ihre Initialen",
"redirect-url": "Weiterleitungs-URL", "redirect-url": "Weiterleitungs-URL",
"bulk-send": "Massenversand", "bulk-send": "Massenversand",
"select-timezone": "Zeitzone", "select-timezone": "Wählen Sie Ihre Zeitzone",
"current-time": "Aktuelle Uhrzeit", "current-time": "Aktuelle Uhrzeit",
"email-help": "Aus Sicherheitsgründen dürfen Sie die E-Mail-Adresse nicht ändern. Bitte erstellen Sie ein weiteres kostenloses Konto mit der neuen E-Mail-Adresse.", "email-help": "Aus Sicherheitsgründen dürfen Sie die E-Mail-Adresse nicht ändern. Bitte erstellen Sie ein weiteres kostenloses Konto mit der neuen E-Mail-Adresse.",
"doc-sent": "Dokument erfolgreich gesendet.", "doc-sent": "Dokument erfolgreich gesendet.",
@@ -859,27 +856,5 @@
"enabled-signature-type-help": "Die Einstellung 'Aktivierte Signaturtypen' bestimmt, welche Signaturoptionen in Ihrer Organisation verfügbar sind. Wenn Sie beispielsweise die Option 'Zeichnen' deaktivieren, wird sie den Mitgliedern Ihrer Organisation im Signatur-Widget nicht angezeigt, während die anderen drei Optionen weiterhin zugänglich bleiben.", "enabled-signature-type-help": "Die Einstellung 'Aktivierte Signaturtypen' bestimmt, welche Signaturoptionen in Ihrer Organisation verfügbar sind. Wenn Sie beispielsweise die Option 'Zeichnen' deaktivieren, wird sie den Mitgliedern Ihrer Organisation im Signatur-Widget nicht angezeigt, während die anderen drei Optionen weiterhin zugänglich bleiben.",
"indexing-public-profile": "Erlaube die Indexierung des öffentlichen Profils durch Suchmaschinen", "indexing-public-profile": "Erlaube die Indexierung des öffentlichen Profils durch Suchmaschinen",
"user-created-successfully": "Benutzer erfolgreich erstellt.", "user-created-successfully": "Benutzer erfolgreich erstellt.",
"only-15-reminder-allowed": "Sie können bis zu 15 automatische Erinnerungen festlegen. Wenn zum Beispiel 'TimeToComplete' auf 15 Tage und 'RemindOnceInEvery' auf 1 Tag eingestellt ist, erreichen Sie das maximale Limit von 15 Erinnerungen. Passen Sie Ihre Einstellungen entsprechend an.", "only-15-reminder-allowed": "Sie können bis zu 15 automatische Erinnerungen festlegen. Wenn zum Beispiel 'TimeToComplete' auf 15 Tage und 'RemindOnceInEvery' auf 1 Tag eingestellt ist, erreichen Sie das maximale Limit von 15 Erinnerungen. Passen Sie Ihre Einstellungen entsprechend an."
"rate-your-experience": "Wie war Ihre Erfahrung mit {{appName}}?",
"thanks-for-feedback": "Danke für Ihr Feedback 🙏",
"share-your-feedback": "Teilen Sie Ihr Feedback",
"share-your-review": "Teilen Sie Ihre Bewertung",
"date-format": "Datumsformat",
"document-deleted": "Das Dokument wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.",
"save-as-template-?": "Sind Sie sicher, dass Sie dieses Dokument als Vorlage speichern möchten?",
"go-to-manage-templates": "Zu 'Vorlagen verwalten' gehen",
"template-created": "Vorlage erstellt",
"how-would-you-like-to-proceed?": "Wie möchten Sie fortfahren?",
"failed-to-load-refresh-page": "Fehler beim Laden des Dokuments. Bitte versuchen Sie, diese Seite zu aktualisieren.",
"document-has-been-signed": "Das Dokument wurde erfolgreich unterschrieben!",
"document-has-been-signed-by-you": "Das Dokument wurde erfolgreich von Ihnen unterschrieben!",
"participant-completed-signing": "Alle Teilnehmer haben den Signaturprozess abgeschlossen.",
"you-will-receive-email-shortly": "✅ Das war's! Sie erhalten in Kürze eine Bestätigungs-E-Mail.",
"please-provide-templateid": "Bitte geben Sie templateid an",
"this-template-is-not-public": "Dieses template ist nicht öffentlich",
"invalid-templateid": "Ungültige templateid",
"contact-billing-at-opensign": "Um weitere Plätze hinzuzufügen, kontaktieren Sie bitte OpenSign™ unter <1>billing@opensignlabs.com</1> für Unterstützung.",
"title-length-alert": "Der Titel darf höchstens 250 Zeichen lang sein.",
"note-length-alert": "Die Notiz darf höchstens 200 Zeichen lang sein.",
"description-length-alert": "Die Beschreibung darf höchstens 500 Zeichen lang sein."
} }
@@ -151,8 +151,7 @@
"extend-expiry-date": "Extend expiry date", "extend-expiry-date": "Extend expiry date",
"Duplicate Template": "Duplicate template", "Duplicate Template": "Duplicate template",
"Duplicate": "Duplicate", "Duplicate": "Duplicate",
"daily-mail-quota": "Daily Email Quota", "daily-mail-quota": "Daily Email Quota"
"Save as template": "Save as template"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Sr.No", "Sr.No": "Sr.No",
@@ -248,7 +247,7 @@
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?", "generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
"yes": "Yes", "yes": "Yes",
"copied": "Copied", "copied": "Copied",
"something-went-wrong-mssg": "Something went wrong, refreshing this page may solve this issue.", "something-went-wrong-mssg": "Something went wrong, please try again later.",
"token-generated": "Token generated successfully.", "token-generated": "Token generated successfully.",
"webhook": "Webhook", "webhook": "Webhook",
"update-webhook": "Update webhook", "update-webhook": "Update webhook",
@@ -284,12 +283,11 @@
"make-template-public": "Make template public", "make-template-public": "Make template public",
"make-template-private": "Make template private", "make-template-private": "Make template private",
"make-template-public-alert": "Are you sure you want to make this template public?", "make-template-public-alert": "Are you sure you want to make this template public?",
"make-template-private-alert-non": "Are you sure you want to make this template private?",
"make-template-private-alert": "Are you sure you want to make this template private? This will remove it from your public profile.", "make-template-private-alert": "Are you sure you want to make this template private? This will remove it from your public profile.",
"public-role": "Public role", "public-role": "Public role",
"public-url": "Public profile", "public-url": "Public profile",
"embed-template": "Embed template", "embed-template": "Embed template",
"public-url-copy": "Here's your public URL: ", "public-url-copy": "Heres your public URL: ",
"public-url-copy-mssg": "Copy it or share it with the signer, and you will be able to see all your publicly set templates.", "public-url-copy-mssg": "Copy it or share it with the signer, and you will be able to see all your publicly set templates.",
"add-public-url-alert": "You can generate your {{appName}} public profile", "add-public-url-alert": "You can generate your {{appName}} public profile",
"share-with-alert": "You cannot share a template if any roles already have contacts assigned. Please remove all contact assignments from the roles before sharing the template.", "share-with-alert": "You cannot share a template if any roles already have contacts assigned. Please remove all contact assignments from the roles before sharing the template.",
@@ -310,12 +308,12 @@
"revoke-document": "Revoke document", "revoke-document": "Revoke document",
"revoke-document-alert": "Are you sure you want to revoke this document?", "revoke-document-alert": "Are you sure you want to revoke this document?",
"resend-mail": "Resend mail", "resend-mail": "Resend mail",
"resend-mail-help": "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "resend-mail-help": "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Subject", "subject": "Subject",
"body": "Body", "body": "Body",
"add-contact": "Add contact", "add-contact": "Add contact",
"edit-contact": "Edit contact", "edit-contact": "Edit contact",
"add-signer-alert": "Contact already exist! Please select it from 'Signers' dropdown", "add-signer-alert": "Contact already exist! Please select it from Signers dropdown",
"record-delete-alert": "Record deleted successfully!", "record-delete-alert": "Record deleted successfully!",
"record-revoke-alert": "Record revoked successfully!", "record-revoke-alert": "Record revoked successfully!",
"mail-sent-alert": "Mail sent successfully.", "mail-sent-alert": "Mail sent successfully.",
@@ -460,7 +458,6 @@
"placeholder-alert-3": " Are you sure you want to send out this document for signatures?", "placeholder-alert-3": " Are you sure you want to send out this document for signatures?",
"placeholder-alert-4": "You have successfully sent mails to all recipients!", "placeholder-alert-4": "You have successfully sent mails to all recipients!",
"placeholder-mail-alert": "You have successfully sent email to {{name}}. Subsequent signers will get email(s) once {{name}} signs the document", "placeholder-mail-alert": "You have successfully sent email to {{name}}. Subsequent signers will get email(s) once {{name}} signs the document",
"placeholder-mail-alert-you": "Subsequent signers will get email(s) once you signs the document.",
"placeholder-alert-5": "Do you want to sign the document right now?", "placeholder-alert-5": "Do you want to sign the document right now?",
"placeholder-alert-6": "Please setup mail adapter to send mail!", "placeholder-alert-6": "Please setup mail adapter to send mail!",
"placeholder-alert-7": "Please select signer for add placeholder!", "placeholder-alert-7": "Please select signer for add placeholder!",
@@ -524,7 +521,7 @@
"correct-password": "Please provide correct password", "correct-password": "Please provide correct password",
"decrypting-pdf": " Decrypting pdf please wait...", "decrypting-pdf": " Decrypting pdf please wait...",
"invalid-otp": "Invalid otp", "invalid-otp": "Invalid otp",
"user-not-found": "User not found", "user-not-found": "User not found!",
"enter-otp-alert": "Please enter OTP!", "enter-otp-alert": "Please enter OTP!",
"get-verification-code": "Get verification code", "get-verification-code": "Get verification code",
"get-verification-code-2": "You will get a verification code via Email", "get-verification-code-2": "You will get a verification code via Email",
@@ -587,7 +584,7 @@
}, },
"tour-mssg": { "tour-mssg": {
"home-layout-1": "You have logged in successfully! Let's take a look.", "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.", "home-layout-2": "To upload documents for self-signing or to request others signatures, simply select the respective buttons.",
"home-layout-3": "You are ready to start using {{appName}}! If you need support feel free to contact us.", "home-layout-3": "You are ready to start using {{appName}}! If you need support feel free to contact us.",
"generate-token": "Upgrade now to generate production API token.", "generate-token": "Upgrade now to generate production API token.",
"opensign-drive-1": "Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.", "opensign-drive-1": "Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.",
@@ -596,7 +593,7 @@
"opensign-drive-4": "Click on this menu to display the documents in list view.", "opensign-drive-4": "Click on this menu to display the documents in list view.",
"opensign-drive-5": "The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.", "opensign-drive-5": "The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.",
"opensign-drive-6": "Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.", "opensign-drive-6": "Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.",
"opensign-drive-7": "Right-click on any folder to see options. Choose 'Rename' to change the folder's name or click on the folder to navigate through its contents.", "opensign-drive-7": "Right-click on any folder to see options. Choose Rename to change the folders name or click on the folder to navigate through its contents.",
"pdf-request-file-1": "List of signers who still need to sign the document .", "pdf-request-file-1": "List of signers who still need to sign the document .",
"pdf-request-file-2": "Click any of the placeholders appearing on the document to sign. You will then see options to draw your signature, type it, or upload an image .", "pdf-request-file-2": "Click any of the placeholders appearing on the document to sign. You will then see options to draw your signature, type it, or upload an image .",
"pdf-request-file-3": "Click Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .", "pdf-request-file-3": "Click Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .",
@@ -609,24 +606,24 @@
"placeholder-sign-4": "Drag or click on a field to add it to the document.", "placeholder-sign-4": "Drag or click on a field to add it to the document.",
"placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.", "placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
"placeholder-sign-6": "Clicking 'Next' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.", "placeholder-sign-6": "Clicking 'Next' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
"report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like 'HR Manager' and 'New Employee'. Each time you use the template, you can assign the 'New Employee' role to different incoming staff members, while the 'HR Manager' role remains constant, facilitating a seamless onboarding process for each recruit. ", "report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like HR Manager and New Employee. Each time you use the template, you can assign the New Employee role to different incoming staff members, while the HR Manager role remains constant, facilitating a seamless onboarding process for each recruit. ",
"redirect": "Click the 'Use' button to create a new document from an existing template.", "redirect": "Click the 'Use' button to create a new document from an existing template.",
"bulksend": "To quickly send multiple documents using an existing template by just creating the recipient email addresses, click the 'Bulk Send' button.", "bulksend": "To quickly send multiple documents using an existing template by just creating the recipient email addresses, click the 'Bulk Send' button.",
"option": "This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won't affect existing documents.Use the Delete button you can delete template. ", "option": "This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
"signyour-self-1": "Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.", "signyour-self-1": "Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.",
"signyour-self-2": "Drag and drop anywhere in this area. You can resize and move it later.", "signyour-self-2": "Drag and drop anywhere in this area. You can resize and move it later.",
"template-placeholder-1": "Clicking 'Add role' button will allow you to add various signer roles. You can attach users to each role in subsequent steps.", "template-placeholder-1": "Clicking 'Add role' button will allow you to add various signer roles. You can attach users to each role in subsequent steps.",
"template-placeholder-2": "Once roles are added, select a role from list to add a place-holder where he is supposed to sign. The placeholder will appear in the same colour as the role name once you drop it on the document.", "template-placeholder-2": "Once roles are added, select a role from list to add a place-holder where he is supposed to sign. The placeholder will appear in the same colour as the role name once you drop it on the document.",
"template-placeholder-3": "Drag or click on a field to add it to the document.", "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-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, you'll be prompted to create a new document from this template if you wish.", "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.", "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.", "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.", "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.", "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.", "Drafts": "This are documents you have started but have not finalized for sending.",
"public-template": "This video demonstrates how to set up your personalized public profile, such as 'https://opensign.me/your-username'. You'll also learn how to customize your tagline and make your templates available for public signing.", "public-template": "This video demonstrates how to set up your personalized public profile, such as https://opensign.me/your-username. Youll also learn how to customize your tagline and make your templates available for public signing.",
"allowModify-widgets": "You can drag and drop any of these fields onto the document, in addition to the fields already designated for you by the document creator." "allowModify-widgets": "You can drag and drop any of these fields onto the document, in addition to the fields already designated for you by the document creator."
}, },
"enter-email-plaholder": "Add an email address and hit enter", "enter-email-plaholder": "Add an email address and hit enter",
@@ -660,13 +657,13 @@
"bulk-send-subcription-alert": "Please upgrade to Professional or Team plan to use bulk send.", "bulk-send-subcription-alert": "Please upgrade to Professional or Team plan to use bulk send.",
"generate-test-token": "Generate test token", "generate-test-token": "Generate test token",
"regenerate-test-token": "Regenerate test token", "regenerate-test-token": "Regenerate test token",
"help-test-token": "This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once you've completed your testing, you can upgrade to one of our paid plans to generate a production token.", "help-test-token": "This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once youve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
"help-api-token": "This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.", "help-api-token": "This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
"reason": "Reason", "reason": "Reason",
"decline-by": "Declined/revoked by", "decline-by": "Declined/revoked by",
"document-declined": "Document declined", "document-declined": "Document declined",
"public-template-mssg-1": "To integrate OpenSign into your React or Next.js project, simply run the following command:", "public-template-mssg-1": "To integrate OpenSign into your React or Next.js project, simply run the following command:",
"public-template-mssg-2": "Ensure you have npm or yarn set up in your project. If you're using Yarn, you can replace npm install with yarn add @opensign/react.", "public-template-mssg-2": "Ensure you have npm or yarn set up in your project. If youre using Yarn, you can replace npm install with yarn add @opensign/react.",
"public-template-mssg-3": "Need more details or examples?", "public-template-mssg-3": "Need more details or examples?",
"public-template-mssg-4": "Visit the", "public-template-mssg-4": "Visit the",
"public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.", "public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.",
@@ -794,7 +791,7 @@
"term-cond-p22": "Understand that {{appName}} is a platform facilitating the transaction and is not a party to the agreement.", "term-cond-p22": "Understand that {{appName}} is a platform facilitating the transaction and is not a party to the agreement.",
"term-cond-h7": "7. Legal Effect", "term-cond-h7": "7. Legal Effect",
"term-cond-p23": "Your electronic signature facilitated through {{appName}}:", "term-cond-p23": "Your electronic signature facilitated through {{appName}}:",
"term-cond-p24": "Complies with applicable electronic signature laws, including but not limited to the E-SIGN Act in the United States, the EU eIDAS Regulation, and India's Information Technology Act.", "term-cond-p24": "Complies with applicable electronic signature laws, including but not limited to the E-SIGN Act in the United States, the EU eIDAS Regulation, and Indias Information Technology Act.",
"term-cond-p25": "Is legally binding between You and the Sender for the signed document(s).", "term-cond-p25": "Is legally binding between You and the Sender for the signed document(s).",
"term-cond-h8": "8. Platform Role and Limitation of Liability", "term-cond-h8": "8. Platform Role and Limitation of Liability",
"term-cond-p26": "{{appName}} serves as a platform to facilitate electronic transactions. It is not responsible for the content, validity, or enforceability of the documents sent by the Sender. Any disputes or issues related to the document or its signing must be resolved directly between You and the Sender.", "term-cond-p26": "{{appName}} serves as a platform to facilitate electronic transactions. It is not responsible for the content, validity, or enforceability of the documents sent by the Sender. Any disputes or issues related to the document or its signing must be resolved directly between You and the Sender.",
@@ -834,7 +831,7 @@
"initial-type": "Your initials", "initial-type": "Your initials",
"redirect-url": "Redirect url", "redirect-url": "Redirect url",
"bulk-send": "Bulk send", "bulk-send": "Bulk send",
"select-timezone": "Timezone", "select-timezone": "Select your Timezone",
"current-time": "Current time", "current-time": "Current time",
"email-help": "You are not allowed to change email address due to security reasons. Please create another free account using the new email address.", "email-help": "You are not allowed to change email address due to security reasons. Please create another free account using the new email address.",
"doc-sent": "Document sent successfully.", "doc-sent": "Document sent successfully.",
@@ -854,32 +851,10 @@
"draft-template-info-p1": "To make your template public, it must either contain a single role, or, if it includes multiple roles, all additional roles must already be assigned to signers. The unassigned public role should remain empty and must be placed in the first position.", "draft-template-info-p1": "To make your template public, it must either contain a single role, or, if it includes multiple roles, all additional roles must already be assigned to signers. The unassigned public role should remain empty and must be placed in the first position.",
"visit-below-link": "Visit below link to know more -", "visit-below-link": "Visit below link to know more -",
"storage-help": "Enabling BYOC lets you connect your own S3 storage so your files remain entirely under your control—no external copies retained. If data autonomy matters to you, consider upgrading to Teams to unlock this feature.", "storage-help": "Enabling BYOC lets you connect your own S3 storage so your files remain entirely under your control—no external copies retained. If data autonomy matters to you, consider upgrading to Teams to unlock this feature.",
"daily-quota-reached": "You've reached your daily quota. For assistance, please contact quotas@opensignlabs.com.", "daily-quota-reached": "Youve reached your daily quota. For assistance, please contact quotas@opensignlabs.com.",
"enabled-signature-type": "Enabled Signature Types", "enabled-signature-type": "Enabled Signature Types",
"enabled-signature-type-help": "The 'Enabled Signature Types' setting determines which signature options are available across your organization. For example, if you disable the 'Draw' option, members of your organization will not see it in the signature widget, while the other three options will remain accessible.", "enabled-signature-type-help": "The 'Enabled Signature Types' setting determines which signature options are available across your organization. For example, if you disable the 'Draw' option, members of your organization will not see it in the signature widget, while the other three options will remain accessible.",
"indexing-public-profile": "Allow indexing of public profile by search engines", "indexing-public-profile": "Allow indexing of public profile by search engines",
"user-created-successfully": "user created successfully.", "user-created-successfully": "user created successfully.",
"only-15-reminder-allowed": "You can set up to 15 automatic reminders. For example, if 'TimeToComplete' is 15 days and 'RemindOnceInEvery' is 1 day, you'll reach the maximum limit of 15 reminders. Adjust your settings accordingly.", "only-15-reminder-allowed": "You can set up to 15 automatic reminders. For example, if 'TimeToComplete' is 15 days and 'RemindOnceInEvery' is 1 day, you'll reach the maximum limit of 15 reminders. Adjust your settings accordingly."
"rate-your-experience": "How was your experience with {{appName}}?",
"thanks-for-feedback": "Thanks for your feedback 🙏",
"share-your-feedback": "Share your feedback",
"share-your-review": "Share your review",
"date-format": "Date format",
"document-deleted": "The document has been deleted or you don't have access. Please contact the sender.",
"save-as-template-?": "Are you sure you want to save this document as template?",
"go-to-manage-templates": "go to 'Manage templates'",
"template-created": "Template Created",
"how-would-you-like-to-proceed?": "How would you like to proceed?",
"failed-to-load-refresh-page": "Failed to load the document. Please try refreshing this page.",
"document-has-been-signed": "The document has been signed successfully!",
"document-has-been-signed-by-you": "The document has been successfully signed by you!",
"participant-completed-signing": "All participants have completed the signing process.",
"you-will-receive-email-shortly": "✅ That's it! You'll receive a confirmation email shortly.",
"please-provide-templateid": "Please provide templateid",
"this-template-is-not-public": "This template is not public",
"invalid-templateid": "Invaldi templateid",
"contact-billing-at-opensign": "To add more seats, please contact OpenSign™ at <1>billing@opensignlabs.com</1> for assistance",
"title-length-alert": "Title must be at most 250 characters long.",
"note-length-alert": "Note must be at most 200 characters long.",
"description-length-alert": "Description must be at most 500 characters long."
} }
@@ -151,8 +151,7 @@
"extend-expiry-date": "Date d'expiration", "extend-expiry-date": "Date d'expiration",
"Duplicate Template": "Plantilla duplicada", "Duplicate Template": "Plantilla duplicada",
"Duplicate": "Duplicada", "Duplicate": "Duplicada",
"daily-mail-quota": "Cuota diaria de correos electrónicos", "daily-mail-quota": "Cuota diaria de correos electrónicos"
"Save as template": "Guardar como plantilla"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nº", "Sr.No": "Nº",
@@ -249,7 +248,7 @@
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.", "generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
"yes": "Sí", "yes": "Sí",
"copied": "Copiado", "copied": "Copiado",
"something-went-wrong-mssg": "Un problème est survenu, Actualiser cette page peut résoudre le problème.", "something-went-wrong-mssg": "Algo salió mal, por favor, intenta de nuevo más tarde.",
"token-generated": "Token generado exitosamente.", "token-generated": "Token generado exitosamente.",
"webhook": "Webhook", "webhook": "Webhook",
"update-webhook": "Actualizar webhook", "update-webhook": "Actualizar webhook",
@@ -285,7 +284,6 @@
"make-template-public": "Convertir la plantilla en pública", "make-template-public": "Convertir la plantilla en pública",
"make-template-private": "Convertir la plantilla en privada", "make-template-private": "Convertir la plantilla en privada",
"make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?", "make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?",
"make-template-private-alert-non": "¿Está seguro de que desea hacer este plantilla privado?",
"make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.", "make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.",
"public-role": "Rol público", "public-role": "Rol público",
"public-url": "Perfil publico", "public-url": "Perfil publico",
@@ -311,7 +309,7 @@
"revoke-document": "Revocar documento", "revoke-document": "Revocar documento",
"revoke-document-alert": "¿En definitiva quieres revocar este documento?", "revoke-document-alert": "¿En definitiva quieres revocar este documento?",
"resend-mail": "Reenviar correo", "resend-mail": "Reenviar correo",
"resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Asunto", "subject": "Asunto",
"body": "Cuerpo", "body": "Cuerpo",
"add-contact": "Agregar contacto", "add-contact": "Agregar contacto",
@@ -460,7 +458,6 @@
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?", "placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!", "placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
"placeholder-mail-alert": "Has enviado un correo electrónico con éxito a {{name}}. Los siguientes firmantes recibirán un correo electrónico una vez que {{name}} firme el documento.", "placeholder-mail-alert": "Has enviado un correo electrónico con éxito a {{name}}. Los siguientes firmantes recibirán un correo electrónico una vez que {{name}} firme el documento.",
"placeholder-mail-alert-you": "Los firmantes posteriores recibirán correos electrónicos una vez que firme el documento.",
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?", "placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!", "placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!", "placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
@@ -524,7 +521,7 @@
"correct-password": "Por favor, proporciona la contraseña correcta", "correct-password": "Por favor, proporciona la contraseña correcta",
"decrypting-pdf": " Desencriptando PDF, por favor, espera...", "decrypting-pdf": " Desencriptando PDF, por favor, espera...",
"invalid-otp": "OTP inválido", "invalid-otp": "OTP inválido",
"user-not-found": "Usuario no encontrado", "user-not-found": "¡Usuario no encontrado!",
"enter-otp-alert": "¡Por favor, ingresa el OTP!", "enter-otp-alert": "¡Por favor, ingresa el OTP!",
"get-verification-code": "Obtener código de verificación", "get-verification-code": "Obtener código de verificación",
"get-verification-code-2": "Obtendrás un código de verificación por correo", "get-verification-code-2": "Obtendrás un código de verificación por correo",
@@ -834,7 +831,7 @@
"initial-type": "Tus iniciales", "initial-type": "Tus iniciales",
"redirect-url": "URL de redireccionamiento", "redirect-url": "URL de redireccionamiento",
"bulk-send": "envío masivo", "bulk-send": "envío masivo",
"select-timezone": "Zona horaria", "select-timezone": "Seleccione su zona horaria",
"current-time": "Hora actual", "current-time": "Hora actual",
"email-help": "No se permite cambiar la dirección de correo electrónico por razones de seguridad. Por favor, cree otra cuenta gratuita utilizando la nueva dirección de correo electrónico.", "email-help": "No se permite cambiar la dirección de correo electrónico por razones de seguridad. Por favor, cree otra cuenta gratuita utilizando la nueva dirección de correo electrónico.",
"doc-sent": "Documento enviado con éxito.", "doc-sent": "Documento enviado con éxito.",
@@ -859,27 +856,5 @@
"enabled-signature-type-help": "La configuración de 'Tipos de firma habilitados' determina qué opciones de firma están disponibles en su organización. Por ejemplo, si desactiva la opción 'Dibujar', los miembros de su organización no la verán en el widget de firma, mientras que las otras tres opciones seguirán siendo accesibles.", "enabled-signature-type-help": "La configuración de 'Tipos de firma habilitados' determina qué opciones de firma están disponibles en su organización. Por ejemplo, si desactiva la opción 'Dibujar', los miembros de su organización no la verán en el widget de firma, mientras que las otras tres opciones seguirán siendo accesibles.",
"indexing-public-profile": "Permitir la indexación del perfil público por los motores de búsqueda", "indexing-public-profile": "Permitir la indexación del perfil público por los motores de búsqueda",
"user-created-successfully": "Usuario creado con éxito.", "user-created-successfully": "Usuario creado con éxito.",
"only-15-reminder-allowed": "Puede configurar hasta 15 recordatorios automáticos. Por ejemplo, si 'TimeToComplete' es de 15 días y 'RemindOnceInEvery' es de 1 día, alcanzará el límite máximo de 15 recordatorios. Ajuste su configuración en consecuencia.", "only-15-reminder-allowed": "Puede configurar hasta 15 recordatorios automáticos. Por ejemplo, si 'TimeToComplete' es de 15 días y 'RemindOnceInEvery' es de 1 día, alcanzará el límite máximo de 15 recordatorios. Ajuste su configuración en consecuencia."
"rate-your-experience": "¿Cómo fue su experiencia con {{appName}}?",
"thanks-for-feedback": "Gracias por su comentario 🙏",
"share-your-feedback": "Comparta sus comentarios",
"share-your-review": "Comparta su reseña",
"date-format": "Formato de fecha",
"document-deleted": "El documento ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.",
"save-as-template-?": "¿Está seguro de que desea guardar este documento como plantilla?",
"go-to-manage-templates": "Ir a 'Gestionar plantillas'",
"template-created": "Plantilla creada",
"how-would-you-like-to-proceed?": "¿Cómo le gustaría proceder?",
"failed-to-load-refresh-page": "Error al cargar el documento. Intente actualizar esta página.",
"document-has-been-signed": "¡El documento ha sido firmado con éxito!",
"document-has-been-signed-by-you": "¡El documento ha sido firmado con éxito por usted!",
"participant-completed-signing": "Todos los participantes han completado el proceso de firma.",
"you-will-receive-email-shortly": "✅ ¡Eso es todo! Recibirá un correo de confirmación en breve.",
"please-provide-templateid": "Por favor, proporcione templateid",
"this-template-is-not-public": "Esta template no es pública",
"invalid-templateid": "templateid no válida",
"contact-billing-at-opensign": "Para agregar más asientos, comuníquese con OpenSign™ a <1>billing@opensignlabs.com</1> para obtener ayuda.",
"title-length-alert": "El título debe tener como máximo 250 caracteres.",
"note-length-alert": "La nota debe tener como máximo 200 caracteres.",
"description-length-alert": "La descripción debe tener como máximo 500 caracteres."
} }
@@ -172,8 +172,7 @@
"extend-expiry-date": "Prolonger la date d'expiration", "extend-expiry-date": "Prolonger la date d'expiration",
"Duplicate Template": "dupliquer le modèle", "Duplicate Template": "dupliquer le modèle",
"Duplicate": "Double", "Duplicate": "Double",
"daily-mail-quota": "Quota d'e-mails quotidien", "daily-mail-quota": "Quota d'e-mails quotidien"
"Save as template": "Enregistrer comme modèle"
}, },
"report-help": { "report-help": {
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.", "Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
@@ -201,7 +200,7 @@
"send-in-order-help": { "send-in-order-help": {
"p1": "Choisissez la manière dont vous souhaitez que les demandes de signature soient envoyées aux signataires du document :", "p1": "Choisissez la manière dont vous souhaitez que les demandes de signature soient envoyées aux signataires du document :",
"p2": "La sélection de cette option enverra initialement la demande de signature au premier signataire. Une fois que le premier signataire a terminé sa partie, le prochain signataire de la séquence recevra la demande. Ce processus se poursuit jusqu'à ce que tous les signataires aient signé le document. Cette méthode garantit que le document est signé dans un ordre spécifique.", "p2": "La sélection de cette option enverra initialement la demande de signature au premier signataire. Une fois que le premier signataire a terminé sa partie, le prochain signataire de la séquence recevra la demande. Ce processus se poursuit jusqu'à ce que tous les signataires aient signé le document. Cette méthode garantit que le document est signé dans un ordre spécifique.",
"p3": "La sélection de cette option enverra les liens de signature à tous les signataires simultanément. Chaque signataire peut signer le document à sa convenance, que d'autres signataires aient ou non complété leur signature. Cette méthode est plus rapide mais n'impose aucun ordre de signature entre les participants.", "p3": "La sélection de cette option enverra les liens de signature à tous les signataires simultanément. Chaque signataire peut signer le document à sa convenance, que d'autres signataires aient ou non complété leur signature. Cette méthode est plus rapide mais nimpose aucun ordre de signature entre les participants.",
"p4": "Sélectionnez l'option qui correspond le mieux aux besoins de votre traitement de documents." "p4": "Sélectionnez l'option qui correspond le mieux aux besoins de votre traitement de documents."
}, },
"no": "Non", "no": "Non",
@@ -227,7 +226,7 @@
"create": "Créer", "create": "Créer",
"signers": "Signataires", "signers": "Signataires",
"signers-help": "Commencez à saisir le nom d'un contact pour voir les signataires suggérés par vos contacts enregistrés ou en ajouter de nouveaux. Organisez l'ordre de signature en ajoutant des signataires dans l'ordre souhaité. Utilisez le bouton « + » pour inclure les signataires et le « x » pour les supprimer. Chaque signataire recevra un e-mail invité à signer le document dans l'ordre indiqué.", "signers-help": "Commencez à saisir le nom d'un contact pour voir les signataires suggérés par vos contacts enregistrés ou en ajouter de nouveaux. Organisez l'ordre de signature en ajoutant des signataires dans l'ordre souhaité. Utilisez le bouton « + » pour inclure les signataires et le « x » pour les supprimer. Chaque signataire recevra un e-mail invité à signer le document dans l'ordre indiqué.",
"bcc-help": "Commencez à taper le nom d'un contact pour voir les suggestions parmi vos contacts enregistrés ou en ajouter de nouveaux. Utilisez le bouton '+' pour ajouter un utilisateur et le bouton 'x' pour le supprimer. L'adresse e-mail de l'utilisateur sélectionné sera ajoutée en Bcc (copie carbone invisible). Chaque utilisateur recevra une notification par e-mail une fois le document terminé.", "bcc-help": "Commencez à taper le nom dun contact pour voir les suggestions parmi vos contacts enregistrés ou en ajouter de nouveaux. Utilisez le bouton '+' pour ajouter un utilisateur et le bouton 'x' pour le supprimer. Ladresse e-mail de lutilisateur sélectionné sera ajoutée en Bcc (copie carbone invisible). Chaque utilisateur recevra une notification par e-mail une fois le document terminé.",
"add-signer": "Ajouter un signataire", "add-signer": "Ajouter un signataire",
"contact-not-found": "Contact introuvable", "contact-not-found": "Contact introuvable",
"add-yourself": "Ajoutez-vous", "add-yourself": "Ajoutez-vous",
@@ -284,7 +283,6 @@
"make-template-public": "Rendre le modèle public", "make-template-public": "Rendre le modèle public",
"make-template-private": "Rendre le modèle privé", "make-template-private": "Rendre le modèle privé",
"make-template-public-alert": "Êtes-vous sûr de vouloir rendre ce modèle public ?", "make-template-public-alert": "Êtes-vous sûr de vouloir rendre ce modèle public ?",
"make-template-private-alert-non": "Êtes-vous sûr de vouloir rendre ce modèle privé ?",
"make-template-private-alert": "Êtes-vous sûr de vouloir rendre ce modèle privé ? Cela le supprimera de votre profil public.", "make-template-private-alert": "Êtes-vous sûr de vouloir rendre ce modèle privé ? Cela le supprimera de votre profil public.",
"public-role": "Rôle public", "public-role": "Rôle public",
"public-url": "Profil public", "public-url": "Profil public",
@@ -310,7 +308,7 @@
"revoke-document": "Révoquer le document", "revoke-document": "Révoquer le document",
"revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?", "revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?",
"resend-mail": "Renvoyer le courrier", "resend-mail": "Renvoyer le courrier",
"resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Sujet", "subject": "Sujet",
"body": "Corps", "body": "Corps",
"add-contact": "Ajouter le contact", "add-contact": "Ajouter le contact",
@@ -460,7 +458,6 @@
"placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ", "placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ",
"placeholder-alert-4": "Vous avez envoyé avec succès des mails à tous les", "placeholder-alert-4": "Vous avez envoyé avec succès des mails à tous les",
"placeholder-mail-alert": "Vous avez envoyé un e-mail avec succès à {{name}}. Les signataires suivants recevront un e-mail une fois que {{name}} aura signé le document.", "placeholder-mail-alert": "Vous avez envoyé un e-mail avec succès à {{name}}. Les signataires suivants recevront un e-mail une fois que {{name}} aura signé le document.",
"placeholder-mail-alert-you": "Les signataires suivants recevront un e-mail dès que vous signez le document.",
"placeholder-alert-5": "Voulez-vous signer des documents maintenant ?", "placeholder-alert-5": "Voulez-vous signer des documents maintenant ?",
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !", "placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !",
"placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !", "placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
@@ -524,7 +521,7 @@
"correct-password": "Veuillez fournir un mot de passe correct", "correct-password": "Veuillez fournir un mot de passe correct",
"decrypting-pdf": "Décryptage du pdf, veuillez patienter...", "decrypting-pdf": "Décryptage du pdf, veuillez patienter...",
"invalid-otp": "OTP invalide", "invalid-otp": "OTP invalide",
"user-not-found": "Utilisateur non trouvé", "user-not-found": "Utilisateur non trouvé!",
"enter-otp-alert": "Veuillez saisir OTP !", "enter-otp-alert": "Veuillez saisir OTP !",
"get-verification-code": "Obtenir le code de vérification", "get-verification-code": "Obtenir le code de vérification",
"get-verification-code-2": "Vous recevrez un code de vérification par e-mail", "get-verification-code-2": "Vous recevrez un code de vérification par e-mail",
@@ -660,7 +657,7 @@
"bulk-send-subcription-alert": "Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.", "bulk-send-subcription-alert": "Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.",
"generate-test-token": "Générer jeton de test", "generate-test-token": "Générer jeton de test",
"regenerate-test-token": "Régénérer le jeton de test", "regenerate-test-token": "Régénérer le jeton de test",
"help-test-token": "Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à l'un de nos forfaits payants pour générer un jeton de production.", "help-test-token": "Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à lun de nos forfaits payants pour générer un jeton de production.",
"help-api-token": "Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.", "help-api-token": "Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.",
"reason": "Raison", "reason": "Raison",
"decline-by": "Refusé/révoqué par", "decline-by": "Refusé/révoqué par",
@@ -834,7 +831,7 @@
"initial-type": "Vos initiales", "initial-type": "Vos initiales",
"redirect-url": "URL de redirection", "redirect-url": "URL de redirection",
"bulk-send": "Envoi groupé", "bulk-send": "Envoi groupé",
"select-timezone": "Fuseau horaire", "select-timezone": "Sélectionnez votre fuseau horaire",
"current-time": "Heure actuelle", "current-time": "Heure actuelle",
"email-help": "Vous n'êtes pas autorisé à changer d'adresse e-mail pour des raisons de sécurité. Veuillez créer un autre compte gratuit en utilisant la nouvelle adresse e-mail.", "email-help": "Vous n'êtes pas autorisé à changer d'adresse e-mail pour des raisons de sécurité. Veuillez créer un autre compte gratuit en utilisant la nouvelle adresse e-mail.",
"doc-sent": "Document envoyé avec succès.", "doc-sent": "Document envoyé avec succès.",
@@ -859,27 +856,5 @@
"enabled-signature-type-help": "Le paramètre 'Types de signature activés' détermine quelles options de signature sont disponibles dans votre organisation. Par exemple, si vous désactivez l'option 'Dessiner', les membres de votre organisation ne la verront pas dans le widget de signature, tandis que les trois autres options resteront accessibles.", "enabled-signature-type-help": "Le paramètre 'Types de signature activés' détermine quelles options de signature sont disponibles dans votre organisation. Par exemple, si vous désactivez l'option 'Dessiner', les membres de votre organisation ne la verront pas dans le widget de signature, tandis que les trois autres options resteront accessibles.",
"indexing-public-profile": "Autoriser l'indexation du profil public par les moteurs de recherche", "indexing-public-profile": "Autoriser l'indexation du profil public par les moteurs de recherche",
"user-created-successfully": "Utilisateur créé avec succès.", "user-created-successfully": "Utilisateur créé avec succès.",
"only-15-reminder-allowed": "Vous pouvez définir jusqu'à 15 rappels automatiques. Par exemple, si 'TimeToComplete' est de 15 jours et 'RemindOnceInEvery' est de 1 jour, vous atteindrez la limite maximale de 15 rappels. Ajustez vos paramètres en conséquence.", "only-15-reminder-allowed": "Vous pouvez définir jusqu'à 15 rappels automatiques. Par exemple, si 'TimeToComplete' est de 15 jours et 'RemindOnceInEvery' est de 1 jour, vous atteindrez la limite maximale de 15 rappels. Ajustez vos paramètres en conséquence."
"rate-your-experience": "Comment s'est passée votre expérience avec {{appName}} ?",
"thanks-for-feedback": "Merci pour votre retour 🙏",
"share-your-feedback": "Partagez votre avis",
"share-your-review": "Partagez votre avis",
"date-format": "Format de date",
"document-deleted": "Le document a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur.",
"save-as-template-?": "Êtes-vous sûr de vouloir enregistrer ce document comme modèle ?",
"go-to-manage-templates": "Aller à 'Gérer les modèles'",
"template-created": "Modèle créé",
"how-would-you-like-to-proceed?": "Comment souhaitez-vous procéder ?",
"failed-to-load-refresh-page": "Échec du chargement du document. Veuillez essayer d'actualiser cette page.",
"document-has-been-signed": "Le document a été signé avec succès !",
"document-has-been-signed-by-you": "Le document a été signé avec succès par vous !",
"participant-completed-signing": "Tous les participants ont terminé le processus de signature.",
"you-will-receive-email-shortly": "✅ Voilà, c'est fait ! Vous recevrez un e-mail de confirmation sous peu.",
"please-provide-templateid": "Veuillez fournir templateid",
"this-template-is-not-public": "Ce template n'est pas public",
"invalid-templateid": "templateid invalide",
"contact-billing-at-opensign": "Pour ajouter plus de places, veuillez contacter OpenSign™ à l'adresse <1>billing@opensignlabs.com</1> pour obtenir de l'aide.",
"title-length-alert": "Le titre doit comporter au maximum 250 caractères.",
"note-length-alert": "La note doit comporter au maximum 200 caractères.",
"description-length-alert": "La description doit comporter au maximum 500 caractères"
} }
@@ -151,8 +151,7 @@
"extend-expiry-date": "Estendi data di scadenza", "extend-expiry-date": "Estendi data di scadenza",
"Duplicate Template": "Duplica modello", "Duplicate Template": "Duplica modello",
"Duplicate": "Duplica", "Duplicate": "Duplica",
"daily-mail-quota": "Quota e-mail giornaliera", "daily-mail-quota": "Quota e-mail giornaliera"
"Save as template": "Salva come modello"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nr.", "Sr.No": "Nr.",
@@ -248,7 +247,7 @@
"generate-token-alert": "Sei sicuro di voler rigenerare il token? Questo invaliderà il vecchio token.", "generate-token-alert": "Sei sicuro di voler rigenerare il token? Questo invaliderà il vecchio token.",
"yes": "Sì", "yes": "Sì",
"copied": "Copiato", "copied": "Copiato",
"something-went-wrong-mssg": "Si è verificato un errore, L'aggiornamento della pagina potrebbe risolvere il problema.", "something-went-wrong-mssg": "Qualcosa è andato storto, riprova più tardi.",
"token-generated": "Token generato con successo.", "token-generated": "Token generato con successo.",
"webhook": "Webhook", "webhook": "Webhook",
"update-webhook": "Aggiorna Webhook", "update-webhook": "Aggiorna Webhook",
@@ -284,7 +283,6 @@
"make-template-public": "Rendi il modello pubblico", "make-template-public": "Rendi il modello pubblico",
"make-template-private": "Rendi il modello privato", "make-template-private": "Rendi il modello privato",
"make-template-public-alert": "Sei sicuro di voler rendere pubblico questo modello?", "make-template-public-alert": "Sei sicuro di voler rendere pubblico questo modello?",
"make-template-private-alert-non": "Sei sicuro di voler rendere privato questo modello?",
"make-template-private-alert": "Sei sicuro di voler rendere privato questo modello? Questo lo rimuoverà dal tuo profilo pubblico.", "make-template-private-alert": "Sei sicuro di voler rendere privato questo modello? Questo lo rimuoverà dal tuo profilo pubblico.",
"public-role": "Ruolo pubblico", "public-role": "Ruolo pubblico",
"public-url": "Profilo pubblico", "public-url": "Profilo pubblico",
@@ -310,12 +308,12 @@
"revoke-document": "Revoca documento", "revoke-document": "Revoca documento",
"revoke-document-alert": "Sei sicuro di voler revocare questo documento?", "revoke-document-alert": "Sei sicuro di voler revocare questo documento?",
"resend-mail": "Reinvia email", "resend-mail": "Reinvia email",
"resend-mail-help": "Puoi usare le seguenti variabili che verranno sostituite con i loro valori effettivi: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "resend-mail-help": "Puoi usare le seguenti variabili che verranno sostituite con i loro valori effettivi: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Oggetto", "subject": "Oggetto",
"body": "Corpo del messaggio", "body": "Corpo del messaggio",
"add-contact": "Aggiungi contatto", "add-contact": "Aggiungi contatto",
"edit-contact": "Modifica contatto", "edit-contact": "Modifica contatto",
"add-signer-alert": "Il contatto esiste già! Selezionalo dal menu a tendina 'Firmatari'.", "add-signer-alert": "Il contatto esiste già! Selezionalo dal menu a tendina Firmatari.",
"record-delete-alert": "Record eliminato con successo!", "record-delete-alert": "Record eliminato con successo!",
"record-revoke-alert": "Record revocato con successo!", "record-revoke-alert": "Record revocato con successo!",
"mail-sent-alert": "Email inviata con successo.", "mail-sent-alert": "Email inviata con successo.",
@@ -460,7 +458,6 @@
"placeholder-alert-3": "Sei sicuro di voler inviare questo documento per le firme?", "placeholder-alert-3": "Sei sicuro di voler inviare questo documento per le firme?",
"placeholder-alert-4": "Hai inviato con successo le mail a tutti i destinatari!", "placeholder-alert-4": "Hai inviato con successo le mail a tutti i destinatari!",
"placeholder-mail-alert": "Hai inviato con successo un'e-mail a {{name}}. I firmatari successivi riceveranno un'e-mail una volta che {{name}} avrà firmato il documento.", "placeholder-mail-alert": "Hai inviato con successo un'e-mail a {{name}}. I firmatari successivi riceveranno un'e-mail una volta che {{name}} avrà firmato il documento.",
"placeholder-mail-alert-you": "I firmatari successivi riceveranno un'email non appena firmi il documento.",
"placeholder-alert-5": "Vuoi firmare i documenti ora?", "placeholder-alert-5": "Vuoi firmare i documenti ora?",
"placeholder-alert-6": "Configura l'adattatore email per inviare mail!", "placeholder-alert-6": "Configura l'adattatore email per inviare mail!",
"placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!", "placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!",
@@ -524,7 +521,7 @@
"correct-password": "Fornisci la password corretta", "correct-password": "Fornisci la password corretta",
"decrypting-pdf": "Decrittazione PDF, attendi...", "decrypting-pdf": "Decrittazione PDF, attendi...",
"invalid-otp": "OTP non valido", "invalid-otp": "OTP non valido",
"user-not-found": "Utente non trovato", "user-not-found": "Utente non trovato!",
"enter-otp-alert": "Inserisci l'OTP!", "enter-otp-alert": "Inserisci l'OTP!",
"get-verification-code": "Ottieni codice di verifica", "get-verification-code": "Ottieni codice di verifica",
"get-verification-code-2": "Riceverai un codice di verifica tramite Email", "get-verification-code-2": "Riceverai un codice di verifica tramite Email",
@@ -609,7 +606,7 @@
"placeholder-sign-4": "Trascina o fai clic su un campo per aggiungerlo al documento.", "placeholder-sign-4": "Trascina o fai clic su un campo per aggiungerlo al documento.",
"placeholder-sign-5": "L'area del contenuto PDF visualizza già i segnaposti esistenti del modello. Per tua comodità, questi segnaposti corrisponderanno al colore del nome del destinatario, rendendoli facilmente identificabili.", "placeholder-sign-5": "L'area del contenuto PDF visualizza già i segnaposti esistenti del modello. Per tua comodità, questi segnaposti corrisponderanno al colore del nome del destinatario, rendendoli facilmente identificabili.",
"placeholder-sign-6": "Facendo clic su 'Invia' il documento verrà salvato. Nel passaggio successivo potrai personalizzare le email da inviare ai destinatari o copiare i link di firma e condividerli direttamente con i destinatari.", "placeholder-sign-6": "Facendo clic su 'Invia' il documento verrà salvato. Nel passaggio successivo potrai personalizzare le email da inviare ai destinatari o copiare i link di firma e condividerli direttamente con i destinatari.",
"report-1": "Fai clic sul pulsante 'Aggiungi' per creare un nuovo modello. I modelli sono documenti riutilizzabili progettati per generare rapidamente nuovi documenti con la stessa struttura e firmatari diversi. Ad esempio, un modello HR per l'onboarding potrebbe avere ruoli predefinita come 'Responsabile HR' e 'Nuovo Dipendente'. Ogni volta che usi il modello, puoi assegnare il ruolo 'Nuovo Dipendente' a membri dello staff in arrivo, mentre il ruolo 'Responsabile HR' rimane costante, facilitando un processo di onboarding fluido per ogni nuovo assunto.", "report-1": "Fai clic sul pulsante 'Aggiungi' per creare un nuovo modello. I modelli sono documenti riutilizzabili progettati per generare rapidamente nuovi documenti con la stessa struttura e firmatari diversi. Ad esempio, un modello HR per l'onboarding potrebbe avere ruoli predefinita come Responsabile HR e Nuovo Dipendente. Ogni volta che usi il modello, puoi assegnare il ruolo Nuovo Dipendente a membri dello staff in arrivo, mentre il ruolo Responsabile HR rimane costante, facilitando un processo di onboarding fluido per ogni nuovo assunto.",
"redirect": "Fai clic sul pulsante 'Usa' per creare un nuovo documento da un modello esistente.", "redirect": "Fai clic sul pulsante 'Usa' per creare un nuovo documento da un modello esistente.",
"bulksend": "Per inviare rapidamente più documenti utilizzando un modello esistente creando semplicemente gli indirizzi e-mail dei destinatari, fai clic sul pulsante 'Invio Multiplo'.", "bulksend": "Per inviare rapidamente più documenti utilizzando un modello esistente creando semplicemente gli indirizzi e-mail dei destinatari, fai clic sul pulsante 'Invio Multiplo'.",
"option": "Questo menu rivela altre opzioni come Modifica ed Elimina. Usa il pulsante 'Modifica' per aggiungere ruoli di firmatari, modificare i campi e aggiornare il modello. Le modifiche si applicheranno a tutti i futuri documenti creati da questo modello ma non influiranno sui documenti esistenti. Usa il pulsante Elimina per eliminare il modello.", "option": "Questo menu rivela altre opzioni come Modifica ed Elimina. Usa il pulsante 'Modifica' per aggiungere ruoli di firmatari, modificare i campi e aggiornare il modello. Le modifiche si applicheranno a tutti i futuri documenti creati da questo modello ma non influiranno sui documenti esistenti. Usa il pulsante Elimina per eliminare il modello.",
@@ -626,7 +623,7 @@
"Recent signature requests": "Questo è un elenco di documenti che aspettano la tua firma.", "Recent signature requests": "Questo è un elenco di documenti che aspettano la tua firma.",
"Recently sent for signatures": "Questo è un elenco di documenti che hai inviato ad altre parti per la firma.", "Recently sent for signatures": "Questo è un elenco di documenti che hai inviato ad altre parti per la firma.",
"Drafts": "Questi sono documenti che hai iniziato ma non hai finalizzato per l'invio.", "Drafts": "Questi sono documenti che hai iniziato ma non hai finalizzato per l'invio.",
"public-template": "Questo video dimostra come configurare il tuo profilo pubblico personalizzato, come 'https://opensign.me/tuo-username'. Imparerai anche come personalizzare il tuo slogan e rendere i tuoi modelli disponibili per la firma pubblica.", "public-template": "Questo video dimostra come configurare il tuo profilo pubblico personalizzato, come https://opensign.me/tuo-username. Imparerai anche come personalizzare il tuo slogan e rendere i tuoi modelli disponibili per la firma pubblica.",
"allowModify-widgets": "È possibile trascinare e rilasciare uno qualsiasi di questi campi nel documento, oltre ai campi già designati dal creatore del documento." "allowModify-widgets": "È possibile trascinare e rilasciare uno qualsiasi di questi campi nel documento, oltre ai campi già designati dal creatore del documento."
}, },
"enter-email-plaholder": "Aggiungi un indirizzo email e premi invio", "enter-email-plaholder": "Aggiungi un indirizzo email e premi invio",
@@ -834,7 +831,7 @@
"initial-type": "Le tue iniziali", "initial-type": "Le tue iniziali",
"redirect-url": "URL di reindirizzamento", "redirect-url": "URL di reindirizzamento",
"bulk-send": "Invio in blocco", "bulk-send": "Invio in blocco",
"select-timezone": "Fuso orario", "select-timezone": "Seleziona il tuo fuso orario",
"current-time": "Ora corrente", "current-time": "Ora corrente",
"email-help": "Non ti è permesso cambiare l'indirizzo email per motivi di sicurezza. Ti preghiamo di creare un altro account gratuito utilizzando il nuovo indirizzo email.", "email-help": "Non ti è permesso cambiare l'indirizzo email per motivi di sicurezza. Ti preghiamo di creare un altro account gratuito utilizzando il nuovo indirizzo email.",
"doc-sent": "Documento inviato con successo.", "doc-sent": "Documento inviato con successo.",
@@ -859,27 +856,5 @@
"enabled-signature-type-help": "L'impostazione 'Tipi di firma abilitati' determina quali opzioni di firma sono disponibili nella tua organizzazione. Ad esempio, se disabiliti l'opzione 'Disegna', i membri della tua organizzazione non la vedranno nel widget della firma, mentre le altre tre opzioni resteranno accessibili.", "enabled-signature-type-help": "L'impostazione 'Tipi di firma abilitati' determina quali opzioni di firma sono disponibili nella tua organizzazione. Ad esempio, se disabiliti l'opzione 'Disegna', i membri della tua organizzazione non la vedranno nel widget della firma, mentre le altre tre opzioni resteranno accessibili.",
"indexing-public-profile": "Consenti l'indicizzazione del profilo pubblico dai motori di ricerca", "indexing-public-profile": "Consenti l'indicizzazione del profilo pubblico dai motori di ricerca",
"user-created-successfully": "Utente creato con successo.", "user-created-successfully": "Utente creato con successo.",
"only-15-reminder-allowed": "Puoi impostare fino a 15 promemoria automatici. Ad esempio, se 'TimeToComplete' è di 15 giorni e 'RemindOnceInEvery' è di 1 giorno, raggiungerai il limite massimo di 15 promemoria. Regola le tue impostazioni di conseguenza.", "only-15-reminder-allowed": "Puoi impostare fino a 15 promemoria automatici. Ad esempio, se 'TimeToComplete' è di 15 giorni e 'RemindOnceInEvery' è di 1 giorno, raggiungerai il limite massimo di 15 promemoria. Regola le tue impostazioni di conseguenza."
"rate-your-experience": "Com'è stata la sua esperienza con {{appName}}?",
"thanks-for-feedback": "Grazie per il tuo feedback 🙏",
"share-your-feedback": "Condividi il tuo feedback",
"share-your-review": "Condividi la tua recensione",
"date-format": "Formato data",
"document-deleted": "Il documento è stato eliminato o non hai accesso. Si prega di contattare il mittente.",
"save-as-template-?": "Sei sicuro di voler salvare questo documento come modello?",
"go-to-manage-templates": "Vai a 'Gestisci modelli'",
"template-created": "Modello creato",
"how-would-you-like-to-proceed?": "Come desideri procedere?",
"failed-to-load-refresh-page": "Impossibile caricare il documento. Prova ad aggiornare questa pagina.",
"document-has-been-signed": "Il documento è stato firmato con successo!",
"document-has-been-signed-by-you": "Il documento è stato firmato con successo da lei!",
"participant-completed-signing": "Tutti i partecipanti hanno completato il processo di firma.",
"you-will-receive-email-shortly": "✅ È tutto! Riceverà a breve un'e-mail di conferma.",
"please-provide-templateid": "Si prega di fornire templateid",
"this-template-is-not-public": "Questo template non è pubblico",
"invalid-templateid": "templateid non valido",
"contact-billing-at-opensign": " Per aggiungere altri posti, contattare OpenSign™ all'indirizzo <1>billing@opensignlabs.com</1> per assistenza.",
"title-length-alert": "Il titolo può contenere al massimo 250 caratteri.",
"note-length-alert": "La nota può contenere al massimo 200 caratteri.",
"description-length-alert": " La descrizione può contenere al massimo 500 caratteri."
} }
+10 -9
View File
@@ -3,7 +3,9 @@ import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput"; import SuggestionInput from "./shared/fields/SuggestionInput";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
const BulkSendUi = (props) => { const BulkSendUi = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [forms, setForms] = useState([]); const [forms, setForms] = useState([]);
@@ -72,6 +74,7 @@ const BulkSendUi = (props) => {
setForms(newForms); setForms(newForms);
}; };
const handleRemoveForm = (index) => { const handleRemoveForm = (index) => {
const updatedForms = forms.filter((_, i) => i !== index); const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms); setForms(updatedForms);
@@ -159,16 +162,15 @@ const BulkSendUi = (props) => {
}; };
const batchQuery = async (Documents) => { const batchQuery = async (Documents) => {
const token = { const token =
"X-Parse-Session-Token": localStorage.getItem("accesstoken") { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
};
const functionsUrl = `${localStorage.getItem( const functionsUrl = `${localStorage.getItem(
"baseUrl" "baseUrl"
)}functions/batchdocuments`; )}functions/batchdocuments`;
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"),
...token ...token,
}; };
const params = { Documents: JSON.stringify(Documents) }; const params = { Documents: JSON.stringify(Documents) };
try { try {
@@ -216,9 +218,7 @@ const BulkSendUi = (props) => {
className="flex flex-col" className="flex flex-col"
key={field.fieldId} key={field.fieldId}
> >
<label className="block text-xs font-semibold"> <label>{field.label}</label>
{field.label}
</label>
<SuggestionInput <SuggestionInput
required required
type="email" type="email"
@@ -275,7 +275,8 @@ const BulkSendUi = (props) => {
)} )}
</> </>
) : ( ) : (
<></> <>
</>
)} )}
</> </>
)} )}
@@ -1,9 +1,7 @@
import React from "react"; import React from "react";
import { Document, Page } from "react-pdf"; import { Document, Page } from "react-pdf";
import { Stage, Layer, Rect, Text } from "react-konva"; import { Stage, Layer, Rect, Text } from "react-konva";
import { useTranslation } from "react-i18next";
const RenderDebugPdf = (props) => { const RenderDebugPdf = (props) => {
const { t } = useTranslation();
return ( return (
<div> <div>
<div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]"> <div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
@@ -14,9 +12,10 @@ const RenderDebugPdf = (props) => {
onMouseMove={props.handleMouseMoveDiv} onMouseMove={props.handleMouseMoveDiv}
> >
<Document <Document
onLoadError={() => props.setPdfLoadFail(false)} onLoadError={() => {
loading={t("loading-doc")} props.setPdfLoadFail(false);
error={<p className="mx-2">{t("failed-to-load-refresh-page")}</p>} }}
loading={"Loading Document.."}
onLoadSuccess={props.pageDetails} onLoadSuccess={props.pageDetails}
ref={props.pdfRef} ref={props.pdfRef}
file={props.pdfUrl} file={props.pdfUrl}
@@ -17,8 +17,8 @@ const AddRoleModal = (props) => {
onChange={(e) => props.setRoleName(e.target.value)} onChange={(e) => props.setRoleName(e.target.value)}
placeholder={ placeholder={
props.signersdata.length > 0 props.signersdata.length > 0
? "Role " + (props.signersdata.length + 1) ? "User " + (props.signersdata.length + 1)
: "Role 1" : "User 1"
} }
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
/> />
@@ -0,0 +1,276 @@
import React, { useEffect, useState } from "react";
import opensignLogo from "../../assets/images/logo.png";
import {
Page,
Text,
View,
Document,
StyleSheet,
Image
} from "@react-pdf/renderer";
function Certificate({ pdfData }) {
const [isMultiSigners, setIsMultiSigners] = useState();
const [multiSigner, setMultiSigners] = useState([]);
const [isLoad, setIsLoad] = useState(false);
useEffect(() => {
handleSignerData();
// eslint-disable-next-line
}, []);
const handleSignerData = () => {
const checkSigners = pdfData.filter((data) => data.Signers);
if (checkSigners && checkSigners.length > 0) {
setIsMultiSigners(true);
const checkSignSigners =
pdfData[0].AuditTrail &&
pdfData[0].AuditTrail.length > 0 &&
pdfData[0].AuditTrail.filter((data) => data.Activity === "Signed");
setMultiSigners(checkSignSigners);
} else {
setIsMultiSigners(false);
}
setIsLoad(true);
};
const styles = StyleSheet.create({
page: {
borderRadius: "5px",
padding: "10px",
backgroundColor: "white"
},
section1: {
border: "1px solid rgb(177, 174, 174)",
padding: "20px"
},
textStyle: {
fontWeight: "bold",
fontSize: "11px",
marginBottom: "10px"
},
textStyle2: {
fontWeight: "600",
fontSize: "11px",
marginBottom: "10px",
color: "gray"
},
image: {
width: "71px",
height: "17px"
}
});
const generatedDate = () => {
const newDate = new Date();
const utcTime = newDate.toUTCString();
return (
<Text
style={{
color: "gray",
fontSize: "10px"
}}
>
Generated On {utcTime}
</Text>
);
};
const changeCompletedDate = () => {
const completedOn = pdfData[0].updatedAt;
const newDate = new Date(completedOn);
const utcTime = newDate.toUTCString();
return <Text style={styles.textStyle2}>{utcTime}</Text>;
};
const signerName = (data) => {
const getSignerName = pdfData[0].Signers.filter(
(sign) => sign.objectId === data.UserPtr.objectId
);
return (
getSignerName[0] &&
getSignerName.length > 0 && (
<>
<Text style={styles.textStyle}>
Name : &nbsp;
<Text style={styles.textStyle2}>{getSignerName[0].Name}</Text>
</Text>
<Text style={styles.textStyle}>
Email : &nbsp;
<Text style={styles.textStyle2}>{getSignerName[0].Email}</Text>
</Text>
</>
)
);
};
return (
isLoad && (
<Document>
{/** Page defines a single page of content. */}
<Page size="A4" style={styles.page}>
<View style={styles.section1}>
<View
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "30px"
}}
>
<Image src={opensignLogo} style={styles.image} />
{generatedDate()}
</View>
<View style={{ justifyContent: "center" }}>
<Text
style={{
textAlign: "center",
fontSize: "20px",
fontWeight: "bold",
color: "#31bceb",
marginBottom: "10px"
}}
>
{" "}
Certificate of Completion
</Text>
<View style={{ border: "1px solid #bdbbbb" }}></View>
<View>
<View>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px"
}}
>
Summary
</Text>
</View>
<View style={{ display: "flex", flexDirection: "column" }}>
<Text style={styles.textStyle}>
Document ID : &nbsp;
<Text style={styles.textStyle2}>{pdfData[0].objectId}</Text>
</Text>
<Text style={styles.textStyle}>
Document Name : &nbsp;
<Text style={styles.textStyle2}>{pdfData[0].Name}</Text>
</Text>
<Text style={styles.textStyle}>
Organization : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].ExtUserPtr.Company}
</Text>
</Text>
<Text style={styles.textStyle}>
Completed on : &nbsp;{changeCompletedDate()}
</Text>
{multiSigner && multiSigner.length > 0 && (
<Text style={styles.textStyle}>
Signers : &nbsp;
<Text style={styles.textStyle2}>
{multiSigner.length}
</Text>
</Text>
)}
</View>
{isMultiSigners ? (
<View style={{ display: "flex", flexDirection: "column" }}>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px"
}}
>
Recipients
</Text>
<View>
{multiSigner &&
multiSigner.map((data, ind) => {
return (
<View
key={ind}
style={{
display: "flex",
flexDirection: "column"
}}
>
<View
style={{
border: "0.4px solid #bdbbbb",
marginBottom: "10px"
}}
></View>
{signerName(data)}
<Text style={styles.textStyle}>
Accessed from : &nbsp;
<Text style={styles.textStyle2}>
{data.ipAddress}
</Text>
</Text>
</View>
);
})}
</View>
</View>
) : (
<View style={{ display: "flex", flexDirection: "column" }}>
<Text
style={{
fontSize: "16px",
fontWeight: "bold",
color: "#31bceb",
margin: "10px 0px 10px 0px"
}}
>
Recipients
</Text>
<Text style={styles.textStyle}>
Signers : &nbsp;<Text style={styles.textStyle2}>1</Text>
</Text>
<Text style={styles.textStyle}>
Name : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].ExtUserPtr.Name}
</Text>
</Text>
<Text style={styles.textStyle}>
Email : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].ExtUserPtr.Email}
</Text>
</Text>
<Text style={styles.textStyle}>
Accessed from : &nbsp;
<Text style={styles.textStyle2}>
{pdfData[0].AuditTrail &&
pdfData[0].AuditTrail[0].ipAddress}
</Text>
</Text>
<Text style={styles.textStyle}>
Signed on : &nbsp;{changeCompletedDate()}
</Text>
</View>
)}
</View>
</View>
</View>
</Page>
</Document>
)
);
}
export default Certificate;
@@ -30,20 +30,16 @@ function DraftDocument() {
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
) { ) {
if (documentData?.result?.error?.includes("deleted")) {
setIsLoading({ setIsLoading({
isLoader: false, isLoader: false,
message: t("document-deleted") message: "Error: Something went wrong!"
}); });
} else { } else {
setIsLoading({ setIsLoading({
isLoader: false, isLoader: false,
message: t("something-went-wrong-mssg") message: "No data found!"
}); });
} }
} else {
setIsLoading({ isLoader: false, message: t("no-data") });
}
}; };
//check document type and render on signyour self and placeholder route //check document type and render on signyour self and placeholder route
@@ -1,10 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { getFileName } from "../../constant/Utils"; import { getFileName } from "../../constant/Utils";
import {
maxDescriptionLength,
maxNoteLength,
maxTitleLength
} from "../../constant/const";
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";
@@ -55,18 +50,6 @@ const EditTemplate = ({ template, onSuccess }) => {
alert(t("invalid-redirect-url")); alert(t("invalid-redirect-url"));
return; return;
} }
if (formData?.Name?.length > maxTitleLength) {
alert(t("title-length-alert"));
return;
}
if (formData?.Note?.length > maxNoteLength) {
alert(t("note-length-alert"));
return;
}
if (formData?.Description?.length > maxDescriptionLength) {
alert(t("description-length-alert"));
return;
}
const isChecked = formData.SendinOrder === "true" ? true : false; const isChecked = formData.SendinOrder === "true" ? true : false;
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true; const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
const AutoReminder = formData?.AutomaticReminders || false; const AutoReminder = formData?.AutomaticReminders || false;
@@ -205,7 +188,7 @@ const EditTemplate = ({ template, onSuccess }) => {
<Tooltip id="istourenabled-tooltip" className="z-50"> <Tooltip id="istourenabled-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]"> <div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("enable-tour")}</p> <p className="font-bold">{t("enable-tour")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold">{t("yes")}: </span> <span className="font-bold">{t("yes")}: </span>
@@ -216,7 +199,7 @@ const EditTemplate = ({ template, onSuccess }) => {
<span>{t("istourenabled-help.p2")}</span> <span>{t("istourenabled-help.p2")}</span>
</li> </li>
</ol> </ol>
</div> </p>
<p>{t("istourenabled-help.p3", { appName: appName })}</p> <p>{t("istourenabled-help.p3", { appName: appName })}</p>
</div> </div>
</Tooltip> </Tooltip>
@@ -5,7 +5,6 @@ import Loader from "../../primitives/Loader";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Parse from "parse"; import Parse from "parse";
function EmailComponent({ function EmailComponent({
isEmail, isEmail,
setIsEmail, setIsEmail,
@@ -21,7 +20,6 @@ function EmailComponent({
const [emailErr, setEmailErr] = useState(false); const [emailErr, setEmailErr] = useState(false);
const [isDownloading, setIsDownloading] = useState(""); const [isDownloading, setIsDownloading] = useState("");
const isAndroid = /Android/i.test(navigator.userAgent); const isAndroid = /Android/i.test(navigator.userAgent);
//function for send email //function for send email
const sendEmail = async () => { const sendEmail = async () => {
setIsLoading(true); setIsLoading(true);
@@ -107,7 +105,7 @@ function EmailComponent({
</div> </div>
)} )}
<div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content"> <div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content">
<span className="text-base-content font-bold text-sm md:text-lg"> <span className="text-base-content font-semibold">
{t("successfully-signed")} {t("successfully-signed")}
</span> </span>
<div className="flex flex-row"> <div className="flex flex-row">
@@ -116,14 +114,14 @@ function EmailComponent({
onClick={(e) => onClick={(e) =>
handleToPrint(e, setIsDownloading, pdfDetails) handleToPrint(e, setIsDownloading, pdfDetails)
} }
className="op-btn op-btn-neutral op-btn-sm text-xs md:text-[15px]" className="op-btn op-btn-neutral op-btn-sm text-[15px]"
> >
<i className="fa-light fa-print" aria-hidden="true"></i> <i className="fa-light fa-print" aria-hidden="true"></i>
{t("print")} {t("print")}
</button> </button>
)} )}
<button <button
className="op-btn op-btn-primary op-btn-sm text-xs md:text-[15px] ml-2" className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
onClick={() => { onClick={() => {
handleClose(); handleClose();
setIsDownloadModal(true); setIsDownloadModal(true);
@@ -139,12 +137,12 @@ function EmailComponent({
{t("email-mssg")} {t("email-mssg")}
</p> </p>
{emailList.length > 0 ? ( {emailList.length > 0 ? (
<div className="p-0 border-[1px] op-border-primary w-full rounded-md text-[15px] overflow-hidden"> <div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]">
<div className="flex flex-row flex-wrap"> <div className="flex flex-row flex-wrap">
{emailList.map((data, ind) => { {emailList.map((data, ind) => {
return ( return (
<div <div
className="flex flex-row items-center op-bg-primary mx-[2px] mt-[2px] rounded-md py-[5px] px-[10px]" className="flex flex-row items-center op-bg-primary m-[4px] rounded-md py-[5px] px-[10px]"
key={ind} key={ind}
> >
<span className="text-base-100 text-[13px]"> <span className="text-base-100 text-[13px]">
@@ -164,7 +162,7 @@ function EmailComponent({
<input <input
type="email" type="email"
value={emailValue} value={emailValue}
className="p-[10px] rounded-md w-full text-[15px] bg-transparent outline-none" className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-transparent outline-none"
onChange={handleEmailValue} onChange={handleEmailValue}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
onBlur={() => emailValue && handleEnterPress("add")} onBlur={() => emailValue && handleEnterPress("add")}
@@ -181,7 +179,7 @@ function EmailComponent({
<input <input
type="email" type="email"
value={emailValue} value={emailValue}
className="p-[10px] pb-[20px] rounded-md w-full text-[15px] outline-none bg-transparent border-[1px] op-border-primary" className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary"
onChange={handleEmailValue} onChange={handleEmailValue}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
placeholder={t("enter-email-plaholder")} placeholder={t("enter-email-plaholder")}
@@ -199,7 +197,7 @@ function EmailComponent({
{t("email-error-1")} {t("email-error-1")}
</p> </p>
)} )}
{/* <button <button
className={`${ className={`${
emailValue ? "cursor-pointer" : "cursor-default" emailValue ? "cursor-pointer" : "cursor-default"
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`} } op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
@@ -207,12 +205,12 @@ function EmailComponent({
> >
<i className="fa-light fa-plus" aria-hidden="true"></i> <i className="fa-light fa-plus" aria-hidden="true"></i>
</button> </button>
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded"> <div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
<span className="font-bold">{t("report-heading.Note")}: </span> <span className="font-bold">{t("report-heading.Note")}: </span>
<span className="text-[15px]">{t("email-error-2")}</span> <span className="text-[15px]">{t("email-error-2")}</span>
</div> </div>
<hr className="w-full my-[15px] bg-base-content" /> */} <hr className="w-full my-[15px] bg-base-content" />
<div className="mt-2">
<button <button
type="button" type="button"
className="op-btn op-btn-secondary" className="op-btn op-btn-secondary"
@@ -228,7 +226,6 @@ function EmailComponent({
{t("close")} {t("close")}
</button> </button>
</div> </div>
</div>
</ModalUi> </ModalUi>
)} )}
</div> </div>
@@ -0,0 +1,305 @@
import React, { useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism";
import { copytoData } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router";
function EmbedTab(props) {
const { t } = useTranslation();
const navigate = useNavigate();
const tabName = [
{ title: "React/Next.js", icon: "fa-brands fa-react", color: "#61dafb" },
{ title: "JavaScript", icon: "fa-brands fa-js", color: "#ffd43b" },
{ title: "Angular", icon: "fa-brands fa-angular", color: "#ff5733" }
];
const [activeTab, setActiveTab] = useState(0);
// State to track if the code has been copied
const [isCopied, setIsCopied] = useState(false);
const reactCode = [
{
id: 0,
title: "Installation",
codeString: `npm install @opensign/react`
},
{
id: 1,
title: "Usage",
codeString: `
import React from "react";
import Opensign from "@opensign/react";
export default function App() {
return (
<div className="app">
<Opensign
onLoad={() => console.log("success")}
onLoadError={(error) => console.log(error)}
templateId= "${props.templateId ? props.templateId : "#templateId"}"
/>
</div>
);
}
`
}
];
const angularCode = [
{
id: 0,
title: "Installation",
codeString: `npm install @opensign/angular`
},
{
id: 1,
title: "Usage",
codeString: `
import { Component } from '@angular/core';
import { OpensignComponent } from "@opensign/angular"
@Component({
selector:'app-root',
standalone: true,
imports: [OpensignComponent],
template:\`<opensign templateId="${props.templateId ? props.templateId : "#templateId"}"
(onLoad)="handleLoad()"
(onLoadError)="handleError($event)"
></opensign>\`,
})
export class AppComponent {
handleLoad() {
console.log("success");
}
handleError(error: string) {
console.log(error);
}
}
`
}
];
const jsCodeString = `
<script
src= "${window.location.origin}/static/js/public-template.bundle.js"
id="opensign-script"
templateId=${props.templateId ? props.templateId : "#templateId"}
></script>
`;
const handleCopy = (code, ind) => {
copytoData(code);
setIsCopied({ ...isCopied, [ind]: true });
setTimeout(() => setIsCopied(false), 3000);
};
return (
<div className={`${props.templateId && "border-t-[1px] mt-4"}`}>
{props.templateId && (
<h3 className="text-base-content font-bold text-lg pt-[15px] pb-[5px]">
{t("embed-template")}
</h3>
)}
<div className="flex justify-center items-center mt-2">
<div role="tablist" className="op-tabs op-tabs-bordered">
{tabName.map((tabData, ind) => (
<div
onClick={() => setActiveTab(ind)}
key={ind}
role="tab"
className={`${
activeTab === ind ? "op-tab-active" : ""
} op-tab flex items-center pb-10 md:pb-0`}
>
<i
className={`${tabData.icon}`}
style={{ color: tabData.color }}
></i>
<span className="ml-1 text-[10px] font-medium md:font-normal md:text-[15px]">
{tabData.title}
</span>
</div>
))}
</div>
</div>
<div>
{activeTab === 0 ? (
<div className="mt-4">
{reactCode.map((data, ind) => {
return (
<div key={ind}>
<p className="font-medium text-[18px]">
{t(`${data.title}`)}
</p>
{ind === 0 && (
<p className="text-[15px] mt-2">
{t("public-template-mssg-1")}
</p>
)}
<div className="relative p-1">
<div
onClick={() => handleCopy(data.codeString, ind)}
className="absolute top-[20px] right-[20px] cursor-pointer"
>
<i className="fa-light fa-copy text-white mr-[2px]" />
<span className=" text-white">
{isCopied[ind] ? t("copied-code") : t("copy-code")}
</span>
</div>
<SyntaxHighlighter
customStyle={{
borderRadius: "15px"
}}
language="javascript"
style={tomorrow}
>
{data.codeString}
</SyntaxHighlighter>
</div>
</div>
);
})}
{props.isEmbedPage && (
<p className="text-[15px] my-2">
{t("js-snippet-msg-1")}
<span
className="text-blue-600 cursor-pointer px-1"
onClick={() => navigate("/report/6TeaPr321t")}
>
{t("js-snippet-msg-2")}
</span>
<span>{t("js-snippet-msg-3")}</span>
</p>
)}
<p className="font-medium mt-3 text-[15px]">
{t("public-template-mssg-3")}
</p>
<p className="my-[6px]">
{" "}
{t("public-template-mssg-4")}
<a
href="https://www.npmjs.com/package/@opensign/react"
target="_blank"
rel="noreferrer"
className="cursor-pointer text-blue-700 "
>
{" "}
OpenSign React package{" "}
</a>
{t("public-template-mssg-5")}
</p>
</div>
) : activeTab === 1 ? (
<div className="mt-4">
<div>
<p className="font-medium text-[18px]">{t(`Usage`)}</p>
<p className="text-[15px] my-2">{t("js-snippet-msg")}</p>
<div className="relative p-1">
<div
onClick={() => handleCopy(jsCodeString, 0)}
className="absolute top-[20px] right-[20px] cursor-pointer"
>
<i className="fa-light fa-copy text-white mr-[2px]" />
<span className=" text-white">
{isCopied[0] ? t("copied-code") : t("copy-code")}
</span>
</div>
<SyntaxHighlighter
customStyle={{
borderRadius: "15px"
}}
language="javascript"
style={tomorrow}
>
{jsCodeString}
</SyntaxHighlighter>
</div>
{props.isEmbedPage && (
<p className="text-[15px] my-2">
{t("js-snippet-msg-1")}
<span
className="text-blue-600 cursor-pointer px-1"
onClick={() => navigate("/report/6TeaPr321t")}
>
{t("js-snippet-msg-2")}
</span>
<span>{t("js-snippet-msg-3")}</span>
</p>
)}
</div>
</div>
) : (
activeTab === 2 && (
<div className="mt-4">
{angularCode.map((data, ind) => {
return (
<div key={ind}>
<p className="font-medium text-[18px]">
{t(`${data.title}`)}
</p>
{ind === 0 && (
<p className="text-[15px] mt-2">
{t("angular-npm-mssg-1")}
</p>
)}
<div className="relative p-1">
<div
onClick={() => handleCopy(data.codeString, ind)}
className="absolute top-[20px] right-[20px] cursor-pointer"
>
<i className="fa-light fa-copy text-white mr-[2px]" />
<span className=" text-white">
{isCopied[ind] ? t("copied-code") : t("copy-code")}
</span>
</div>
<SyntaxHighlighter
customStyle={{
borderRadius: "15px"
}}
language="javascript"
style={tomorrow}
>
{data.codeString}
</SyntaxHighlighter>
</div>
</div>
);
})}
{props.isEmbedPage && (
<p className="text-[15px] my-2">
{t("js-snippet-msg-1")}
<span
className="text-blue-600 cursor-pointer px-1"
onClick={() => navigate("/report/6TeaPr321t")}
>
{t("js-snippet-msg-2")}
</span>
<span>{t("js-snippet-msg-3")}</span>
</p>
)}
<p className="font-medium mt-3 text-[15px]">
{t("public-template-mssg-3")}
</p>
<p className="my-[6px]">
{" "}
{t("public-template-mssg-4")}
<a
href="https://www.npmjs.com/package/@opensign/angular"
target="_blank"
rel="noreferrer"
className="cursor-pointer text-blue-700 "
>
{" "}
OpenSign Angular package{" "}
</a>
{t("public-template-mssg-5")}
</p>
</div>
)
)}
</div>
</div>
);
}
export default EmbedTab;
@@ -14,7 +14,6 @@ import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader"; import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import { maxFileSize } from "../../constant/const";
function Header(props) { function Header(props) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -51,13 +50,6 @@ function Header(props) {
} }
}; };
// `removeFile` is used to remove file if exists
const removeFile = (e) => {
if (e) {
e.target.value = "";
}
};
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) { if (!file) {
@@ -68,13 +60,6 @@ function Header(props) {
alert("Only PDF files are allowed."); alert("Only PDF files are allowed.");
return; return;
} }
const mb = Math.round(file?.size / Math.pow(1024, 2));
if (mb > maxFileSize) {
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
removeFile(e);
return;
}
try { try {
const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfBytes = await file.arrayBuffer();
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -7,7 +7,6 @@ import {
} from "../../constant/Utils"; } from "../../constant/Utils";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import { maxFileSize } from "../../constant/const";
function PdfZoom(props) { function PdfZoom(props) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -42,14 +41,6 @@ function PdfZoom(props) {
console.log("error in delete pdf page", e); console.log("error in delete pdf page", e);
} }
}; };
// `removeFile` is used to remove file if exists
const removeFile = (e) => {
if (e) {
e.target.value = "";
}
};
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) { if (!file) {
@@ -60,12 +51,6 @@ function PdfZoom(props) {
alert("Only PDF files are allowed."); alert("Only PDF files are allowed.");
return; return;
} }
const mb = Math.round(file?.size / Math.pow(1024, 2));
if (mb > maxFileSize) {
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
removeFile(e);
return;
}
try { try {
const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfBytes = await file.arrayBuffer();
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -25,8 +25,6 @@ const selectFormat = (data) => {
switch (data) { switch (data) {
case "L": case "L":
return "MM/dd/yyyy"; return "MM/dd/yyyy";
case "MM/DD/YYYY":
return "MM/dd/yyyy";
case "DD-MM-YYYY": case "DD-MM-YYYY":
return "dd-MM-yyyy"; return "dd-MM-yyyy";
case "DD/MM/YYYY": case "DD/MM/YYYY":
@@ -43,8 +41,6 @@ const selectFormat = (data) => {
return "MM.dd.yyyy"; return "MM.dd.yyyy";
case "MMM DD, YYYY": case "MMM DD, YYYY":
return "MMM dd, yyyy"; return "MMM dd, yyyy";
case "MMMM DD, YYYY":
return "MMMM dd, yyyy";
case "DD MMMM, YYYY": case "DD MMMM, YYYY":
return "dd MMMM, yyyy"; return "dd MMMM, yyyy";
default: default:
@@ -24,7 +24,7 @@ const widgetCls =
function PlaceholderType(props) { function PlaceholderType(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const type = props?.pos?.type; const type = props?.pos?.type;
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`); const widgetTypeTraslation = t(`widgets-name.${props?.pos?.type}`);
const [selectOption, setSelectOption] = useState(""); const [selectOption, setSelectOption] = useState("");
const [validatePlaceholder, setValidatePlaceholder] = useState(""); const [validatePlaceholder, setValidatePlaceholder] = useState("");
const inputRef = useRef(null); const inputRef = useRef(null);
@@ -342,8 +342,8 @@ function PlaceholderType(props) {
className="font-medium" className="font-medium"
> >
{props.isNeedSign {props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation ? props.pos?.options?.hint || widgetTypeTraslation
: widgetTypeTranslation} : widgetTypeTraslation}
</div> </div>
)} )}
</div> </div>
@@ -368,8 +368,8 @@ function PlaceholderType(props) {
className="font-medium" className="font-medium"
> >
{props.isNeedSign {props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation ? props.pos?.options?.hint || widgetTypeTraslation
: widgetTypeTranslation} : widgetTypeTraslation}
</div> </div>
)} )}
</div> </div>
@@ -475,7 +475,7 @@ function PlaceholderType(props) {
/> />
) : ( ) : (
<div style={textWidgetStyle} className="select-none-cls"> <div style={textWidgetStyle} className="select-none-cls">
<span>{textValue || widgetTypeTranslation}</span> <span>{textValue || widgetTypeTraslation}</span>
</div> </div>
); );
case "dropdown": case "dropdown":
@@ -538,7 +538,7 @@ function PlaceholderType(props) {
> >
{props.pos?.options?.name {props.pos?.options?.name
? props.pos.options.name ? props.pos.options.name
: widgetTypeTranslation} : widgetTypeTraslation}
<i className="fa-light fa-circle-chevron-down mr-1 "></i> <i className="fa-light fa-circle-chevron-down mr-1 "></i>
</div> </div>
); );
@@ -562,8 +562,8 @@ function PlaceholderType(props) {
className="font-medium text-center" className="font-medium text-center"
> >
{props.isNeedSign {props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation ? props.pos?.options?.hint || widgetTypeTraslation
: widgetTypeTranslation} : widgetTypeTraslation}
</div> </div>
)} )}
</div> </div>
@@ -574,7 +574,7 @@ function PlaceholderType(props) {
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? ( (props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea <textarea
ref={inputRef} ref={inputRef}
placeholder={hint || widgetTypeTranslation} placeholder={hint}
rows={1} rows={1}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
value={textValue} value={textValue}
@@ -597,7 +597,7 @@ function PlaceholderType(props) {
/> />
) : ( ) : (
<div className="flex h-full select-none-cls" style={textWidgetStyle}> <div className="flex h-full select-none-cls" style={textWidgetStyle}>
<span>{widgetTypeTranslation}</span> <span>{widgetTypeTraslation}</span>
</div> </div>
); );
case "company": case "company":
@@ -606,7 +606,7 @@ function PlaceholderType(props) {
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? ( (props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea <textarea
ref={inputRef} ref={inputRef}
placeholder={hint || widgetTypeTranslation} placeholder={hint}
rows={1} rows={1}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
value={textValue} value={textValue}
@@ -628,7 +628,7 @@ function PlaceholderType(props) {
/> />
) : ( ) : (
<div style={textWidgetStyle} className="select-none-cls"> <div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span> <span>{widgetTypeTraslation}</span>
</div> </div>
); );
case "job title": case "job title":
@@ -637,7 +637,7 @@ function PlaceholderType(props) {
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? ( (props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea <textarea
ref={inputRef} ref={inputRef}
placeholder={hint || widgetTypeTranslation} placeholder={hint}
rows={1} rows={1}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
value={textValue} value={textValue}
@@ -659,7 +659,7 @@ function PlaceholderType(props) {
/> />
) : ( ) : (
<div style={textWidgetStyle} className="select-none-cls"> <div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span> <span>{widgetTypeTraslation}</span>
</div> </div>
); );
case "date": case "date":
@@ -748,8 +748,8 @@ function PlaceholderType(props) {
className="font-medium text-center" className="font-medium text-center"
> >
{props.isNeedSign {props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation ? props.pos?.options?.hint || widgetTypeTraslation
: widgetTypeTranslation} : widgetTypeTraslation}
</div> </div>
)} )}
</div> </div>
@@ -760,7 +760,7 @@ function PlaceholderType(props) {
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? ( (props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<textarea <textarea
ref={inputRef} ref={inputRef}
placeholder={hint || widgetTypeTranslation} placeholder={hint}
rows={1} rows={1}
onKeyDown={(e) => { onKeyDown={(e) => {
// Prevent new line on Enter key press // Prevent new line on Enter key press
@@ -792,7 +792,7 @@ function PlaceholderType(props) {
/> />
) : ( ) : (
<div style={textWidgetStyle} className="select-none-cls"> <div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span> <span>{widgetTypeTraslation}</span>
</div> </div>
); );
case radioButtonWidget: case radioButtonWidget:
@@ -891,8 +891,8 @@ function PlaceholderType(props) {
className="font-medium" className="font-medium"
> >
{props.isNeedSign {props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation ? props.pos?.options?.hint || widgetTypeTraslation
: widgetTypeTranslation} : widgetTypeTraslation}
</div> </div>
)} )}
</div> </div>
@@ -4,7 +4,6 @@ import { Document, Page } from "react-pdf";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import { base64ToArrayBuffer } from "../../constant/Utils"; import { base64ToArrayBuffer } from "../../constant/Utils";
import { maxFileSize } from "../../constant/const";
function RenderAllPdfPage(props) { function RenderAllPdfPage(props) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -63,12 +62,6 @@ function RenderAllPdfPage(props) {
}; };
const pdfDataBase64 = `data:application/pdf;base64,${props?.pdfBase64Url}`; const pdfDataBase64 = `data:application/pdf;base64,${props?.pdfBase64Url}`;
// `removeFile` is used to remove file if exists
const removeFile = (e) => {
if (e) {
e.target.value = "";
}
};
// `handleFileUpload` is trigger when user click on add pages btn and is used to merge multiple pdf // `handleFileUpload` is trigger when user click on add pages btn and is used to merge multiple pdf
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
@@ -80,12 +73,6 @@ function RenderAllPdfPage(props) {
alert("Only PDF files are allowed."); alert("Only PDF files are allowed.");
return; return;
} }
const mb = Math.round(file?.size / Math.pow(1024, 2));
if (mb > maxFileSize) {
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
removeFile(e);
return;
}
try { try {
const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfBytes = await file.arrayBuffer();
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -123,7 +110,6 @@ function RenderAllPdfPage(props) {
autoSignScroll hide-scrollbar max-h-[100vh] `} autoSignScroll hide-scrollbar max-h-[100vh] `}
> >
<Document <Document
error=""
loading={t("loading-doc")} loading={t("loading-doc")}
onLoadSuccess={onDocumentLoad} onLoadSuccess={onDocumentLoad}
file={pdfDataBase64} file={pdfDataBase64}
+11 -8
View File
@@ -381,15 +381,17 @@ function RenderPdf(props) {
</React.Fragment> </React.Fragment>
); );
}))} }))}
{/* Mobile */}
<Document <Document
error={<p className="mx-2">{t("failed-to-load-refresh-page")}</p>}
onLoadError={() => props.setPdfLoad(false)} onLoadError={() => props.setPdfLoad(false)}
loading={t("loading-doc")} loading={t("loading-doc")}
onLoadSuccess={props.pageDetails} onLoadSuccess={props.pageDetails}
onClick={() => // ref={pdfRef}'
props.setSelectWidgetId && props.setSelectWidgetId("") onClick={() => {
if (props.setSelectWidgetId) {
props.setSelectWidgetId("");
} }
}}
file={pdfDataBase64} file={pdfDataBase64}
> >
<Page <Page
@@ -599,16 +601,17 @@ function RenderPdf(props) {
</React.Fragment> </React.Fragment>
); );
}))} }))}
{/* large device */}
{/* this component for render pdf document is in middle of the component */} {/* this component for render pdf document is in middle of the component */}
<Document <Document
error={<p className="mx-2">{t("failed-to-load-refresh-page")}</p>}
onLoadError={() => props.setPdfLoad(false)} onLoadError={() => props.setPdfLoad(false)}
loading={t("loading-doc")} loading={t("loading-doc")}
onLoadSuccess={props.pageDetails} onLoadSuccess={props.pageDetails}
onClick={() => onClick={() => {
props.setSelectWidgetId && props.setSelectWidgetId("") if (props.setSelectWidgetId) {
props.setSelectWidgetId("");
} }
}}
file={pdfDataBase64} file={pdfDataBase64}
> >
<Page <Page
@@ -1,79 +0,0 @@
import React, { useState } from "react";
import { formatDateTime } from "../../../constant/Utils";
import { useTranslation } from "react-i18next";
const DateFormatSelector = (props) => {
const { t } = useTranslation();
const date = new Date();
const [selectedFormat, setSelectedFormat] = useState(props.dateFormat);
const [is12Hour, setIs12Hour] = useState(props?.is12HourTime);
const dateFormats = [
"MM/DD/YYYY",
"MMMM DD, YYYY",
"DD MMMM, YYYY",
"DD-MM-YYYY",
"DD MMM, YYYY",
"YYYY-MM-DD",
"MM-DD-YYYY",
"MM.DD.YYYY",
"MMM DD, YYYY"
];
// Handle format change
const handleFormatChange = (event) => {
setSelectedFormat(event.target.value);
props.setDateFormat && props.setDateFormat(event.target.value);
};
const handleHrInput = () => {
setIs12Hour(!is12Hour);
props.setIs12HourTime && props.setIs12HourTime(!is12Hour);
};
return (
<div className="max-w-[400px] pr-[20px]">
<label className="text-[14px] mb-[0.7rem] font-medium">
{t("date-format")}
</label>
<select
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]"
value={selectedFormat}
onChange={handleFormatChange}
>
{dateFormats.map((format) => (
<option key={format} value={format}>
{format}
</option>
))}
</select>
<div className="flex flex-col md:flex-row md:gap-4 mt-[0.75rem] text-[12px]">
<div className="flex items-center gap-2 ml-2">
<input
type="radio"
value={true}
className="op-radio op-radio-xs"
checked={is12Hour}
onChange={handleHrInput}
/>
<div className="text-center">12 hr</div>
</div>
<div className="flex items-center gap-2 ml-2">
<input
type="radio"
value={false}
className="op-radio op-radio-xs"
checked={!is12Hour}
onChange={handleHrInput}
/>
<div className="text-center">24 hr</div>
</div>
</div>
<p className="mt-[12px] ml-[10px] text-[13px] font-medium">
<strong>
{formatDateTime(date, selectedFormat, props?.timezone, is12Hour)}
</strong>
</p>
</div>
);
};
export default DateFormatSelector;
@@ -1,10 +1,10 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import AsyncSelect from "react-select/async"; import AsyncSelect from "react-select/async";
import AddContact from "../../../primitives/AddContact"; import AddSigner from "../../AddSigner";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip"; import Tooltip from "../../../primitives/Tooltip";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { findContact } from "../../../constant/Utils";
function arrayMove(array, from, to) { function arrayMove(array, from, to) {
array = array.slice(); array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]); array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
@@ -103,21 +103,17 @@ const SignersInput = (props) => {
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown // `handleNewDetails` is used to set just save from quick form to selected option in dropdown
const handleNewDetails = (data) => { const handleNewDetails = (data) => {
const user = { setState([...state, data]);
value: data["objectId"],
label: data["Name"],
email: data?.Email
};
setState([...state, user]);
if (selected.length > 0) { if (selected.length > 0) {
setSelected([...selected, user]); setSelected([...selected, data]);
} else { } else {
setSelected([user]); setSelected([data]);
} }
}; };
const loadOptions = async (inputValue) => { const loadOptions = async (inputValue) => {
try { try {
const contactRes = await findContact(inputValue); const params = { search: inputValue };
const contactRes = await Parse.Cloud.run("getsigners", params);
if (contactRes) { if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes)); const res = JSON.parse(JSON.stringify(contactRes));
//compareArrays is a function where compare between two array (total signersList and dcument signers list) //compareArrays is a function where compare between two array (total signersList and dcument signers list)
@@ -152,9 +148,7 @@ const SignersInput = (props) => {
{props.label ? props.label : t("signers")} {props.label ? props.label : t("signers")}
{props.required && <span className="text-red-500 text-[13px]">*</span>} {props.required && <span className="text-red-500 text-[13px]">*</span>}
<span <span
className={`z-[${ className={`z-[${props?.helptextZindex ? props.helptextZindex : 30}] absolute ml-1 text-xs`}
props?.helptextZindex ? props.helptextZindex : 30
}] absolute ml-1 text-xs`}
> >
<Tooltip <Tooltip
id={`${props.label ? props.label : "signers"}-tooltip`} id={`${props.label ? props.label : "signers"}-tooltip`}
@@ -215,8 +209,9 @@ const SignersInput = (props) => {
</button> </button>
{isModal && ( {isModal && (
<AddContact <AddSigner
isDisableTitle valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails} details={handleNewDetails}
closePopup={handleModalCloseClick} closePopup={handleModalCloseClick}
/> />
@@ -7,10 +7,27 @@ const TimezoneSelector = (props) => {
// Intl.DateTimeFormat().resolvedOptions().timeZone // Default to the user's local timezone // Intl.DateTimeFormat().resolvedOptions().timeZone // Default to the user's local timezone
const onChangeTimezone = (timezone) => { const onChangeTimezone = (timezone) => {
setSelectedTimezone(timezone?.value); setSelectedTimezone(timezone);
props.setTimezone && props.setTimezone(timezone?.value); props.setTimezone && props.setTimezone(timezone?.value);
}; };
// Format date and time for the selected timezone
const formatDate = (date, timezone) => {
return timezone
? new Intl.DateTimeFormat("en-US", {
weekday: "short",
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZone: timezone,
hour12: false
}).format(date)
: new Date(date).toUTCString();
};
return ( return (
<> <>
<div className="max-w-[400px] pr-[20px]"> <div className="max-w-[400px] pr-[20px]">
@@ -37,6 +54,11 @@ const TimezoneSelector = (props) => {
}} }}
/> />
</div> </div>
<div className="mt-[12px] ml-[10px] text-[13px]">
<strong>
{formatDate(new Date(), selectedTimezone?.value || selectedTimezone)}
</strong>{" "}
</div>
</> </>
); );
}; };
+3 -4
View File
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
import { NavLink } from "react-router"; import { NavLink } from "react-router";
const Menu = ({ item, isOpen, closeSidebar }) => { const Menu = ({ item, isOpen, closeSidebar }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -17,15 +18,13 @@ const Menu = ({ item, isOpen, closeSidebar }) => {
className={({ isActive }) => className={({ isActive }) =>
`${ `${
isActive ? " bg-base-300 text-base-content" : "" isActive ? " bg-base-300 text-base-content" : ""
} flex items-center justify-start text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none` } flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
} }
onClick={closeSidebar} onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1} tabIndex={isOpen ? 0 : -1}
role="menuitem" role="menuitem"
> >
<span className="w-[20px] h-[20px] flex justify-center">
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i> <i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
</span>
<span className="ml-3 lg:ml-4"> <span className="ml-3 lg:ml-4">
{t(`sidebar.${item.title}`, { appName: drivename })} {t(`sidebar.${item.title}`, { appName: drivename })}
</span> </span>
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
import { NavLink } from "react-router"; import { NavLink } from "react-router";
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => { const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const { title, icon, children } = item; const { title, icon, children } = item;
@@ -11,16 +12,14 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
<li role="none" className="my-0.5"> <li role="none" className="my-0.5">
<button <button
onClick={() => toggleSubmenu(item.title)} onClick={() => toggleSubmenu(item.title)}
className="flex items-center justify-start text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none " className="flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none "
aria-expanded={submenuOpen} aria-expanded={submenuOpen}
aria-haspopup="true" aria-haspopup="true"
aria-controls={`submenu-${title}`} aria-controls={`submenu-${title}`}
> >
<span className="w-[20px] h-[20px] flex justify-center">
<i className={`${icon} text-[18px]`}></i> <i className={`${icon} text-[18px]`}></i>
</span>
<div className="flex justify-between items-center w-full"> <div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4 text-start"> <span className="ml-3 lg:ml-4">
{t(`sidebar.${item.title}`, { appName })} {t(`sidebar.${item.title}`, { appName })}
</span> </span>
<i <i
@@ -46,18 +45,16 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
className={({ isActive }) => className={({ isActive }) =>
`${ `${
isActive ? "bg-base-300 text-base-content" : "" isActive ? "bg-base-300 text-base-content" : ""
} flex items-center justify-start text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none` } flex items-center text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
} }
onClick={closeSidebar} onClick={closeSidebar}
role="menuitem" role="menuitem"
tabIndex={submenuOpen ? 0 : -1} tabIndex={submenuOpen ? 0 : -1}
> >
<span className="w-[15px] h-[15px] flex justify-center">
<i <i
className={`${childItem.icon} text-[18px]`} className={`${childItem.icon} text-[18px]`}
aria-hidden="true" aria-hidden="true"
></i> ></i>
</span>
<span className="ml-3 lg:ml-4"> <span className="ml-3 lg:ml-4">
{t(`sidebar.${item.title}-Children.${childItem.title}`, { {t(`sidebar.${item.title}-Children.${childItem.title}`, {
appName: drivename appName: drivename
+50 -93
View File
@@ -8,7 +8,6 @@ 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 { themeColor } from "./const"; import { themeColor } from "./const";
import { format, toZonedTime } from "date-fns-tz";
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"];
@@ -233,47 +232,17 @@ export const widgets = [
{ type: "email", icon: "fa-light fa-envelope", iconSize: "20px" } { type: "email", icon: "fa-light fa-envelope", iconSize: "20px" }
]; ];
export const getDate = (dateformat) => { export const getDate = () => {
const format = dateformat || "MM/DD/YYYY";
const date = new Date(); const date = new Date();
const milliseconds = date.getTime(); const milliseconds = date.getTime();
const newDate = moment(milliseconds).format(format); const newDate = moment(milliseconds).format("MM/DD/YYYY");
return newDate; return newDate;
}; };
export const addWidgetOptions = (type) => {
export const selectFormat = (data) => { const defaultOpt = {
switch (data) { name: type,
case "L": status: "required"
return "MM/dd/yyyy";
case "MM/DD/YYYY":
return "MM/dd/yyyy";
case "DD-MM-YYYY":
return "dd-MM-yyyy";
case "DD/MM/YYYY":
return "dd/MM/yyyy";
case "LL":
return "MMMM dd, yyyy";
case "DD MMM, YYYY":
return "dd MMM, yyyy";
case "YYYY-MM-DD":
return "yyyy-MM-dd";
case "MM-DD-YYYY":
return "MM-dd-yyyy";
case "MM.DD.YYYY":
return "MM.dd.yyyy";
case "MMM DD, YYYY":
return "MMM dd, yyyy";
case "MMMM DD, YYYY":
return "MMMM dd, yyyy";
case "DD MMMM, YYYY":
return "dd MMMM, yyyy";
default:
return "MM/dd/yyyy";
}
}; };
export const addWidgetOptions = (type, signer) => {
const defaultOpt = { name: type, status: "required" };
switch (type) { switch (type) {
case "signature": case "signature":
return defaultOpt; return defaultOpt;
@@ -294,16 +263,12 @@ export const addWidgetOptions = (type, signer) => {
return { ...defaultOpt }; return { ...defaultOpt };
case "job title": case "job title":
return { ...defaultOpt }; return { ...defaultOpt };
case "date": { case "date":
const dateFormat = signer?.DateFormat
? selectFormat(signer?.DateFormat)
: "MM/dd/yyyy";
return { return {
...defaultOpt, ...defaultOpt,
response: getDate(signer?.DateFormat), response: getDate(),
validation: { format: dateFormat, type: "date-format" } validation: { format: "MM/dd/yyyy", type: "date-format" }
}; };
}
case "image": case "image":
return defaultOpt; return defaultOpt;
case "email": case "email":
@@ -324,7 +289,7 @@ export const addWidgetOptions = (type, signer) => {
} }
}; };
export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => { export const addWidgetSelfsignOptions = (type, getWidgetValue) => {
switch (type) { switch (type) {
case "signature": case "signature":
return { name: "signature" }; return { name: "signature" };
@@ -354,16 +319,12 @@ export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
defaultValue: getWidgetValue(type), defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" } validation: { type: "text", pattern: "" }
}; };
case "date": { case "date":
const dateFormat = owner?.DateFormat
? selectFormat(owner?.DateFormat)
: "MM/dd/yyyy";
return { return {
name: "date", name: "date",
response: getDate(owner?.DateFormat), response: getDate(),
validation: { format: dateFormat, type: "date-format" } validation: { format: "MM/dd/yyyy", type: "date-format" }
}; };
}
case "image": case "image":
return { name: "image" }; return { name: "image" };
case "email": case "email":
@@ -643,15 +604,6 @@ export const createDocument = async (
const RedirectUrl = Doc?.RedirectUrl const RedirectUrl = Doc?.RedirectUrl
? { RedirectUrl: Doc?.RedirectUrl } ? { RedirectUrl: Doc?.RedirectUrl }
: {}; : {};
const TemplateId = Doc?.objectId
? {
TemplateId: {
__type: "Pointer",
className: "contracts_Template",
objectId: Doc?.objectId
}
}
: {};
const data = { const data = {
Name: Doc.Name, Name: Doc.Name,
URL: pdfUrl, URL: pdfUrl,
@@ -681,8 +633,7 @@ export const createDocument = async (
...SignatureType, ...SignatureType,
...NotifyOnSignatures, ...NotifyOnSignatures,
...Bcc, ...Bcc,
...RedirectUrl, ...RedirectUrl
...TemplateId
}; };
const remindOnceInEvery = Doc?.RemindOnceInEvery; const remindOnceInEvery = Doc?.RemindOnceInEvery;
const TimeToCompleteDays = parseInt(Doc?.TimeToCompleteDays); const TimeToCompleteDays = parseInt(Doc?.TimeToCompleteDays);
@@ -981,7 +932,10 @@ export const calculateInitialWidthHeight = (widgetData) => {
const height = span.offsetHeight; const height = span.offsetHeight;
document.body.removeChild(span); document.body.removeChild(span);
return { getWidth: width, getHeight: height }; return {
getWidth: width,
getHeight: height
};
}; };
export const addInitialData = (signerPos, setXyPosition, value, userId) => { export const addInitialData = (signerPos, setXyPosition, value, userId) => {
function widgetDataValue(type) { function widgetDataValue(type) {
@@ -1012,7 +966,9 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
) )
}; };
} else { } else {
return item; return {
...item
};
} }
} else if (item.pos && item.pos.length > 0) { } else if (item.pos && item.pos.length > 0) {
// If there is no nested array, add the new field // If there is no nested array, add the new field
@@ -1030,9 +986,17 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
...item.options, ...item.options,
defaultValue: item?.options?.defaultValue || widgetData defaultValue: item?.options?.defaultValue || widgetData
} }
// Width:
// calculateInitialWidthHeight(item.type, widgetData).getWidth ||
// item?.Width,
// Height:
// calculateInitialWidthHeight(item.type, widgetData).getHeight ||
// item?.Height
}; };
} else { } else {
return item; return {
...item
};
} }
} }
}); });
@@ -2192,9 +2156,7 @@ export const handleSendOTP = async (email) => {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId") "X-Parse-Application-Id": localStorage.getItem("parseAppId")
}; };
const body = { const body = { email: email };
email: email
};
await axios.post(url, body, { headers: headers }); await axios.post(url, body, { headers: headers });
} catch (error) { } catch (error) {
alert(error.message); alert(error.message);
@@ -2205,13 +2167,13 @@ export const fetchUrl = async (url, pdfName) => {
try { try {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
alert("something went wrong, refreshing this page may solve this issue."); alert("something went wrong, please try again later.");
throw new Error("Network response was not ok"); throw new Error("Network response was not ok");
} }
const blob = await response.blob(); const blob = await response.blob();
saveAs(blob, `${sanitizeFileName(pdfName)}_signed_by_${appName}.pdf`); saveAs(blob, `${sanitizeFileName(pdfName)}_signed_by_${appName}.pdf`);
} catch (error) { } catch (error) {
alert("something went wrong, refreshing this page may solve this issue."); alert("something went wrong, please try again later.");
console.error("Error downloading the file:", error); console.error("Error downloading the file:", error);
} }
}; };
@@ -2282,7 +2244,7 @@ export const handleDownloadPdf = async (
} catch (err) { } catch (err) {
console.log("err in getsignedurl", err); console.log("err in getsignedurl", err);
setIsDownloading(""); setIsDownloading("");
alert("something went wrong, refreshing this page may solve this issue."); alert("something went wrong, please try again later.");
} }
} }
}; };
@@ -2340,7 +2302,7 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
} catch (err) { } catch (err) {
setIsDownloading(""); setIsDownloading("");
console.log("err in getsignedurl", err); console.log("err in getsignedurl", err);
alert("something went wrong, refreshing this page may solve this issue."); alert("something went wrong, please try again later.");
} }
}; };
@@ -2426,7 +2388,7 @@ export const handleDownloadCertificate = async (
} catch (err) { } catch (err) {
setIsDownloading("certificate_err"); setIsDownloading("certificate_err");
console.log("err in download in certificate", err); console.log("err in download in certificate", err);
alert("something went wrong, refreshing this page may solve this issue."); alert("something went wrong, please try again later.");
} }
} }
}; };
@@ -2883,11 +2845,20 @@ export function generatePdfName(length) {
// Format date and time for the selected timezone // Format date and time for the selected timezone
export const formatTimeInTimezone = (date, timezone) => { export const formatTimeInTimezone = (date, timezone) => {
const nyDate = timezone && toZonedTime(date, timezone); return timezone
const generatedDate = timezone ? new Intl.DateTimeFormat("en-US", {
? format(nyDate, "EEE, dd MMM yyyy HH:mm:ss zzz", { timeZone: timezone }) weekday: "short",
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZone: timezone,
timeZoneName: "short",
hour12: false
}).format(date)
: new Date(date).toUTCString(); : new Date(date).toUTCString();
return generatedDate;
}; };
// `usertimezone` is used to get timezone of current user // `usertimezone` is used to get timezone of current user
@@ -2972,10 +2943,8 @@ export const mailTemplate = (param) => {
param.senderMail + param.senderMail +
"</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'> " + "</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'> " +
param.organization + param.organization +
"</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'>" + "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.localExpireDate + param.localExpireDate +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.note +
"</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" + "</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" +
param.sigingUrl + param.sigingUrl +
"><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;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 " + "><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;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 " +
@@ -2988,15 +2957,3 @@ export const mailTemplate = (param) => {
return { subject, body }; return { subject, body };
}; };
export function formatDateTime(date, dateFormat, timeZone, is12Hour) {
const zonedDate = toZonedTime(date, timeZone); // Convert date to the given timezone
const timeFormat = is12Hour ? "hh:mm:ss a" : "HH:mm:ss";
return dateFormat
? format(
zonedDate,
`${selectFormat(dateFormat)}, ${timeFormat} 'GMT' XXX`,
{ timeZone }
)
: formatTimeInTimezone(date, timeZone);
}
+3 -1
View File
@@ -1,9 +1,11 @@
import logo from "../assets/images/logo.png"; import logo from "../assets/images/logo.png";
export function serverUrl_fn() { export function serverUrl_fn() {
let baseUrl = process.env.REACT_APP_SERVERURL let baseUrl;
baseUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL ? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app"; : window.location.origin + "/api/app";
return baseUrl; return baseUrl;
} }
export const appInfo = { export const appInfo = {
-4
View File
@@ -4,7 +4,3 @@ export const documentCls = "contracts_Document";
export const themeColor = "#47a3ad"; export const themeColor = "#47a3ad";
export const iconColor = "#686968"; export const iconColor = "#686968";
export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
export const maxFileSize = 10; // 10MB
export const maxTitleLength = 250; // 250 characters
export const maxNoteLength = 200; // 200 characters
export const maxDescriptionLength = 500; // 500 characters
+8
View File
@@ -14,6 +14,7 @@ import {
Preview Preview
} from "react-dnd-multi-backend"; } from "react-dnd-multi-backend";
import DragElement from "./components/pdf/DragElement"; import DragElement from "./components/pdf/DragElement";
import TagManager from "react-gtm-module";
import Parse from "parse"; import Parse from "parse";
import "./polyfills"; import "./polyfills";
import { serverUrl_fn } from "./constant/appinfo"; import { serverUrl_fn } from "./constant/appinfo";
@@ -55,6 +56,13 @@ const generatePreview = (props) => {
); );
}; };
if (process.env.REACT_APP_GTM) {
const tagManagerArgs = {
gtmId: process.env.REACT_APP_GTM
};
TagManager.initialize(tagManagerArgs);
}
const root = ReactDOM.createRoot(document.getElementById("root")); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<CookiesProvider defaultSetOptions={{ path: "/" }}> <CookiesProvider defaultSetOptions={{ path: "/" }}>
+1 -100
View File
@@ -1,3 +1,4 @@
export default function reportJson(id) { export default function reportJson(id) {
// console.log("json ", json); // console.log("json ", json);
const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"]; const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"];
@@ -28,25 +29,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-trash", btnIcon: "fa-light fa-trash",
redirectUrl: "", redirectUrl: "",
action: "delete" action: "delete"
},
{
btnId: "22534",
hoverLabel: "option",
btnColor: "",
restrictBtn: true,
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1630",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
}
]
} }
], ],
helpMsg: helpMsg:
@@ -125,14 +107,6 @@ export default function reportJson(id) {
redirectUrl: "", redirectUrl: "",
action: "revoke" action: "revoke"
}, },
{
btnId: "0630",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
},
{ {
btnId: "1488", btnId: "1488",
btnLabel: "Delete", btnLabel: "Delete",
@@ -169,25 +143,6 @@ export default function reportJson(id) {
restrictBtn: true, restrictBtn: true,
redirectUrl: "", redirectUrl: "",
action: "delete" action: "delete"
},
{
btnId: "33534",
hoverLabel: "option",
btnColor: "",
restrictBtn: true,
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "0930",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
}
]
} }
], ],
helpMsg: helpMsg:
@@ -214,25 +169,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-trash", btnIcon: "fa-light fa-trash",
redirectUrl: "", redirectUrl: "",
action: "delete" action: "delete"
},
{
btnId: "44534",
hoverLabel: "option",
btnColor: "",
restrictBtn: true,
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "0940",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
}
]
} }
], ],
helpMsg: helpMsg:
@@ -276,14 +212,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-hourglass-end", btnIcon: "fa-light fa-hourglass-end",
redirectUrl: "", redirectUrl: "",
action: "extendexpiry" action: "extendexpiry"
},
{
btnId: "5530",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
} }
] ]
} }
@@ -345,14 +273,6 @@ export default function reportJson(id) {
redirectUrl: "", redirectUrl: "",
action: "revoke" action: "revoke"
}, },
{
btnId: "7730",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
},
{ {
btnId: "2000", btnId: "2000",
btnLabel: "Delete", btnLabel: "Delete",
@@ -403,25 +323,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-trash", btnIcon: "fa-light fa-trash",
redirectUrl: "", redirectUrl: "",
action: "delete" action: "delete"
},
{
btnId: "55534",
hoverLabel: "option",
btnColor: "",
restrictBtn: true,
textColor: "black",
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "6630",
btnLabel: "Save as template",
hoverLabel: "Save as template",
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "saveastemplate"
}
]
} }
] ]
}; };
+3 -2
View File
@@ -15,7 +15,8 @@ import { showHeader } from "../redux/reducers/showHeader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const HomeLayout = () => { const HomeLayout = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -71,7 +72,7 @@ const HomeLayout = () => {
//function to use save data in cookies storage //function to use save data in cookies storage
const saveCookies = () => { const saveCookies = () => {
const main_Domain = window.location.origin; const main_Domain = window.location.origin;
const domainName = window.location.hostname; const domainName = window.location.hostname; //app.opensignlabs.com
// Find the index of the first dot in the string // Find the index of the first dot in the string
const indexOfFirstDot = domainName.indexOf("."); const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character // Remove the first dot and get the substring starting from the next character
+22 -59
View File
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Confetti from "react-confetti"; // Import the confetti library
import { import {
getBase64FromUrl, getBase64FromUrl,
handleDownloadCertificate, handleDownloadCertificate,
@@ -11,25 +10,19 @@ import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import DownloadPdfZip from "../primitives/DownloadPdfZip"; import DownloadPdfZip from "../primitives/DownloadPdfZip";
import Title from "../components/Title"; import Title from "../components/Title";
import CheckCircle from "../primitives/CheckCircle";
const DocSuccessPage = () => { const DocSuccessPage = () => {
const { t } = useTranslation();
const signed = window.location?.search?.includes("docid"); const signed = window.location?.search?.includes("docid");
const sent = window.location?.search?.includes("message"); const sent = window.location?.search?.includes("message");
const [isDownloading, setIsDownloading] = useState(false); const [isDownloading, setIsDownloading] = useState(false);
const [isDownloadModal, setIsDownloadModal] = useState(false); const [isDownloadModal, setIsDownloadModal] = useState(false);
const [pdfDetails, setPdfDetails] = useState([]); const [pdfDetails, setPdfDetails] = useState([]);
const [pdfBase64Url, setPdfBase64Url] = useState(""); const [pdfBase64Url, setPdfBase64Url] = useState("");
const [showConfetti, setShowConfetti] = useState(true); // State to control confetti const { t } = useTranslation();
useEffect(() => { useEffect(() => {
initialsetup(); initialsetup();
// Stop confetti after 5 seconds
const timer = setTimeout(() => setShowConfetti(false), 5000);
return () => clearTimeout(timer);
}, []); }, []);
const initialsetup = async () => { const initialsetup = async () => {
const search = window.location.search.split("?")[1]; const search = window.location.search.split("?")[1];
if (search) { if (search) {
@@ -51,7 +44,6 @@ const DocSuccessPage = () => {
} }
} }
}; };
const handleDownload = () => { const handleDownload = () => {
if (pdfDetails?.[0]?.IsCompleted) { if (pdfDetails?.[0]?.IsCompleted) {
setIsDownloadModal(true); setIsDownloadModal(true);
@@ -59,80 +51,51 @@ const DocSuccessPage = () => {
handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url); handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url);
} }
}; };
return ( return (
<> <div className="flex h-screen justify-center items-center text-sm md:text-base">
<Title title="Success" /> <Title title="Success" />
{/* Confetti Effect */}
{showConfetti && (
<Confetti width={window.innerWidth} height={window.innerHeight} />
)}
{sent ? ( {sent ? (
<div className="min-h-screen flex flex-col items-center justify-center p-3 md:p-8 text-center"> <div>{t("doc-sent")}</div>
<div className="max-w-lg md:max-w-2xl bg-white rounded-lg shadow-lg p-3 md:p-10">
{t("doc-sent")}
</div>
</div>
) : signed ? ( ) : signed ? (
<> <div className="text-center">
<div className="min-h-screen flex flex-col items-center justify-center p-3 md:p-8 text-center"> <p>
<div className="max-w-lg md:max-w-2xl bg-white rounded-lg shadow-lg p-3 md:p-10">
<div className="flex flex-col items-center space-y-4 ">
<CheckCircle className="text-green-500 w-12 h-12 md:w-14 md:h-14" />
<h1 className="text-xl md:text-2xl font-semibold text-gray-800">
{pdfDetails?.[0]?.IsCompleted {pdfDetails?.[0]?.IsCompleted
? t("document-has-been-signed") ? t("document-signed-alert-4")
: t("document-has-been-signed-by-you")} : t("document-signed-alert")}
</h1>
{pdfDetails?.[0]?.IsCompleted && (
<p className="text-sm md:text-base text-gray-600">
{t("participant-completed-signing")}
</p> </p>
)} <div className="m-2">
</div>
{/* Action Buttons */}
<div className="mt-6 flex flex-wrap justify-center gap-2">
<button <button
onClick={(e) => handleToPrint(e, setIsDownloading, pdfDetails)}
type="button" type="button"
className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-primary" className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
onClick={() => handleDownload()}
> >
<i className="fa-light fa-download" aria-hidden="true"></i> <i className="fa-light fa-print" aria-hidden="true"></i>
<span>{t("download")}</span> <span className="hidden lg:block">{t("print")}</span>
</button> </button>
{pdfDetails?.[0]?.IsCompleted && ( {pdfDetails?.[0]?.IsCompleted && (
<button <button
type="button" type="button"
onClick={() => onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading) handleDownloadCertificate(pdfDetails, setIsDownloading)
} }
className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary" className="font-[500] text-[13px] mr-[5px] op-btn op-btn-secondary"
> >
<i <i
className="fa-light fa-award mx-[3px] md:mx-0" className="fa-light fa-award mx-[3px] lg:mx-0"
aria-hidden="true" aria-hidden="true"
></i> ></i>
<span>{t("certificate")}</span> <span className="hidden lg:block">{t("certificate")}</span>
</button> </button>
)} )}
<button <button
onClick={(e) =>
handleToPrint(e, setIsDownloading, pdfDetails)
}
type="button" type="button"
className="font-medium text-sm md:text-[13px] px-4 py-2 op-btn op-btn-neutral" className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary"
onClick={() => handleDownload()}
> >
<i className="fa-light fa-print" aria-hidden="true"></i> <i className="fa-light fa-download" aria-hidden="true"></i>
<span>{t("print")}</span> <span className="hidden lg:block">{t("download")}</span>
</button> </button>
</div> </div>
{/* Footer Message */}
<p className="mt-4 md:mt-6 text-xs md:text-sm text-gray-500">
{t("you-will-receive-email-shortly")}
</p>
</div>
</div>
{isDownloading === "pdf" && ( {isDownloading === "pdf" && (
<div className="fixed z-[1000] inset-0 flex justify-center items-center bg-black bg-opacity-30"> <div className="fixed z-[1000] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader /> <Loader />
@@ -151,7 +114,7 @@ const DocSuccessPage = () => {
} }
handleClose={() => setIsDownloading("")} handleClose={() => setIsDownloading("")}
> >
<div className="p-3 md:p-5 text-sm md:text-base text-center text-base-content"> <div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
{isDownloading === "certificate" ? ( {isDownloading === "certificate" ? (
<p>{t("generate-certificate-alert")}</p> <p>{t("generate-certificate-alert")}</p>
) : ( ) : (
@@ -166,11 +129,11 @@ const DocSuccessPage = () => {
isDocId={true} isDocId={true}
pdfBase64={pdfBase64Url} pdfBase64={pdfBase64Url}
/> />
</> </div>
) : ( ) : (
<></> <></>
)} )}
</> </div>
); );
}; };
+13 -18
View File
@@ -7,17 +7,17 @@ import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer"; import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Loader from "../primitives/Loader";
function ForgotPassword() { function ForgotPassword() {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
const [state, setState] = useState({ email: "", password: "", hideNav: "" }); const [state, setState] = useState({ email: "", password: "", hideNav: "" });
const [toast, setToast] = useState({ type: "", message: "" }); const [sentStatus, setSentStatus] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [image, setImage] = useState(); const [image, setImage] = useState();
const handleChange = (event) => { const handleChange = (event) => {
@@ -40,23 +40,18 @@ function ForgotPassword() {
if (!emailRegex.test(state.email)) { if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address."); alert("Please enter a valid email address.");
} else { } else {
setIsLoading(true);
localStorage.setItem("appLogo", appInfo.applogo); localStorage.setItem("appLogo", appInfo.applogo);
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings)); localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
if (state.email) { if (state.email) {
const username = state.email; const username = state.email;
try { try {
await Parse.User.requestPasswordReset(username); await Parse.User.requestPasswordReset(username);
setToast({ type: "success", message: t("reset-password-alert-1") }); setSentStatus("success");
} catch (err) { } catch (err) {
console.log("err ", err.code); console.log("err ", err.code);
setToast({ setSentStatus("failed");
type: "danger",
message: err.message || t("reset-password-alert-2")
});
} finally { } finally {
setIsLoading(false); setTimeout(() => setSentStatus(""), 1000);
setTimeout(() => setToast({ type: "", message: "" }), 1000);
} }
} }
} }
@@ -80,13 +75,13 @@ function ForgotPassword() {
}; };
return ( return (
<div> <div>
{isLoading && (
<div className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50">
<Loader />
</div>
)}
<Title title="Forgot password" /> <Title title="Forgot password" />
{toast?.message && <Alert type={toast.type}>{toast.message}</Alert>} {sentStatus === "success" && (
<Alert type="success">{t("reset-password-alert-1")}</Alert>
)}
{sentStatus === "failed" && (
<Alert type={"danger"}>{t("reset-password-alert-2")}</Alert>
)}
<div className="md:p-10 lg:p-16"> <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"> <div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div className="w-[250px] h-[66px] inline-block overflow-hidden"> <div className="w-[250px] h-[66px] inline-block overflow-hidden">
+14 -24
View File
@@ -18,12 +18,6 @@ import {
} from "../constant/Utils"; } from "../constant/Utils";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import axios from "axios"; import axios from "axios";
import {
maxFileSize,
maxDescriptionLength,
maxNoteLength,
maxTitleLength
} from "../constant/const";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip"; import { Tooltip } from "react-tooltip";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
@@ -44,6 +38,7 @@ function Form() {
const Forms = (props) => { const Forms = (props) => {
const appName = "OpenSign™"; const appName = "OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const maxFileSize = 20;
const abortController = new AbortController(); const abortController = new AbortController();
const inputFileRef = useRef(null); const inputFileRef = useRef(null);
const navigate = useNavigate(); const navigate = useNavigate();
@@ -192,6 +187,13 @@ const Forms = (props) => {
} }
} catch (err) { } catch (err) {
if (err?.message?.includes("is encrypted")) { if (err?.message?.includes("is encrypted")) {
try {
await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
}
try { try {
setIsDecrypting(true); setIsDecrypting(true);
const size = files?.[0].size; const size = files?.[0].size;
@@ -359,18 +361,6 @@ const Forms = (props) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (fileupload) { if (fileupload) {
if (formData?.Name?.length > maxTitleLength) {
alert(t("title-length-alert"));
return;
}
if (formData?.Note?.length > maxNoteLength) {
alert(t("note-length-alert"));
return;
}
if (formData?.Description?.length > maxDescriptionLength) {
alert(t("description-length-alert"));
return;
}
if (formData.RedirectUrl && !isValidURL(formData?.RedirectUrl)) { if (formData.RedirectUrl && !isValidURL(formData?.RedirectUrl)) {
alert(t("invalid-redirect-url")); alert(t("invalid-redirect-url"));
return; return;
@@ -869,7 +859,7 @@ const Forms = (props) => {
<div className="max-w-[200px] md:max-w-[450px]"> <div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("send-in-order")}</p> <p className="font-bold">{t("send-in-order")}</p>
<p>{t("send-in-order-help.p1")}</p> <p>{t("send-in-order-help.p1")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold"> <span className="font-bold">
@@ -884,7 +874,7 @@ const Forms = (props) => {
<span>{t("send-in-order-help.p3")}</span> <span>{t("send-in-order-help.p3")}</span>
</li> </li>
</ol> </ol>
</div> </p>
<p>{t("send-in-order-help.p4")}</p> <p>{t("send-in-order-help.p4")}</p>
</div> </div>
</Tooltip> </Tooltip>
@@ -966,7 +956,7 @@ const Forms = (props) => {
{t("send-in-order")} {t("send-in-order")}
</p> </p>
<p>{t("send-in-order-help.p1")}</p> <p>{t("send-in-order-help.p1")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold"> <span className="font-bold">
@@ -985,7 +975,7 @@ const Forms = (props) => {
</span> </span>
</li> </li>
</ol> </ol>
</div> </p>
<p>{t("send-in-order-help.p4")}</p> <p>{t("send-in-order-help.p4")}</p>
</div> </div>
</Tooltip> </Tooltip>
@@ -1103,7 +1093,7 @@ const Forms = (props) => {
<Tooltip id="istourenabled-tooltip" className="z-[999]"> <Tooltip id="istourenabled-tooltip" className="z-[999]">
<div className="max-w-[200px] md:max-w-[450px]"> <div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("enable-tour")}</p> <p className="font-bold">{t("enable-tour")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold"> <span className="font-bold">
@@ -1116,7 +1106,7 @@ const Forms = (props) => {
<span>{t("istourenabled-help.p2")}</span> <span>{t("istourenabled-help.p2")}</span>
</li> </li>
</ol> </ol>
</div> </p>
<p> <p>
{t("istourenabled-help.p3", { appName: appName })} {t("istourenabled-help.p3", { appName: appName })}
</p> </p>
+19 -32
View File
@@ -1,8 +1,13 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import axios from "axios"; import axios from "axios";
import { emailRegex } from "../constant/const"; import {
import { contractUsers, saveLanguageInLocal } from "../constant/Utils"; emailRegex,
} from "../constant/const";
import {
contractUsers,
saveLanguageInLocal
} from "../constant/Utils";
import logo from "../assets/images/logo.png"; import logo from "../assets/images/logo.png";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import Parse from "parse"; import Parse from "parse";
@@ -15,9 +20,7 @@ function GuestLogin() {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const { id, userMail, contactBookId, base64url } = useParams(); const { id, userMail, contactBookId, base64url } = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const [email, setEmail] = useState( const [email, setEmail] = useState(userMail);
userMail?.toLowerCase()?.replace(/\s/g, "")
);
const [OTP, setOTP] = useState(""); const [OTP, setOTP] = useState("");
const [EnterOTP, setEnterOtp] = useState(false); const [EnterOTP, setEnterOtp] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -63,7 +66,10 @@ function GuestLogin() {
setAppLogo(logo); setAppLogo(logo);
localStorage.clear(); // Clears everything localStorage.clear(); // Clears everything
localStorage.setItem("appname", "OpenSign™"); localStorage.setItem(
"appname",
"OpenSign™"
);
//save isGuestSigner true in local to handle login flow header in mobile view //save isGuestSigner true in local to handle login flow header in mobile view
localStorage.setItem("isGuestSigner", true); localStorage.setItem("isGuestSigner", true);
saveLanguageInLocal(i18n); saveLanguageInLocal(i18n);
@@ -78,18 +84,12 @@ function GuestLogin() {
//split url in array from '/' //split url in array from '/'
const checkSplit = decodebase64.split("/"); const checkSplit = decodebase64.split("/");
setDocumentId(checkSplit[0]); setDocumentId(checkSplit[0]);
setContact((prev) => ({ setContact((prev) => ({ ...prev, email: checkSplit[1] }));
...prev, setEmail(checkSplit[1]);
email: checkSplit[1]?.toLowerCase()?.replace(/\s/g, "")
}));
setEmail(checkSplit[1]?.toLowerCase()?.replace(/\s/g, ""));
const contactId = checkSplit?.[2]; const contactId = checkSplit?.[2];
setSendmail(checkSplit[3]); setSendmail(checkSplit[3]);
if (!contactId) { if (!contactId) {
const params = { const params = { email: checkSplit[1], docId: checkSplit[0] };
email: checkSplit[1]?.toLowerCase()?.replace(/\s/g, ""),
docId: checkSplit[0]
};
try { try {
const linkContactRes = await Parse.Cloud.run( const linkContactRes = await Parse.Cloud.run(
"linkcontacttodoc", "linkcontacttodoc",
@@ -111,12 +111,9 @@ function GuestLogin() {
//send email OTP function //send email OTP function
const SendOtp = async () => { const SendOtp = async () => {
setLoading(true); setLoading(true);
setEmail(email?.toLowerCase()?.replace(/\s/g, "")); setEmail(email);
try { try {
const params = { const params = { email: email.toString(), docId: documentId };
email: email?.toLowerCase()?.replace(/\s/g, "")?.toString(),
docId: documentId
};
const Otp = await Parse.Cloud.run("SendOTPMailV1", params); const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
if (Otp) { if (Otp) {
setLoading(false); setLoading(false);
@@ -147,10 +144,7 @@ function GuestLogin() {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": parseId "X-Parse-Application-Id": parseId
}; };
let body = { let body = { email: email, otp: OTP };
email: email?.toLowerCase()?.replace(/\s/g, ""),
otp: OTP
};
let user = await axios.post(url, body, { headers: headers }); let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") { if (user.data.result === "Invalid Otp") {
alert(t("invalid-otp")); alert(t("invalid-otp"));
@@ -195,7 +189,7 @@ function GuestLogin() {
}; };
const handleUserData = async (e) => { const handleUserData = async (e) => {
e.preventDefault(); e.preventDefault();
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) { if (!emailRegex.test(contact.email)) {
alert("Please enter a valid email address."); alert("Please enter a valid email address.");
} else { } else {
const params = { ...contact, docId: documentId }; const params = { ...contact, docId: documentId };
@@ -222,14 +216,7 @@ function GuestLogin() {
} }
}; };
const handleInputChange = (e) => { const handleInputChange = (e) => {
if (e.target.name === "email") {
setContact((prev) => ({
...prev,
[e.target.name]: e.target.value?.toLowerCase()?.replace(/\s/g, "")
}));
} else {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value })); setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
}
}; };
return ( return (
<div> <div>
+114 -25
View File
@@ -7,7 +7,9 @@ import { NavLink, useNavigate, useLocation } from "react-router";
import login_img from "../assets/images/login_img.svg"; import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize"; import { useWindowSize } from "../hook/useWindowSize";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { emailRegex } from "../constant/const"; import {
emailRegex,
} from "../constant/const";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer"; import { fetchAppInfo } from "../redux/reducers/infoReducer";
@@ -22,7 +24,8 @@ import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage"; import SelectLanguage from "../components/pdf/SelectLanguage";
function Login() { function Login() {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -40,7 +43,7 @@ function Login() {
baseUrl: localStorage.getItem("baseUrl"), baseUrl: localStorage.getItem("baseUrl"),
parseAppId: localStorage.getItem("parseAppId"), parseAppId: localStorage.getItem("parseAppId"),
loading: false, loading: false,
thirdpartyLoader: false thirdpartyLoader: false,
}); });
const [userDetails, setUserDetails] = useState({ const [userDetails, setUserDetails] = useState({
Company: "", Company: "",
@@ -54,15 +57,13 @@ function Login() {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
const showToast = (type, msg) => {
setState({ ...state, loading: false, alertType: type, alertMsg: msg });
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
};
const checkUserExt = async () => { const checkUserExt = async () => {
const app = await getAppLogo(); const app = await getAppLogo();
if (app?.error === "invalid_json") { if (app?.error === "invalid_json") {
setErrMsg(t("server-down", { appName: appName })); setErrMsg(t("server-down", { appName: appName }));
} else if (app?.user === "not_exist") { } else if (
app?.user === "not_exist"
) {
navigate("/addadmin"); navigate("/addadmin");
} }
if (app?.logo) { if (app?.logo) {
@@ -160,25 +161,60 @@ function Login() {
setIsModal(true); setIsModal(true);
} }
} else { } else {
showToast("danger", t("do-not-access-contact-admin")); setState({
...state,
loading: false,
alertType: "danger",
alertMsg:
"You don't have access, please contact the admin."
});
logOutUser(); logOutUser();
} }
} else { } else {
showToast("danger", t("user-not-found")); setState({ ...state, loading: false });
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "User not found."
});
logOutUser(); logOutUser();
} }
}) })
.catch((error) => { .catch((error) => {
showToast("danger", t("something-went-wrong-mssg")); setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `Something went wrong.`
});
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
console.error("Error while fetching Follow", error); console.error("Error while fetching Follow", error);
}); });
} catch (error) { } catch (error) {
showToast("danger", `${error.message}`); setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `${error.message}`
});
console.log(error);
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
} }
} }
} catch (error) { } catch (error) {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "Invalid username/password or region"
});
console.error("Error while logging in user", error); console.error("Error while logging in user", error);
showToast("danger", "Invalid username/password or region"); } finally {
setTimeout(
() => setState((prev) => ({ ...prev, alertMsg: "" })),
2000
);
} }
} }
} }
@@ -248,29 +284,53 @@ function Login() {
localStorage.setItem("pageType", menu.pageType); localStorage.setItem("pageType", menu.pageType);
navigate(redirectUrl); navigate(redirectUrl);
} else { } else {
showToast("danger", t("role-not-found")); setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "Role not found."
});
logOutUser(); logOutUser();
} }
} else { } else {
showToast("danger", t("do-not-access-contact-admin")); setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
});
logOutUser(); logOutUser();
} }
} else { } else {
showToast("danger", t("user-not-found")); setState({
...state,
alertType: "danger",
alertMsg: "User not found."
});
logOutUser(); logOutUser();
} }
}) })
.catch((err) => { .catch((err) => {
console.error("err in fetching extUser", err); console.error("err in fetching extUser", err);
showToast("danger", `${err.message}`); setState({
...state,
alertType: "danger",
alertMsg: `${err.message}`
});
const payload = { sessionToken: sessionToken }; const payload = { sessionToken: sessionToken };
handleSubmitbtn(payload); handleSubmitbtn(payload);
}); });
} catch (error) { } catch (error) {
showToast("danger", `${error.message}`); setState({
...state,
alertType: "danger",
alertMsg: `${error.message}`
});
console.log(error); console.log(error);
} finally { } finally {
setThirdpartyLoader(false); setThirdpartyLoader(false);
setState({ ...state, loading: false });
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
} }
} }
}; };
@@ -327,17 +387,33 @@ function Login() {
logOutUser(); logOutUser();
} }
} else { } else {
showToast("danger", t("do-not-access-contact-admin")); setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
});
logOutUser(); logOutUser();
} }
} else { } else {
showToast("danger", t("user-not-found")); setState({
...state,
alertType: "danger",
alertMsg: "User not found."
});
logOutUser(); logOutUser();
} }
}); });
} catch (error) { } catch (error) {
showToast("danger", t("something-went-wrong-mssg")); setState({
...state,
alertType: "danger",
alertMsg: "Something went wrong, please try again later."
});
console.log("err", error); console.log("err", error);
} finally {
setState({ ...state, loading: false });
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
} }
}; };
@@ -349,6 +425,8 @@ function Login() {
e.preventDefault(); e.preventDefault();
if (userDetails.Destination && userDetails.Company) { if (userDetails.Destination && userDetails.Company) {
setThirdpartyLoader(true); setThirdpartyLoader(true);
// console.log("handelSubmit", userDetails);
// const payload = await Parse.User.logIn(state.email, state.password);
const payload = { sessionToken: localStorage.getItem("accesstoken") }; const payload = { sessionToken: localStorage.getItem("accesstoken") };
const userInformation = JSON.parse( const userInformation = JSON.parse(
localStorage.getItem("UserInformation") localStorage.getItem("UserInformation")
@@ -388,7 +466,13 @@ function Login() {
alert(t("server-error")); alert(t("server-error"));
} }
} else { } else {
showToast("warning", t("fill-required-details!")); setState({
...state,
loading: false,
alertType: "warning",
alertMsg: "Please fill required details."
});
setTimeout(() => setState((prev) => ({ ...prev, alertMsg: "" })), 2000);
} }
}; };
@@ -417,6 +501,7 @@ function Login() {
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
}; };
return errMsg ? ( return errMsg ? (
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base"> <div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
{errMsg} {errMsg}
@@ -437,7 +522,7 @@ function Login() {
<div <div
aria-labelledby="loginHeading" aria-labelledby="loginHeading"
role="region" role="region"
className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16 h-screen" className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16"
> >
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card"> <div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div className="w-[250px] h-[66px] inline-block overflow-hidden"> <div className="w-[250px] h-[66px] inline-block overflow-hidden">
@@ -482,14 +567,18 @@ function Login() {
<div className="relative"> <div className="relative">
<input <input
id="password" id="password"
type={state.passwordVisible ? "text" : "password"} type={
state.passwordVisible ? "text" : "password"
}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="password" name="password"
value={state.password} value={state.password}
autoComplete="current-password" autoComplete="current-password"
onChange={handleChange} onChange={handleChange}
onInvalid={(e) => onInvalid={(e) =>
e.target.setCustomValidity(t("input-required")) e.target.setCustomValidity(
t("input-required")
)
} }
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
required required
+11 -10
View File
@@ -1,7 +1,11 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import "../styles/opensigndrive.css"; import "../styles/opensigndrive.css";
import { iconColor } from "../constant/const"; import {
import { getDrive } from "../constant/Utils"; iconColor,
} from "../constant/const";
import {
getDrive
} from "../constant/Utils";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import Title from "../components/Title"; import Title from "../components/Title";
import Parse from "parse"; import Parse from "parse";
@@ -25,7 +29,8 @@ const AppLoader = () => {
); );
}; };
function Opensigndrive() { function Opensigndrive() {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -139,7 +144,7 @@ function Opensigndrive() {
try { try {
const driveDetails = await getDrive(docId, skip, limit); const driveDetails = await getDrive(docId, skip, limit);
if (driveDetails && driveDetails === "Error: Something went wrong!") { if (driveDetails && driveDetails === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg")); setHandleError("Error: Something went wrong!");
} else if (driveDetails && driveDetails.length > 0) { } else if (driveDetails && driveDetails.length > 0) {
const addMoreTour = [ const addMoreTour = [
{ {
@@ -630,9 +635,7 @@ function Opensigndrive() {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div <div
id="folder-menu" id="folder-menu"
className={`${ className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block`}
isOptions ? "dropdown show dropDownStyle" : "dropdown"
} hidden md:block`}
onClick={() => setIsOptions(!isOptions)} onClick={() => setIsOptions(!isOptions)}
> >
<div className="sort" data-tut="reactourSecond"> <div className="sort" data-tut="reactourSecond">
@@ -770,9 +773,7 @@ function Opensigndrive() {
</div> </div>
<div <div
id="folder-menu" id="folder-menu"
className={`${ className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} md:hidden`}
isOptions ? "dropdown show dropDownStyle" : "dropdown"
} md:hidden`}
onClick={() => setIsOptions(!isOptions)} onClick={() => setIsOptions(!isOptions)}
> >
<div <div
+54 -46
View File
@@ -64,9 +64,11 @@ import WidgetComponent from "../components/pdf/WidgetComponent";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy"; import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import TextFontSetting from "../components/pdf/TextFontSetting"; import TextFontSetting from "../components/pdf/TextFontSetting";
function PdfRequestFiles() { function PdfRequestFiles(
) {
const { t } = useTranslation(); const { t } = useTranslation();
const appName = "OpenSign™"; const appName =
"OpenSign™";
const [pdfDetails, setPdfDetails] = useState([]); const [pdfDetails, setPdfDetails] = useState([]);
const [signedSigners, setSignedSigners] = useState([]); const [signedSigners, setSignedSigners] = useState([]);
const [unsignedSigners, setUnSignedSigners] = useState([]); const [unsignedSigners, setUnSignedSigners] = useState([]);
@@ -159,7 +161,6 @@ function PdfRequestFiles() {
signId: "" signId: ""
}); });
const [showSignPagenumber, setShowSignPagenumber] = useState([]); const [showSignPagenumber, setShowSignPagenumber] = useState([]);
const [owner, setOwner] = useState({});
const [, drop] = useDrop({ const [, drop] = useDrop({
accept: "BOX", accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor), drop: (item, monitor) => addPositionOfSignature(item, monitor),
@@ -170,8 +171,10 @@ function PdfRequestFiles() {
let sendmail; let sendmail;
let getDocId = ""; let getDocId = "";
let contactBookId = ""; let contactBookId = "";
const route = window.location.pathname; const route =
const getQuery = window.location?.search?.split("?"); //['','sendmail=false'] window.location.pathname;
const getQuery =
window.location?.search?.split("?"); //['','sendmail=false']
if (getQuery) { if (getQuery) {
sendmail = getQuery?.[1]?.split("=")[1]; //false sendmail = getQuery?.[1]?.split("=")[1]; //false
} }
@@ -194,7 +197,9 @@ function PdfRequestFiles() {
getDocumentDetails(getDocumentId); getDocumentDetails(getDocumentId);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [getDocumentId]); }, [
getDocumentId
]);
useEffect(() => { useEffect(() => {
const updateSize = () => { const updateSize = () => {
if (divRef.current) { if (divRef.current) {
@@ -296,7 +301,6 @@ function PdfRequestFiles() {
setHandleError(t("something-went-wrong-mssg")); setHandleError(t("something-went-wrong-mssg"));
} }
setExtUserId(documentData[0]?.ExtUserPtr?.objectId); setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
setOwner(documentData?.[0]?.ExtUserPtr);
const isCompleted = const isCompleted =
documentData[0].IsCompleted && documentData[0].IsCompleted; documentData[0].IsCompleted && documentData[0].IsCompleted;
const expireDate = documentData[0].ExpiryDate.iso; const expireDate = documentData[0].ExpiryDate.iso;
@@ -521,14 +525,6 @@ function PdfRequestFiles() {
} }
); );
const contact = resContact?.data?.result; const contact = resContact?.data?.result;
localStorage.setItem(
"signer",
JSON.stringify({
Name: contact?.Name,
Email: contact?.Email,
UserId: contact?.UserId?.objectId
})
);
setContractName("_Contactbook"); setContractName("_Contactbook");
setSignerUserId(contact?.objectId); setSignerUserId(contact?.objectId);
const tourData = contact?.TourStatus && contact?.TourStatus; const tourData = contact?.TourStatus && contact?.TourStatus;
@@ -563,7 +559,7 @@ function PdfRequestFiles() {
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} catch (err) { } catch (err) {
console.log("Error: error in getDocumentDetails", err); console.log("Error: error in getDocumentDetails", err);
setHandleError(t("something-went-wrong-mssg")); setHandleError("Error: Something went wrong!");
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} }
}; };
@@ -768,11 +764,7 @@ function PdfRequestFiles() {
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
) { ) {
if (documentData?.result?.error?.includes("deleted")) { setHandleError("Error: Something went wrong!");
setHandleError(t("document-deleted"));
} else {
setHandleError(t("something-went-wrong-mssg"));
}
} else { } else {
setHandleError("Document not Found!"); setHandleError("Document not Found!");
} }
@@ -841,14 +833,14 @@ function PdfRequestFiles() {
"en-US", "en-US",
{ day: "numeric", month: "long", year: "numeric" } { day: "numeric", month: "long", year: "numeric" }
); );
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; let senderEmail =
pdfDetails?.[0]?.ExtUserPtr?.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = pdfDetails?.[0].ExtUserPtr.Name; const senderName =
pdfDetails?.[0].ExtUserPtr.Name;
const documentName = pdfDetails?.[0].Name; const documentName = pdfDetails?.[0].Name;
try { try {
let url = `${localStorage.getItem( let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
"baseUrl"
)}functions/sendmailv3`;
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": "X-Parse-Application-Id":
@@ -868,12 +860,16 @@ function PdfRequestFiles() {
`${pdfDetails?.[0].objectId}/${user.Email}` `${pdfDetails?.[0].objectId}/${user.Email}`
); );
} }
let signPdf = `${hostUrl}/login/${encodeBase64}`; let signPdf =
`${hostUrl}/login/${encodeBase64}`;
const orgName = pdfDetails[0]?.ExtUserPtr.Company const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company
: ""; : "";
let replaceVar; let replaceVar;
if (requestBody && requestSubject) { if (
requestBody &&
requestSubject
) {
const replacedRequestBody = requestBody.replace( const replacedRequestBody = requestBody.replace(
/"/g, /"/g,
"'" "'"
@@ -885,7 +881,6 @@ function PdfRequestFiles() {
const variables = { const variables = {
document_title: documentName, document_title: documentName,
note: pdfDetails?.[0]?.Note,
sender_name: senderName, sender_name: senderName,
sender_mail: senderEmail, sender_mail: senderEmail,
sender_phone: senderPhone, sender_phone: senderPhone,
@@ -903,7 +898,6 @@ function PdfRequestFiles() {
); );
} }
const mailparam = { const mailparam = {
note: pdfDetails?.[0]?.Note || "",
senderName: senderName, senderName: senderName,
senderMail: senderEmail, senderMail: senderEmail,
title: documentName, title: documentName,
@@ -918,7 +912,8 @@ function PdfRequestFiles() {
subject: replaceVar?.subject subject: replaceVar?.subject
? replaceVar?.subject ? replaceVar?.subject
: mailTemplate(mailparam).subject, : mailTemplate(mailparam).subject,
from: senderEmail, from:
senderEmail,
html: replaceVar?.body html: replaceVar?.body
? replaceVar?.body ? replaceVar?.body
: mailTemplate(mailparam).body : mailTemplate(mailparam).body
@@ -934,15 +929,12 @@ function PdfRequestFiles() {
} else { } else {
const url = const url =
updateDoc?.[0]?.SignedUrl || updateDoc?.[0]?.URL; updateDoc?.[0]?.SignedUrl || updateDoc?.[0]?.URL;
const fileAdapter = ""; const fileAdapter =
"";
const isCompleted = updateDoc?.[0]?.IsCompleted const isCompleted = updateDoc?.[0]?.IsCompleted
? `&completed=true` ? `&completed=true`
: ""; : "";
const params = `docid=${ const params = `docid=${updateDoc[0].objectId}&docurl=${encodeURIComponent(url)}${isCompleted}${fileAdapter}`;
updateDoc[0].objectId
}&docurl=${encodeURIComponent(
url
)}${isCompleted}${fileAdapter}`;
window.location.href = `/success?${params}`; window.location.href = `/success?${params}`;
} }
} else { } else {
@@ -1523,6 +1515,7 @@ function PdfRequestFiles() {
); );
}; };
const clickOnZoomIn = () => { const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent); onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
}; };
@@ -1659,7 +1652,7 @@ function PdfRequestFiles() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1686,7 +1679,7 @@ function PdfRequestFiles() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1837,7 +1830,11 @@ function PdfRequestFiles() {
}; };
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} /> <Title
title={
"Request Sign"
}
/>
{isLoading.isLoad ? ( {isLoading.isLoad ? (
<LoaderWithMsg isLoading={isLoading} /> <LoaderWithMsg isLoading={isLoading} />
) : handleError ? ( ) : handleError ? (
@@ -1902,7 +1899,9 @@ function PdfRequestFiles() {
: "auto" : "auto"
}} }}
className={`${ className={`${
isGuestSignFlow ? "border-[0.5px] border-gray-300" : "op-card" isGuestSignFlow
? "border-[0.5px] border-gray-300"
: "op-card"
} relative overflow-hidden flex flex-col md:flex-row justify-between bg-base-300`} } relative overflow-hidden flex flex-col md:flex-row justify-between bg-base-300`}
> >
{!requestSignTour && {!requestSignTour &&
@@ -2033,7 +2032,8 @@ function PdfRequestFiles() {
setIsCompleted((prev) => ({ ...prev, isModal: false })) setIsCompleted((prev) => ({ ...prev, isModal: false }))
} }
reduceWidth={ reduceWidth={
!isCompleted?.message && "md:min-w-[440px] md:max-w-[400px]" !isCompleted?.message &&
"md:min-w-[440px] md:max-w-[400px]"
} }
> >
<div className="h-full p-[20px] text-base-content"> <div className="h-full p-[20px] text-base-content">
@@ -2042,7 +2042,9 @@ function PdfRequestFiles() {
<p>{isCompleted?.message}</p> <p>{isCompleted?.message}</p>
{!isredirectCanceled && redirectUrl && ( {!isredirectCanceled && redirectUrl && (
<div className="flex flex-row gap-1 items-center justify-center mb-3 mt-2"> <div className="flex flex-row gap-1 items-center justify-center mb-3 mt-2">
<p>Redirecting you in {redirectTimeLeft} sec...</p> <p>
Redirecting you in {redirectTimeLeft} sec...
</p>
<button <button
onClick={handleRedirectCancel} onClick={handleRedirectCancel}
className="underline cursor-pointer op-text-primary focus:outline-none ml-2" className="underline cursor-pointer op-text-primary focus:outline-none ml-2"
@@ -2061,7 +2063,9 @@ function PdfRequestFiles() {
<div className="flex flex-col mt-3 gap-1 px-[10px] justify-center items-center"> <div className="flex flex-col mt-3 gap-1 px-[10px] justify-center items-center">
{!isredirectCanceled && redirectUrl && ( {!isredirectCanceled && redirectUrl && (
<div className="flex flex-row gap-1 items-center justify-center mb-3"> <div className="flex flex-row gap-1 items-center justify-center mb-3">
<p>Redirecting you in {redirectTimeLeft} sec...</p> <p>
Redirecting you in {redirectTimeLeft} sec...
</p>
<button <button
onClick={handleRedirectCancel} onClick={handleRedirectCancel}
className="underline cursor-pointer op-text-primary focus:outline-none ml-2" className="underline cursor-pointer op-text-primary focus:outline-none ml-2"
@@ -2187,7 +2191,9 @@ function PdfRequestFiles() {
)} )}
{/* pdf header which contain funish back button */} {/* pdf header which contain funish back button */}
<Header <Header
isPdfRequestFiles={true} isPdfRequestFiles={
true
}
pageNumber={pageNumber} pageNumber={pageNumber}
allPages={allPages} allPages={allPages}
changePage={changePage} changePage={changePage}
@@ -2195,7 +2201,9 @@ function PdfRequestFiles() {
signerPos={signerPos} signerPos={signerPos}
isSigned={isSigned} isSigned={isSigned}
isCompleted={isCompleted.isCertificate} isCompleted={isCompleted.isCertificate}
embedWidgetsData={embedWidgetsData} embedWidgetsData={
embedWidgetsData
}
isShowHeader={true} isShowHeader={true}
setIsDecline={setIsDecline} setIsDecline={setIsDecline}
decline={true} decline={true}
+23 -69
View File
@@ -3,7 +3,6 @@ import axios from "axios";
import Parse from "parse"; import Parse from "parse";
import "../styles/signature.css"; import "../styles/signature.css";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import { maxTitleLength } from "../constant/const";
import { DndProvider } from "react-dnd"; import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrop } from "react-dnd"; import { useDrop } from "react-dnd";
@@ -151,6 +150,7 @@ function PlaceHolderSign() {
status: false, status: false,
message: "" message: ""
}); });
const [extUserId, setExtUserId] = useState("");
const [isCustomize, setIsCustomize] = useState(false); const [isCustomize, setIsCustomize] = useState(false);
const [zoomPercent, setZoomPercent] = useState(0); const [zoomPercent, setZoomPercent] = useState(0);
const [scale, setScale] = useState(1); const [scale, setScale] = useState(1);
@@ -164,14 +164,13 @@ function PlaceHolderSign() {
const [userList, setUserList] = useState([]); const [userList, setUserList] = useState([]);
const [isAttchSignerModal, setIsAttchSignerModal] = useState(false); const [isAttchSignerModal, setIsAttchSignerModal] = useState(false);
const [isNewContact, setIsNewContact] = useState({ status: false, id: "" }); const [isNewContact, setIsNewContact] = useState({ status: false, id: "" });
const [owner, setOwner] = useState({});
const [docTitle, setDocTitle] = useState("");
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({ const [, drop] = useDrop({
accept: "BOX", accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor), drop: (item, monitor) => addPositionOfSignature(item, monitor),
collect: (monitor) => ({ isOver: !!monitor.isOver() }) collect: (monitor) => ({ isOver: !!monitor.isOver() })
}); });
const documentId = docId; const documentId = docId;
useEffect(() => { useEffect(() => {
if (documentId) { if (documentId) {
@@ -245,7 +244,6 @@ function PlaceHolderSign() {
//getting document details //getting document details
const documentData = await contractDocument(documentId); const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
setDocTitle(documentData?.[0]?.Name);
if (documentData[0]?.Placeholders?.length > 0) { if (documentData[0]?.Placeholders?.length > 0) {
const signerNotExist = documentData[0]?.Placeholders.some( const signerNotExist = documentData[0]?.Placeholders.some(
(data) => !data.signerObjId && data.Role !== "prefill" (data) => !data.signerObjId && data.Role !== "prefill"
@@ -288,7 +286,7 @@ function PlaceHolderSign() {
setPdfArrayBuffer(arrayBuffer); setPdfArrayBuffer(arrayBuffer);
setPdfBase64Url(base64Pdf); setPdfBase64Url(base64Pdf);
} }
setOwner(documentData?.[0]?.ExtUserPtr); setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const alreadyPlaceholder = documentData[0]?.SignedUrl; const alreadyPlaceholder = documentData[0]?.SignedUrl;
// Check if document is sent for signing // Check if document is sent for signing
if (alreadyPlaceholder) { if (alreadyPlaceholder) {
@@ -459,11 +457,7 @@ function PlaceHolderSign() {
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
) { ) {
if (documentData?.result?.error?.includes("deleted")) {
setHandleError(t("document-deleted"));
} else {
setHandleError(t("something-went-wrong-mssg")); setHandleError(t("something-went-wrong-mssg"));
}
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} else { } else {
setHandleError(t("no-data-avaliable")); setHandleError(t("no-data-avaliable"));
@@ -531,7 +525,7 @@ function PlaceHolderSign() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -562,7 +556,7 @@ function PlaceHolderSign() {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1013,7 +1007,6 @@ function PlaceHolderSign() {
const saveDocumentDetails = async () => { const saveDocumentDetails = async () => {
setIsUiLoading(true); setIsUiLoading(true);
let signerMail = signersdata.slice(); let signerMail = signersdata.slice();
// For "Send in order", only consider the first signer
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) { if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
signerMail.splice(1); signerMail.splice(1);
} }
@@ -1026,7 +1019,7 @@ function PlaceHolderSign() {
objectId: x.objectId objectId: x.objectId
}; };
}); });
const addExtraDays = pdfDetails?.[0]?.TimeToCompleteDays const addExtraDays = pdfDetails[0]?.TimeToCompleteDays
? pdfDetails[0].TimeToCompleteDays ? pdfDetails[0].TimeToCompleteDays
: 15; : 15;
const currentUser = signersdata.find((x) => x.Email === currentId); const currentUser = signersdata.find((x) => x.Email === currentId);
@@ -1041,22 +1034,22 @@ function PlaceHolderSign() {
} else { } else {
setIsCurrUser(currentUser?.objectId ? true : false); setIsCurrUser(currentUser?.objectId ? true : false);
} }
// Compute expiry date with extra days let updateExpiryDate, data;
let updateExpiryDate = new Date(); updateExpiryDate = new Date();
updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays); updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays);
//filter label widgets after add label widgets data on pdf
// Filter out prefill roles
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill"); const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
try { try {
const data = { data = {
Name: docTitle || pdfDetails?.[0]?.Name,
Placeholders: filterPrefill, Placeholders: filterPrefill,
SignedUrl: pdfUrl, SignedUrl: pdfUrl,
Signers: signers, Signers: signers,
SentToOthers: true, SentToOthers: true,
SignatureType: pdfDetails?.[0]?.SignatureType, SignatureType: pdfDetails?.[0]?.SignatureType
ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
}; };
if (updateExpiryDate) {
data["ExpiryDate"] = { iso: updateExpiryDate, __type: "Date" };
}
await axios.put( await axios.put(
`${localStorage.getItem( `${localStorage.getItem(
"baseUrl" "baseUrl"
@@ -1075,11 +1068,6 @@ function PlaceHolderSign() {
setIsUiLoading(false); setIsUiLoading(false);
setSignerPos([]); setSignerPos([]);
setIsSendAlert({ mssg: "confirm", alert: true }); setIsSendAlert({ mssg: "confirm", alert: true });
if (docTitle) {
const updatedPdfDetails = [...pdfDetails];
updatedPdfDetails[0].Name = docTitle;
setPdfDetails(updatedPdfDetails);
}
} catch (e) { } catch (e) {
console.log("error", e); console.log("error", e);
alert(t("something-went-wrong-mssg")); alert(t("something-went-wrong-mssg"));
@@ -1191,7 +1179,6 @@ function PlaceHolderSign() {
const variables = { const variables = {
document_title: documentName, document_title: documentName,
note: pdfDetails?.[0]?.Note,
sender_name: senderName, sender_name: senderName,
sender_mail: senderEmail, sender_mail: senderEmail,
sender_phone: senderPhone || "", sender_phone: senderPhone || "",
@@ -1217,7 +1204,6 @@ function PlaceHolderSign() {
"</body> </html>"; "</body> </html>";
const variables = { const variables = {
document_title: documentName, document_title: documentName,
note: pdfDetails?.[0]?.Note,
sender_name: senderName, sender_name: senderName,
sender_mail: senderEmail, sender_mail: senderEmail,
sender_phone: senderPhone || "", sender_phone: senderPhone || "",
@@ -1232,7 +1218,6 @@ function PlaceHolderSign() {
} }
const mailparam = { const mailparam = {
senderName: senderName, senderName: senderName,
note: pdfDetails?.[0]?.Note || "",
senderMail: senderEmail, senderMail: senderEmail,
title: documentName, title: documentName,
organization: orgName, organization: orgName,
@@ -1240,7 +1225,7 @@ function PlaceHolderSign() {
sigingUrl: signPdf sigingUrl: signPdf
}; };
let params = { let params = {
extUserId: owner?.objectId, extUserId: extUserId,
recipient: signerMail[i].Email, recipient: signerMail[i].Email,
subject: replaceVar?.subject subject: replaceVar?.subject
? replaceVar?.subject ? replaceVar?.subject
@@ -1853,10 +1838,6 @@ function PlaceHolderSign() {
setPdfBase64Url(urlDetails.base64); setPdfBase64Url(urlDetails.base64);
}; };
const handleSendDoc = () => { const handleSendDoc = () => {
if (docTitle?.length > maxTitleLength) {
alert(t("title-length-alert"));
return;
}
setIsAttchSignerModal(false); setIsAttchSignerModal(false);
setCheckTourStatus(true); setCheckTourStatus(true);
alertSendEmail(); alertSendEmail();
@@ -1919,10 +1900,6 @@ function PlaceHolderSign() {
return isAllSigner; return isAllSigner;
}; };
const handleCloseAttachSigner = () => { const handleCloseAttachSigner = () => {
if (docTitle?.length > maxTitleLength) {
alert(t("title-length-alert"));
return;
}
setIsAttchSignerModal(false); setIsAttchSignerModal(false);
}; };
return ( return (
@@ -2116,9 +2093,7 @@ function PlaceHolderSign() {
<LottieWithLoader /> <LottieWithLoader />
{pdfDetails[0].SendinOrder ? ( {pdfDetails[0].SendinOrder ? (
<p> <p>
{isCurrUser {t("placeholder-mail-alert", {
? t("placeholder-mail-alert-you")
: t("placeholder-mail-alert", {
name: signersdata[0]?.Name name: signersdata[0]?.Name
})} })}
</p> </p>
@@ -2197,45 +2172,24 @@ function PlaceHolderSign() {
</ModalUi> </ModalUi>
<ModalUi <ModalUi
isOpen={isAttchSignerModal} isOpen={isAttchSignerModal}
title={t("create-document")} title={t("add-signer")}
handleClose={() => handleCloseAttachSigner()} handleClose={() => handleCloseAttachSigner()}
> >
<div className="h-[100%] px-[20px] py-[10px]"> <div className="h-[100%] p-[20px]">
<div>
<label
htmlFor="doctitle"
className="block text-xs font-semibold"
>
Title
</label>
<input
type="text"
name="doctitle"
value={docTitle}
onChange={(e) => setDocTitle(e.target.value)}
required
onInvalid={(e) =>
e.target.setCustomValidity(t("input-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-[11px] py-[18px]"
/>
</div>
{pdfDetails[0].Placeholders?.some( {pdfDetails[0].Placeholders?.some(
(x) => !x.signerObjId (x) => !x.signerObjId
) && ( ) && (
<> <>
{/* grid grid-cols-1 md:grid-cols-2 */}
<div className="min-h-max max-h-[250px] overflow-y-auto"> <div className="min-h-max max-h-[250px] overflow-y-auto">
<div className="py-2 text-base-content flex flex-col gap-2 relative"> <div className="py-3 px-[10px] op-card border-[1px] border-gray-400 mt-3 md:mx-3 mb-4 bg-base-200 text-base-content flex flex-col gap-2 relative">
{forms?.map((field, id) => { {forms?.map((field, id) => {
return ( return (
<div <div
className="flex flex-col" className="flex flex-col"
key={field?.value} key={field?.value}
> >
<label className="block text-xs font-semibold"> <label>{field?.role}</label>
{field?.role}
</label>
<div className="flex justify-between items-center gap-1"> <div className="flex justify-between items-center gap-1">
<div className="flex-1"> <div className="flex-1">
<AsyncSelect <AsyncSelect
@@ -2288,8 +2242,8 @@ function PlaceHolderSign() {
})} })}
</div> </div>
</div> </div>
<div className="w-full h-[0.5px] bg-[#9f9f9f] mt-[8px] mb-[15px]"></div> <div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
<div className="flex mx-2 mb-2 gap-3"> <div className="flex mx-4 mb-4 gap-3">
<button <button
disabled={handleDisable()} disabled={handleDisable()}
onClick={() => handleSendDoc()} onClick={() => handleSendDoc()}
+27 -55
View File
@@ -20,7 +20,6 @@ import EditorToolbar, {
module2, module2,
formats formats
} from "../components/pdf/EditorToolbar"; } from "../components/pdf/EditorToolbar";
import DateFormatSelector from "../components/shared/fields/DateFormatSelector";
const Preferences = () => { const Preferences = () => {
const appName = "OpenSign™"; const appName = "OpenSign™";
@@ -48,8 +47,6 @@ const Preferences = () => {
const [tab, setTab] = useState([generaltab]); const [tab, setTab] = useState([generaltab]);
const [sendinOrder, setSendinOrder] = useState(true); const [sendinOrder, setSendinOrder] = useState(true);
const [isTourEnabled, setIsTourEnabled] = useState(false); const [isTourEnabled, setIsTourEnabled] = useState(false);
const [dateFormat, setDateFormat] = useState("MM/DD/YYYY");
const [is12HourTime, setIs12HourTime] = useState(false);
useEffect(() => { useEffect(() => {
fetchSignType(); fetchSignType();
@@ -102,18 +99,10 @@ const Preferences = () => {
? _getUser?.IsTourEnabled ? _getUser?.IsTourEnabled
: true; : true;
setIsTourEnabled(istourenabled); setIsTourEnabled(istourenabled);
const DateFormat =
_getUser?.DateFormat !== undefined
? _getUser?.DateFormat
: "MM/DD/YYYY";
setDateFormat(DateFormat);
const is12Hr =
_getUser?.Is12HourTime !== undefined ? _getUser?.Is12HourTime : false;
setIs12HourTime(is12Hr);
} }
} catch (err) { } catch (err) {
console.log("err while getting user details", err); console.log("err while getting user details", err);
setErrMsg(t("something-went-wrong-mssg")); setErrMsg("Something went wrong");
} finally { } finally {
setIsTopLoader(false); setIsTopLoader(false);
} }
@@ -165,13 +154,11 @@ const Preferences = () => {
params = { params = {
...params, ...params,
SendinOrder: sendinOrder, SendinOrder: sendinOrder,
IsTourEnabled: isTourEnabled, IsTourEnabled: isTourEnabled
DateFormat: dateFormat,
Is12HourTime: is12HourTime
}; };
const updateRes = await Parse.Cloud.run("updatepreferences", params); const updateRes = await Parse.Cloud.run("updatepreferences", params);
if (updateRes) { if (updateRes) {
setIsAlert({ type: "success", msg: t("saved-successfully") }); setIsAlert({ type: "success", msg: "Saved successfully." });
let extUser = let extUser =
localStorage.getItem("Extand_Class") && localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
@@ -179,8 +166,6 @@ const Preferences = () => {
extUser.NotifyOnSignatures = isNotifyOnSignatures; extUser.NotifyOnSignatures = isNotifyOnSignatures;
extUser.SendinOrder = sendinOrder; extUser.SendinOrder = sendinOrder;
extUser.IsTourEnabled = isTourEnabled; extUser.IsTourEnabled = isTourEnabled;
extUser.DateFormat = dateFormat;
extUser.Is12HourTime = is12HourTime;
const _extUser = JSON.parse(JSON.stringify(extUser)); const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("Extand_Class", JSON.stringify([_extUser])); localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
} }
@@ -244,23 +229,21 @@ const Preferences = () => {
setIsLoader(true); setIsLoader(true);
const replacedHtmlBody = completionBody.replace(/"/g, "'"); const replacedHtmlBody = completionBody.replace(/"/g, "'");
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`; const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
const updateTenant = await Parse.Cloud.run("updatetenant", { const tenantQuery = new Parse.Query("partners_Tenant");
tenantId: tenantId, const updateTenantObj = await tenantQuery.get(tenantId);
details: { updateTenantObj.set("CompletionBody", htmlBody);
CompletionBody: htmlBody, updateTenantObj.set("CompletionSubject", completionsubject);
CompletionSubject: completionsubject const res = await updateTenantObj.save();
} if (res) {
}); const updateRes = JSON.parse(JSON.stringify(res));
if (updateTenant) {
const updateRes = JSON.parse(JSON.stringify(updateTenant));
SetCompletionBody(updateRes?.CompletionBody); SetCompletionBody(updateRes?.CompletionBody);
setCompletionSubject(updateRes?.CompletionSubject); setCompletionSubject(updateRes?.CompletionSubject);
setIsAlert({ type: "success", msg: t("saved-successfully") }); setIsAlert({ type: "success", msg: "Saved successfully." });
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500); setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
} }
} catch (err) { } catch (err) {
console.log("Err", err); console.log("Err", err);
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") }); setIsAlert({ type: "danger", msg: "Something went wrong." });
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500); setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
} finally { } finally {
setIsLoader(false); setIsLoader(false);
@@ -273,23 +256,21 @@ const Preferences = () => {
setIsLoader(true); setIsLoader(true);
const replacedHtmlBody = requestBody.replace(/"/g, "'"); const replacedHtmlBody = requestBody.replace(/"/g, "'");
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`; const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
const updateTenant = await Parse.Cloud.run("updatetenant", { const tenantQuery = new Parse.Query("partners_Tenant");
tenantId: tenantId, const updateTenantObj = await tenantQuery.get(tenantId);
details: { updateTenantObj.set("RequestBody", htmlBody);
RequestBody: htmlBody, updateTenantObj.set("RequestSubject", requestSubject);
RequestSubject: requestSubject const res = await updateTenantObj.save();
} if (res) {
}); const updateRes = JSON.parse(JSON.stringify(res));
if (updateTenant) {
const updateRes = JSON.parse(JSON.stringify(updateTenant));
setRequestBody(updateRes?.RequestBody); setRequestBody(updateRes?.RequestBody);
setRequestSubject(updateRes?.RequestSubject); setRequestSubject(updateRes?.RequestSubject);
setIsAlert({ type: "success", msg: t("saved-successfully") }); setIsAlert({ type: "success", msg: "Saved successfully." });
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500); setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
} }
} catch (err) { } catch (err) {
console.log("Err", err); console.log("Err", err);
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") }); setIsAlert({ type: "danger", msg: "Something went wrong." });
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500); setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
} finally { } finally {
setIsLoader(false); setIsLoader(false);
@@ -395,7 +376,7 @@ const Preferences = () => {
{t("allowed-signature-types")} {t("allowed-signature-types")}
</p> </p>
<p>{t("allowed-signature-types-help.p1")}</p> <p>{t("allowed-signature-types-help.p1")}</p>
<div className="p-[5px] ml-2"> <p className="p-[5px] ml-2">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold">Draw: </span> <span className="font-bold">Draw: </span>
@@ -422,7 +403,7 @@ const Preferences = () => {
</span> </span>
</li> </li>
</ol> </ol>
</div> </p>
</div> </div>
</ReactTooltip> </ReactTooltip>
</label> </label>
@@ -500,15 +481,6 @@ const Preferences = () => {
setTimezone={setTimezone} setTimezone={setTimezone}
/> />
</div> </div>
<div className="mb-[0.75rem]">
<DateFormatSelector
timezone={timezone}
dateFormat={dateFormat}
is12HourTime={is12HourTime}
setIs12HourTime={setIs12HourTime}
setDateFormat={setDateFormat}
/>
</div>
<div className="mb-[0.75rem] text-[12px]"> <div className="mb-[0.75rem] text-[12px]">
<label className="block mb-[0.7rem]"> <label className="block mb-[0.7rem]">
<span className="text-[14px] font-medium"> <span className="text-[14px] font-medium">
@@ -529,7 +501,7 @@ const Preferences = () => {
<div className="max-w-[200px] md:max-w-[450px]"> <div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("send-in-order")}</p> <p className="font-bold">{t("send-in-order")}</p>
<p>{t("send-in-order-help.p1")}</p> <p>{t("send-in-order-help.p1")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold"> <span className="font-bold">
@@ -542,7 +514,7 @@ const Preferences = () => {
<span>{t("send-in-order-help.p3")}</span> <span>{t("send-in-order-help.p3")}</span>
</li> </li>
</ol> </ol>
</div> </p>
<p>{t("send-in-order-help.p4")}</p> <p>{t("send-in-order-help.p4")}</p>
</div> </div>
</ReactTooltip> </ReactTooltip>
@@ -591,7 +563,7 @@ const Preferences = () => {
> >
<div className="max-w-[200px] md:max-w-[450px]"> <div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("enable-tour")}</p> <p className="font-bold">{t("enable-tour")}</p>
<div className="p-[5px]"> <p className="p-[5px]">
<ol className="list-disc"> <ol className="list-disc">
<li> <li>
<span className="font-bold"> <span className="font-bold">
@@ -604,7 +576,7 @@ const Preferences = () => {
<span>{t("istourenabled-help.p2")}</span> <span>{t("istourenabled-help.p2")}</span>
</li> </li>
</ol> </ol>
</div> </p>
<p> <p>
{t("istourenabled-help.p3", { {t("istourenabled-help.p3", {
appName: appName appName: appName
+54 -13
View File
@@ -40,8 +40,7 @@ import {
convertBase64ToFile, convertBase64ToFile,
generatePdfName, generatePdfName,
handleRemoveWidgets, handleRemoveWidgets,
compressedFileSize, compressedFileSize
addWidgetSelfsignOptions
} from "../constant/Utils"; } from "../constant/Utils";
import { useParams } from "react-router"; import { useParams } from "react-router";
import Tour from "reactour"; import Tour from "reactour";
@@ -145,7 +144,6 @@ function SignYourSelf() {
isVisible: false, isVisible: false,
signId: "" signId: ""
}); });
const [owner, setOwner] = useState({});
const [, drop] = useDrop({ const [, drop] = useDrop({
accept: "BOX", accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor), drop: (item, monitor) => addPositionOfSignature(item, monitor),
@@ -206,7 +204,6 @@ function SignYourSelf() {
const documentData = await contractDocument(documentId); const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
setOwner(documentData?.[0]?.ExtUserPtr);
setPdfDetails(documentData); setPdfDetails(documentData);
const placeholders = const placeholders =
documentData[0]?.Placeholders?.length > 0 documentData[0]?.Placeholders?.length > 0
@@ -252,11 +249,7 @@ function SignYourSelf() {
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
) { ) {
if (documentData?.result?.error?.includes("deleted")) {
setHandleError(t("document-deleted"));
} else {
setHandleError(t("something-went-wrong-mssg")); setHandleError(t("something-went-wrong-mssg"));
}
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} else { } else {
setHandleError(t("no-data-avaliable")); setHandleError(t("no-data-avaliable"));
@@ -328,7 +321,7 @@ function SignYourSelf() {
} }
} catch (err) { } catch (err) {
console.log("Error: error in getDocumentDetails", err); console.log("Error: error in getDocumentDetails", err);
setHandleError(t("something-went-wrong-mssg")); setHandleError("Error: Something went wrong!");
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} }
}; };
@@ -351,6 +344,54 @@ function SignYourSelf() {
} }
}; };
const addWidgetOptions = (type) => {
switch (type) {
case "signature":
return { name: "signature" };
case "stamp":
return { name: "stamp" };
case "checkbox":
return { name: "checkbox" };
case textWidget:
return { name: "text" };
case "initials":
return { name: "initials" };
case "name":
return {
name: "name",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
case "company":
return {
name: "company",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
case "job title":
return {
name: "job title",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
case "date":
return {
name: "date",
response: getDate(),
validation: { format: "MM/dd/yyyy", type: "date-format" }
};
case "image":
return { name: "image" };
case "email":
return {
name: "email",
defaultValue: getWidgetValue(type),
validation: { type: "email", pattern: "" }
};
default:
return {};
}
};
//function for setting position after drop signature button over pdf //function for setting position after drop signature button over pdf
const addPositionOfSignature = (item, monitor) => { const addPositionOfSignature = (item, monitor) => {
setCurrWidgetsDetails({}); setCurrWidgetsDetails({});
@@ -375,7 +416,7 @@ function SignYourSelf() {
// `getBoundingClientRect()` is used to get accurate measurement height of the div // `getBoundingClientRect()` is used to get accurate measurement height of the div
const divHeight = divRef.current.getBoundingClientRect().height; const divHeight = divRef.current.getBoundingClientRect().height;
const getWidth = widgetTypeExist const getWidth = widgetTypeExist
? calculateInitialWidthHeight(widgetValue).getWidth ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
: defaultWidthHeight(dragTypeValue).width; : defaultWidthHeight(dragTypeValue).width;
const getHeight = defaultWidthHeight(dragTypeValue).height; const getHeight = defaultWidthHeight(dragTypeValue).height;
@@ -389,7 +430,7 @@ function SignYourSelf() {
scale: containerScale, scale: containerScale,
Width: getWidth, Width: getWidth,
Height: getHeight, Height: getHeight,
options: addWidgetSelfsignOptions(dragTypeValue, getWidgetValue, owner) options: addWidgetOptions(dragTypeValue)
}; };
dropData.push(dropObj); dropData.push(dropObj);
} else { } else {
@@ -418,7 +459,7 @@ function SignYourSelf() {
type: dragTypeValue, type: dragTypeValue,
Width: getWidth / (containerScale * scale), Width: getWidth / (containerScale * scale),
Height: getHeight / (containerScale * scale), Height: getHeight / (containerScale * scale),
options: addWidgetSelfsignOptions(dragTypeValue, getWidgetValue, owner), options: addWidgetOptions(dragTypeValue),
scale: containerScale scale: containerScale
}; };
dropData.push(dropObj); dropData.push(dropObj);
@@ -1212,7 +1253,7 @@ function SignYourSelf() {
setPdfBase64Url={setPdfBase64Url} setPdfBase64Url={setPdfBase64Url}
setIsUploadPdf={setIsUploadPdf} setIsUploadPdf={setIsUploadPdf}
pdfArrayBuffer={pdfArrayBuffer} pdfArrayBuffer={pdfArrayBuffer}
isMergePdfBtn={!pdfDetails?.[0]?.IsCompleted} isMergePdfBtn={true}
/> />
<div className=" w-full md:w-[57%] flex mr-4"> <div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom <PdfZoom
@@ -139,7 +139,6 @@ const TemplatePlaceholder = () => {
const [updatedPdfUrl, setUpdatedPdfUrl] = useState(""); const [updatedPdfUrl, setUpdatedPdfUrl] = useState("");
const [tempSignerId, setTempSignerId] = useState(""); const [tempSignerId, setTempSignerId] = useState("");
const [unSignedWidgetId, setUnSignedWidgetId] = useState(""); const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
const [owner, setOwner] = useState({});
useEffect(() => { useEffect(() => {
fetchTemplate(); fetchTemplate();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -215,7 +214,6 @@ const TemplatePlaceholder = () => {
: []; : [];
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
setOwner(documentData?.[0]?.ExtUserPtr);
const url = documentData[0] && documentData[0]?.URL; const url = documentData[0] && documentData[0]?.URL;
if (url) { if (url) {
const arrayBuffer = await convertPdfArrayBuffer(url); const arrayBuffer = await convertPdfArrayBuffer(url);
@@ -284,14 +282,14 @@ const TemplatePlaceholder = () => {
const updatedSigners = documentData[0].Signers.map((x, index) => ({ const updatedSigners = documentData[0].Signers.map((x, index) => ({
...x, ...x,
Id: randomId(), Id: randomId(),
Role: "Role " + (index + 1) Role: "User " + (index + 1)
})); }));
setSignersData(updatedSigners); setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id); setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor); setBlockColor(updatedSigners[0].blockColor);
} }
} else { } else {
setRoleName("Role 1"); setRoleName("User 1");
if ( if (
documentData[0].Placeholders && documentData[0].Placeholders &&
documentData[0].Placeholders.length > 0 documentData[0].Placeholders.length > 0
@@ -393,7 +391,7 @@ const TemplatePlaceholder = () => {
scale: containerScale, scale: containerScale,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -423,7 +421,7 @@ const TemplatePlaceholder = () => {
// isMobile: isMobile, // isMobile: isMobile,
zIndex: posZIndex, zIndex: posZIndex,
type: item.text, type: item.text,
options: addWidgetOptions(dragTypeValue, owner), options: addWidgetOptions(dragTypeValue),
Width: widgetWidth / (containerScale * scale), Width: widgetWidth / (containerScale * scale),
Height: widgetHeight / (containerScale * scale) Height: widgetHeight / (containerScale * scale)
}; };
@@ -1101,7 +1099,7 @@ const TemplatePlaceholder = () => {
const Id = randomId(); const Id = randomId();
const index = signersdata.length; const index = signersdata.length;
const obj = { const obj = {
Role: roleName || "Role " + count, Role: roleName || "User " + count,
Id: Id, Id: Id,
blockColor: color[index] blockColor: color[index]
}; };
@@ -1110,7 +1108,7 @@ const TemplatePlaceholder = () => {
signerPtr: {}, signerPtr: {},
signerObjId: "", signerObjId: "",
blockColor: color[index], blockColor: color[index],
Role: roleName || "Role " + count, Role: roleName || "User " + count,
Id: Id Id: Id
}; };
@@ -7,7 +7,8 @@ import Title from "../components/Title";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import { emailRegex } from "../constant/const";
const UpdateExistUserAdmin = () => { const UpdateExistUserAdmin = () => {
const appName = "OpenSign™"; const appName =
"OpenSign™";
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const [formdata, setFormdata] = useState({ email: "", masterkey: "" }); const [formdata, setFormdata] = useState({ email: "", masterkey: "" });
@@ -29,7 +30,7 @@ const UpdateExistUserAdmin = () => {
} }
} catch (err) { } catch (err) {
console.log("Err in checkadminexist", err); console.log("Err in checkadminexist", err);
setErrMsg(t("something-went-wrong-mssg")); setErrMsg("Something went wrong.");
} finally { } finally {
setLoader(false); setLoader(false);
} }
+11 -5
View File
@@ -1,4 +1,7 @@
import React, { useState, useEffect } from "react"; import React, {
useState,
useEffect,
} from "react";
import { Navigate, useNavigate } from "react-router"; import { Navigate, useNavigate } from "react-router";
import Parse from "parse"; import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
@@ -7,7 +10,10 @@ import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName"; import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios"; import axios from "axios";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
import { getSecureUrl, handleSendOTP } from "../constant/Utils"; import {
getSecureUrl,
handleSendOTP,
} from "../constant/Utils";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -115,13 +121,13 @@ function UserProfile() {
const updateExtUser = async (obj) => { const updateExtUser = async (obj) => {
try { try {
const extData = JSON.parse(localStorage.getItem("Extand_Class")); const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData?.[0]?.objectId; const ExtUserId = extData[0].objectId;
const body = { const body = {
Phone: obj?.Phone || "", Phone: obj?.Phone || "",
Name: obj.Name, Name: obj.Name,
JobTitle: jobTitle, JobTitle: jobTitle,
Company: company, Company: company,
Language: obj?.language || "" Language: obj?.language || "",
}; };
await axios.put( await axios.put(
@@ -229,7 +235,7 @@ function UserProfile() {
const handleResend = async (e) => { const handleResend = async (e) => {
e.preventDefault(); e.preventDefault();
setOtpLoader(true); setOtpLoader(true);
await handleSendOTP(Parse.User.current().getEmail()); await handleSendOTP();
setOtpLoader(false); setOtpLoader(false);
alert(t("otp-sent-alert")); alert(t("otp-sent-alert"));
}; };
+7 -7
View File
@@ -33,9 +33,8 @@ const AddContact = (props) => {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/isuserincontactbook`; const url = `${baseURL}functions/isuserincontactbook`;
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"),
@@ -64,13 +63,16 @@ const AddContact = (props) => {
) )
); );
const userId = user?.objectId || ""; const userId = user?.objectId || "";
const tenantDetails = await getTenantDetails(userId); const tenantDetails = await getTenantDetails(
userId,
);
const tenantId = tenantDetails?.objectId || ""; const tenantId = tenantDetails?.objectId || "";
if (tenantId) { if (tenantId) {
try { try {
const baseURL = localStorage.getItem("baseUrl"); const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/savecontact`; const url = `${baseURL}functions/savecontact`;
const token = { const token =
{
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
}; };
const data = { name, email, phone, tenantId }; const data = { name, email, phone, tenantId };
@@ -135,9 +137,7 @@ const AddContact = (props) => {
</div> </div>
)} )}
<div className="w-full mx-auto p-[8px]"> <div className="w-full mx-auto p-[8px]">
{!props?.isDisableTitle && (
<div className="text-[14px] font-[700]">{t("add-contact")}</div> <div className="text-[14px] font-[700]">{t("add-contact")}</div>
)}
{isUserExist && ( {isUserExist && (
<div className="mb-[0.75rem] flex items-center mt-1"> <div className="mb-[0.75rem] flex items-center mt-1">
<input <input
+1 -1
View File
@@ -11,7 +11,7 @@ const Alert = ({ children, type, className }) => {
case "danger": case "danger":
return "op-alert-error"; return "op-alert-error";
case "warning": case "warning":
return "op-alert-warning text-black"; return "op-alert-warning";
default: default:
return ""; return "";
} }
@@ -1,34 +0,0 @@
import React from "react";
const CheckCircle = ({ size = 56, color = "text-green-500" }) => {
return (
<div className={`flex items-center justify-center ${color}`}>
<svg
xmlns="http://www.w3.org/2000/svg"
className={`w-${size / 4} h-${size / 4}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="2"
fill="none"
/>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M9 12l2 2l4-4"
/>
</svg>
</div>
);
};
export default CheckCircle;
+213 -257
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import pad from "../assets/images/pad.svg"; import pad from "../assets/images/pad.svg";
import { Link, useLocation, useNavigate } from "react-router"; 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";
@@ -14,7 +14,7 @@ import {
copytoData, copytoData,
fetchUrl, fetchUrl,
formatDate, formatDate,
formatDateTime, formatTimeInTimezone,
getSignedUrl, getSignedUrl,
getTenantDetails, getTenantDetails,
handleSignatureType, handleSignatureType,
@@ -46,6 +46,7 @@ const ReportTable = (props) => {
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [actLoader, setActLoader] = useState({}); const [actLoader, setActLoader] = useState({});
const [isAlert, setIsAlert] = useState(false);
const [isContactform, setIsContactform] = useState(false); const [isContactform, setIsContactform] = useState(false);
const [isDeleteModal, setIsDeleteModal] = useState({}); const [isDeleteModal, setIsDeleteModal] = useState({});
const [isRevoke, setIsRevoke] = useState({}); const [isRevoke, setIsRevoke] = useState({});
@@ -80,9 +81,6 @@ const ReportTable = (props) => {
const [invalidRecords, setInvalidRecords] = useState(0); const [invalidRecords, setInvalidRecords] = useState(0);
const [renameDoc, setRenameDoc] = useState(""); const [renameDoc, setRenameDoc] = useState("");
const [contact, setContact] = useState({ Name: "", Email: "", Phone: "" }); const [contact, setContact] = useState({ Name: "", Email: "", Phone: "" });
const [isSuccess, setIsSuccess] = useState({});
const [templateId, setTemplateId] = useState("");
const isTemplateReport = props.ReportName === "Templates";
const recordsPerPage = 5; const recordsPerPage = 5;
const startIndex = (currentPage - 1) * props.docPerPage; const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props; const { isMoreDocs, setIsNextRecord } = props;
@@ -138,10 +136,6 @@ const ReportTable = (props) => {
return pages; return pages;
}; };
const showAlert = (type, message, time = 1500) => {
setAlertMsg({ type: type, message: message });
setTimeout(() => setAlertMsg({ type: "", message: "" }), time);
};
const pageNumbers = getPaginationRange(); const pageNumbers = getPaginationRange();
// below useEffect reset currenpage to 1 if user change route // below useEffect reset currenpage to 1 if user change route
useEffect(() => { useEffect(() => {
@@ -153,7 +147,7 @@ const ReportTable = (props) => {
// `fetchTeamList` is used to fetch team list for share with functionality // `fetchTeamList` is used to fetch team list for share with functionality
const fetchTeamList = async () => { const fetchTeamList = async () => {
if (isTemplateReport) { if (props.ReportName === "Templates") {
try { try {
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser?.OrganizationId?.objectId) { if (extUser?.OrganizationId?.objectId) {
@@ -226,21 +220,13 @@ const ReportTable = (props) => {
// `handleURL` is used to open microapp // `handleURL` is used to open microapp
const handleURL = async (item, act) => { const handleURL = async (item, act) => {
if (isTemplateReport) { 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 });
handleUseTemplate(item.objectId, act.redirectUrl);
}
} else {
navigate(`/${act.redirectUrl}?docId=${item?.objectId}`);
}
};
const handleUseTemplate = async (templateId, redirectUrl) => {
try { try {
const params = { templateId: templateId }; const params = { templateId: item.objectId };
const templateDeatils = await axios.post( const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`, `${localStorage.getItem("baseUrl")}functions/getTemplate`,
params, params,
@@ -252,7 +238,8 @@ const ReportTable = (props) => {
} }
} }
); );
const templateData = templateDeatils.data && templateDeatils.data.result; const templateData =
templateDeatils.data && templateDeatils.data.result;
if (!templateData.error) { if (!templateData.error) {
const Doc = templateData; const Doc = templateData;
@@ -260,11 +247,12 @@ const ReportTable = (props) => {
if (Doc.Signers?.length > 0) { if (Doc.Signers?.length > 0) {
Doc.Signers?.forEach((x) => { Doc.Signers?.forEach((x) => {
if (x.objectId) { if (x.objectId) {
signers.push({ const obj = {
__type: "Pointer", __type: "Pointer",
className: "contracts_Contactbook", className: "contracts_Contactbook",
objectId: x.objectId objectId: x.objectId
}); };
signers.push(obj);
} }
}); });
} }
@@ -299,15 +287,6 @@ const ReportTable = (props) => {
const RedirectUrl = Doc?.RedirectUrl const RedirectUrl = Doc?.RedirectUrl
? { RedirectUrl: Doc?.RedirectUrl } ? { RedirectUrl: Doc?.RedirectUrl }
: {}; : {};
const TemplateId = Doc?.objectId
? {
TemplateId: {
__type: "Pointer",
className: "contracts_Template",
objectId: Doc?.objectId
}
}
: {};
let placeholdersArr = []; let placeholdersArr = [];
if (Doc.Placeholders?.length > 0) { if (Doc.Placeholders?.length > 0) {
placeholdersArr = Doc.Placeholders; placeholdersArr = Doc.Placeholders;
@@ -339,45 +318,71 @@ const ReportTable = (props) => {
...SignatureType, ...SignatureType,
...NotifyOnSignatures, ...NotifyOnSignatures,
...Bcc, ...Bcc,
...RedirectUrl, ...RedirectUrl
...TemplateId
}; };
try { try {
const res = await axios.post( const res = await axios.post(
`${localStorage.getItem("baseUrl")}classes/contracts_Document`, `${localStorage.getItem(
"baseUrl"
)}classes/contracts_Document`,
data, data,
{ {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id":
"X-Parse-Session-Token": localStorage.getItem("accesstoken") localStorage.getItem("parseAppId"),
"X-Parse-Session-Token":
localStorage.getItem("accesstoken")
} }
} }
); );
if (res.data && res.data.objectId) { if (res.data && res.data.objectId) {
setActLoader({}); setActLoader({});
navigate(`/${redirectUrl}/${res.data.objectId}`, { setIsAlert(true);
setTimeout(() => setIsAlert(false), 1500);
navigate(`/${act.redirectUrl}/${res.data.objectId}`, {
state: { title: "Use Template" } state: { title: "Use Template" }
}); });
} }
} catch (err) { } catch (err) {
console.log("Err", err); console.log("Err", err);
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
setActLoader({}); setActLoader({});
} }
} else { } else {
setActLoader({}); setActLoader({});
} }
} else { } else {
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
setActLoader({}); setActLoader({});
} }
} catch (err) { } catch (err) {
console.log("err", err); console.log("err", err);
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
setActLoader({}); setActLoader({});
} }
}
} else {
navigate(`/${act.redirectUrl}?docId=${item?.objectId}`);
}
}; };
const handleActionBtn = async (act, item) => { const handleActionBtn = async (act, item) => {
if (act.action === "redirect") { if (act.action === "redirect") {
handleURL(item, act); handleURL(item, act);
@@ -410,8 +415,6 @@ const ReportTable = (props) => {
} else if (act.action === "edit") { } else if (act.action === "edit") {
setContact(item); setContact(item);
setIsModal({ [`edit_${item.objectId}`]: true }); setIsModal({ [`edit_${item.objectId}`]: true });
} else if (act.action === "saveastemplate") {
setIsModal({ [`saveastemplate_${item.objectId}`]: true });
} else if (act.action) { } else if (act.action) {
setIsModal({ [`extendexpiry_${item.objectId}`]: true }); setIsModal({ [`extendexpiry_${item.objectId}`]: true });
} }
@@ -467,7 +470,12 @@ const ReportTable = (props) => {
}); });
if (res.data && res.data.updatedAt) { if (res.data && res.data.updatedAt) {
setActLoader({}); setActLoader({});
showAlert("success", t("record-delete-alert")); setIsAlert(true);
setAlertMsg({
type: "success",
message: t("record-delete-alert")
});
setTimeout(() => setIsAlert(false), 1500);
const upldatedList = props.List.filter( const upldatedList = props.List.filter(
(x) => x.objectId !== item.objectId (x) => x.objectId !== item.objectId
); );
@@ -475,7 +483,12 @@ const ReportTable = (props) => {
} }
} catch (err) { } catch (err) {
console.log("err", err); console.log("err", err);
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
setActLoader({}); setActLoader({});
} }
}; };
@@ -550,7 +563,12 @@ const ReportTable = (props) => {
const res = result.data; const res = result.data;
if (res) { if (res) {
setActLoader({}); setActLoader({});
showAlert("success", t("record-revoke-alert")); setIsAlert(true);
setAlertMsg({
type: "success",
message: t("record-revoke-alert")
});
setTimeout(() => setIsAlert(false), 1500);
const upldatedList = props.List.filter( const upldatedList = props.List.filter(
(x) => x.objectId !== item.objectId (x) => x.objectId !== item.objectId
); );
@@ -561,7 +579,12 @@ const ReportTable = (props) => {
.catch((err) => { .catch((err) => {
console.log("err", err); console.log("err", err);
setReason(""); setReason("");
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
setActLoader({}); setActLoader({});
}); });
}; };
@@ -655,7 +678,6 @@ 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,
note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email, sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
@@ -667,6 +689,7 @@ const ReportTable = (props) => {
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>` signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
}; };
const res = replaceMailVaribles(subject, "", variables); const res = replaceMailVaribles(subject, "", variables);
setMail((prev) => ({ ...prev, subject: res.subject })); setMail((prev) => ({ ...prev, subject: res.subject }));
}; };
// `handlebodyChange` is used to add or change body of resend mail // `handlebodyChange` is used to add or change body of resend mail
@@ -684,7 +707,6 @@ 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,
note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email, sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
@@ -726,7 +748,6 @@ 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,
note: doc?.Note || "",
sender_name: doc.ExtUserPtr.Name, sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email, sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "", sender_phone: doc.ExtUserPtr?.Phone || "",
@@ -768,15 +789,25 @@ const ReportTable = (props) => {
try { try {
const res = await axios.post(url, params, { headers: headers }); const res = await axios.post(url, params, { headers: headers });
if (res?.data?.result?.status === "success") { if (res?.data?.result?.status === "success") {
showAlert("success", t("mail-sent-alert")); setIsAlert(true);
setAlertMsg({ type: "success", message: t("mail-sent-alert") });
setIsResendMail({}); setIsResendMail({});
} else { } else {
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
} }
} catch (err) { } catch (err) {
console.log("err in sendmail", err); console.log("err in sendmail", err);
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
} finally { } finally {
setTimeout(() => setIsAlert(false), 1500);
setIsNextStep({}); setIsNextStep({});
setUserDetails({}); setUserDetails({});
setActLoader({}); setActLoader({});
@@ -809,10 +840,27 @@ const ReportTable = (props) => {
// `handleQuickSendClose` is trigger when bulk send component trigger close event // `handleQuickSendClose` is trigger when bulk send component trigger close event
const handleQuickSendClose = (status, count) => { const handleQuickSendClose = (status, count) => {
setIsBulkSend({}); setIsBulkSend({});
setIsAlert(true);
if (status === "success") { if (status === "success") {
showAlert("success", count + " " + t("document-sent-alert")); if (count > 1) {
setAlertMsg({
type: "success",
message: count + " " + t("document-sent-alert")
});
setTimeout(() => setIsAlert(false), 1500);
} else { } else {
showAlert("danger", t("something-went-wrong-mssg")); setAlertMsg({
type: "success",
message: count + " " + t("document-sent-alert")
});
setTimeout(() => setIsAlert(false), 1500);
}
} else {
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setTimeout(() => setIsAlert(false), 1500);
} }
}; };
@@ -851,7 +899,9 @@ const ReportTable = (props) => {
} catch (err) { } catch (err) {
console.log("err in fetch template in bulk modal", err); console.log("err in fetch template in bulk modal", err);
setIsBulkSend({}); setIsBulkSend({});
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({ type: "danger", message: t("something-went-wrong-mssg") });
setTimeout(() => setIsAlert(false), 1500);
} }
}; };
@@ -875,12 +925,21 @@ const ReportTable = (props) => {
templateCls.set("SharedWith", teamArr); templateCls.set("SharedWith", teamArr);
const res = await templateCls.save(); const res = await templateCls.save();
if (res) { if (res) {
showAlert("success", t("template-share-alert")); setIsAlert(true);
setAlertMsg({
type: "success",
message: t("template-share-alert")
});
} }
} catch (err) { } catch (err) {
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
} finally { } finally {
setActLoader({}); setActLoader({});
setTimeout(() => setIsAlert(false), 1500);
} }
}; };
@@ -907,13 +966,13 @@ const ReportTable = (props) => {
} }
}); });
if (res.data && res.data.updatedAt) { if (res.data && res.data.updatedAt) {
showAlert( setIsAlert(true);
"success", setAlertMsg({
t("expiry-date-updated", { type: "success",
message: t("expiry-date-updated", {
newexpirydate: new Date(expiryDate)?.toLocaleDateString() newexpirydate: new Date(expiryDate)?.toLocaleDateString()
}), })
2000 });
);
if (props.ReportName === "Expired Documents") { if (props.ReportName === "Expired Documents") {
const upldatedList = props.List.filter( const upldatedList = props.List.filter(
(x) => x.objectId !== item.objectId (x) => x.objectId !== item.objectId
@@ -923,39 +982,58 @@ const ReportTable = (props) => {
} }
} catch (err) { } catch (err) {
console.log("err", err); console.log("err", err);
showAlert("danger", t("something-went-wrong-mssg"), 2000); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
} finally { } finally {
setActLoader({}); setActLoader({});
setExpiryDate(); setExpiryDate();
setTimeout(() => setIsAlert(false), 2000);
setIsModal({}); setIsModal({});
} }
} else { } else {
showAlert("danger", t("expiry-date-error"), 2000); setIsAlert(true);
setAlertMsg({ type: "danger", message: t("expiry-date-error") });
setTimeout(() => setIsAlert(false), 2000);
} }
} else { } else {
showAlert("danger", t("expiry-date-error"), 2000); setIsAlert(true);
setAlertMsg({ type: "danger", message: t("expiry-date-error") });
setTimeout(() => setIsAlert(false), 2000);
} }
}; };
// `formatStatusRow` is used to format status row // `formatStatusRow` is used to format status row
const formatStatusRow = (item) => { const formatStatusRow = (item) => {
const timezone = extClass?.[0]?.Timezone || "";
const DateFormat = extClass?.[0]?.DateFormat || "MM/DD/YYYY";
const Is12Hr = extClass?.[0]?.Is12HourTime || false;
const signers = item?.Placeholders?.map((x, i) => { const signers = item?.Placeholders?.map((x, i) => {
const audit = item?.AuditTrail?.find( const matchSigner = item?.AuditTrail?.find(
(audit) => audit?.UserPtr?.objectId === x.signerObjId (audit) => audit?.UserPtr?.objectId === x.signerObjId
); );
const format = (date) => if (matchSigner) {
date const timezone = extClass?.[0]?.Timezone || "";
? formatDateTime(new Date(date), DateFormat, timezone, Is12Hr) const signedon = matchSigner?.SignedOn
? formatTimeInTimezone(new Date(matchSigner?.SignedOn), timezone)
: "-";
const viewedon = matchSigner?.ViewedOn
? formatTimeInTimezone(new Date(matchSigner?.ViewedOn), timezone)
: "-"; : "-";
return { return {
id: i, id: i,
Email: x?.signerPtr?.Email || x?.email, Email: matchSigner.UserPtr.Email,
Activity: audit?.Activity?.toUpperCase() || "SENT", Activity: matchSigner?.Activity?.toUpperCase() || "-",
SignedOn: format(audit?.SignedOn), SignedOn: signedon,
ViewedOn: format(audit?.ViewedOn) ViewedOn: viewedon
}; };
} else {
return {
id: i,
Email: x?.signerPtr?.Email || x?.email,
Activity: "SENT",
SignedOn: "-",
ViewedOn: "-"
};
}
}); });
// Decide how many signers to display based on `showAllSignes` state // Decide how many signers to display based on `showAllSignes` state
const displaySigners = isShowAllSigners[item.objectId] const displaySigners = isShowAllSigners[item.objectId]
@@ -985,7 +1063,7 @@ const ReportTable = (props) => {
<ModalUi <ModalUi
isOpen isOpen
title={t("document-logs")} title={t("document-logs")}
handleClose={handleCloseModal} handleClose={() => setIsModal({})}
> >
<div className="pl-3 first:mt-2 border-t-[1px] border-gray-600 text-[12px] py-2"> <div className="pl-3 first:mt-2 border-t-[1px] border-gray-600 text-[12px] py-2">
<p className="font-bold"> {x?.Email}</p> <p className="font-bold"> {x?.Email}</p>
@@ -1199,25 +1277,28 @@ const ReportTable = (props) => {
const contacts = JSON.stringify(filterdata); const contacts = JSON.stringify(filterdata);
const res = await Parse.Cloud.run("createbatchcontact", { contacts }); const res = await Parse.Cloud.run("createbatchcontact", { contacts });
if (res) { if (res) {
showAlert( setIsAlert(true);
"info", setAlertMsg({
t("contact-imported", { type: "info",
message: t("contact-imported", {
imported: res?.success || 0, imported: res?.success || 0,
failed: res?.failed || 0 failed: res?.failed || 0
}) })
); });
if (res?.success > 0) { if (res?.success > 0) {
setTimeout(() => window.location.reload(), 1500); setTimeout(() => window.location.reload(), 2000);
} }
} }
} catch (err) { } catch (err) {
console.log("err while creating batch contact", err); console.log("err while creating batch contact", err);
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({ type: "danger", message: t("something-went-wrong-mssg") });
} finally { } finally {
setActLoader({}); setActLoader({});
setIsModal({}); setIsModal({});
setImportedData([]); setImportedData([]);
setInvalidRecords(0); setInvalidRecords(0);
setTimeout(() => setIsAlert(false), 2000);
} }
}; };
@@ -1232,20 +1313,30 @@ const ReportTable = (props) => {
if (duplicateRes) { if (duplicateRes) {
const newTemplate = JSON.parse(JSON.stringify(duplicateRes)); const newTemplate = JSON.parse(JSON.stringify(duplicateRes));
props.setList((prevData) => [newTemplate, ...prevData]); props.setList((prevData) => [newTemplate, ...prevData]);
showAlert("success", t("duplicate-template-created")); setIsAlert(true);
setAlertMsg({
type: "success",
message: t("duplicate-template-created")
});
} }
} catch (err) { } catch (err) {
showAlert("danger", t("something-went-wrong-mssg")); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
console.log("Err while create duplicate template", err); console.log("Err while create duplicate template", err);
} finally { } finally {
setActLoader({}); setActLoader({});
setTimeout(() => setIsAlert(false), 2000);
} }
}; };
// `handleRenameDoc` is used to update document name // `handleRenameDoc` is used to update document name
const handleRenameDoc = async (item) => { const handleRenameDoc = async (item) => {
setActLoader({ [item.objectId]: true }); setActLoader({ [item.objectId]: true });
setIsModal({}); setIsModal({});
const className = isTemplateReport const className =
props.ReportName === "Templates"
? "contracts_Template" ? "contracts_Template"
: "contracts_Document"; : "contracts_Document";
try { try {
@@ -1258,11 +1349,21 @@ const ReportTable = (props) => {
x.objectId === item.objectId ? { ...x, Name: renameDoc } : x x.objectId === item.objectId ? { ...x, Name: renameDoc } : x
); );
props.setList(updateList); props.setList(updateList);
setIsAlert(true);
setAlertMsg({
type: "success",
message: "Document updated"
});
setActLoader({}); setActLoader({});
showAlert("success", "Document updated", 2000); setTimeout(() => setIsAlert(false), 2000);
} catch (err) { } catch (err) {
showAlert("danger", t("something-went-wrong-mssg"), 2000); setIsAlert(true);
setAlertMsg({
type: "danger",
message: t("something-went-wrong-mssg")
});
setActLoader({}); setActLoader({});
setTimeout(() => setIsAlert(false), 2000);
} }
}; };
const handleBtnVisibility = (act, item) => { const handleBtnVisibility = (act, item) => {
@@ -1286,72 +1387,6 @@ const ReportTable = (props) => {
const handleCloseModal = () => { const handleCloseModal = () => {
setIsModal({}); setIsModal({});
}; };
const handleSaveAsTemplate = async (doc) => {
try {
const params = { docId: doc?.objectId };
const templateRes = await Parse.Cloud.run("saveastemplate", params);
// console.log("templateRes ", templateRes);
setTemplateId(templateRes?.id);
setIsSuccess({ [doc.objectId]: true });
} catch (err) {
console.log("Err in saveastemplate", err);
} finally {
setActLoader({});
}
};
const handleCloseTemplate = () => {
setTemplateId("");
setIsSuccess({});
handleCloseModal();
setActLoader({});
handleClose();
};
// `handleBulkSend` is used to open modal as well as fetch template
// and show Ui on the basis template response
const handleBulkSendTemplate = async (templateId, docId) => {
setIsBulkSend({ [docId]: true });
setIsLoader({ [docId]: true });
try {
const params = {
templateId: templateId,
include: ["Placeholders.signerPtr"]
};
const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
);
const templateRes = axiosRes.data && axiosRes.data.result;
const tenantSignTypes = await fetchTenantDetails();
const docSignTypes = templateRes?.SignatureType || signatureTypes;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
setSignatureType(updatedSignatureType);
setPlaceholders(templateRes?.Placeholders);
setTemplateDetails(templateRes);
setIsLoader({});
} catch (err) {
console.log("err in fetch template in bulk modal", err);
setIsBulkSend({});
showAlert("danger", t("something-went-wrong-mssg"));
}
};
const handleResendClose = () => {
setIsResendMail({});
setIsNextStep({});
setUserDetails({});
};
return ( return (
<div className="relative"> <div className="relative">
{Object.keys(actLoader)?.length > 0 && ( {Object.keys(actLoader)?.length > 0 && (
@@ -1360,10 +1395,8 @@ const ReportTable = (props) => {
</div> </div>
)} )}
<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">
{alertMsg.message && ( {isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
<Alert type={alertMsg.type}>{alertMsg.message}</Alert> {props.tourData && props.ReportName === "Templates" && (
)}
{props.tourData && isTemplateReport && (
<> <>
<Tour <Tour
onRequestClose={closeTour} onRequestClose={closeTour}
@@ -1400,7 +1433,7 @@ const ReportTable = (props) => {
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[35px]"></i> <i className="fa-light fa-square-plus text-accent text-[30px] md:text-[35px]"></i>
</div> </div>
)} )}
{isTemplateReport && ( {props.ReportName === "Templates" && (
<i <i
data-tut="reactourFirst" data-tut="reactourFirst"
onClick={() => navigate("/form/template")} onClick={() => navigate("/form/template")}
@@ -1599,9 +1632,7 @@ const ReportTable = (props) => {
</th> </th>
)} )}
<td className="p-2 min-w-56 max-w-56"> <td className="p-2 min-w-56 max-w-56">
<div className="font-semibold break-words"> <div className="font-semibold">{item?.Name}</div>
{item?.Name}
</div>
{item?.ExpiryDate?.iso && ( {item?.ExpiryDate?.iso && (
<div className="text-gray-500"> <div className="text-gray-500">
Expires {formatDate(item?.ExpiryDate?.iso)} Expires {formatDate(item?.ExpiryDate?.iso)}
@@ -1671,7 +1702,7 @@ const ReportTable = (props) => {
<div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center"> <div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center">
{props.actions?.length > 0 && {props.actions?.length > 0 &&
props.actions.map((act, index) => props.actions.map((act, index) =>
isTemplateReport ? ( props.ReportName === "Templates" ? (
<React.Fragment key={index}> <React.Fragment key={index}>
{(item.ExtUserPtr?.objectId === {(item.ExtUserPtr?.objectId ===
extClass?.[0]?.objectId || extClass?.[0]?.objectId ||
@@ -1738,7 +1769,7 @@ const ReportTable = (props) => {
isOpen={ isOpen={
isModal["duplicate_" + item.objectId] isModal["duplicate_" + item.objectId]
} }
handleClose={handleCloseModal} handleClose={() => setIsModal({})}
> >
<div className=" flex flex-col px-4 pb-3 pt-2 "> <div className=" flex flex-col px-4 pb-3 pt-2 ">
<p className="text-base"> <p className="text-base">
@@ -1755,7 +1786,7 @@ const ReportTable = (props) => {
</button> </button>
<button <button
className="w-[100px] op-btn op-btn-secondary op-btn-md" className="w-[100px] op-btn op-btn-secondary op-btn-md"
onClick={handleCloseModal} onClick={() => setIsModal({})}
> >
{t("no")} {t("no")}
</button> </button>
@@ -1826,93 +1857,12 @@ const ReportTable = (props) => {
) )
)} )}
</div> </div>
{isModal["saveastemplate_" + item.objectId] && (
<ModalUi
isOpen
title={
isSuccess[item.objectId]
? t("template-created")
: t("btnLabel.Save as template")
}
handleClose={handleCloseTemplate}
>
{isSuccess[item.objectId] ? (
<div className="mx-[10px] my-[15px]">
<p className="text-base text-center">
{t("how-would-you-like-to-proceed?")}
</p>
<div className="flex flex-wrap gap-1 items-center justify-center mt-2">
<button
className="op-btn-primary op-btn op-btn-sm focus:outline-none text-sm relative"
onClick={() =>
handleUseTemplate(
templateId,
"placeHolderSign"
)
}
>
<i className="fa-light fa-plus"></i>{" "}
{t("btnLabel.Use")}
</button>
<button
className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative"
onClick={() =>
handleBulkSendTemplate(
templateId,
item.objectId
)
}
>
<i className="fa-light fa-plus"></i>{" "}
{`${t(`btnLabel.Quick send`)}`}
</button>
<button
className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative"
onClick={() =>
navigate(`/template/${templateId}`)
}
>
<i className="fa-light fa-pen"></i>{" "}
{t(`btnLabel.Edit`)}
</button>
</div>
<Link
to="/report/6TeaPr321t"
className="cursor-pointer underline text-sm w-full flex justify-center mt-2"
>
{t("go-to-manage-templates")}
</Link>
</div>
) : (
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
{t("save-as-template-?")}
</div>
<hr className="bg-[#ccc] mt-3" />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleSaveAsTemplate(item)}
className="op-btn op-btn-primary w-[100px]"
>
{t("yes")}
</button>
<button
onClick={handleCloseTemplate}
className="op-btn op-btn-secondary w-[100px]"
>
{t("no")}
</button>
</div>
</div>
)}
</ModalUi>
)}
{isModal["extendexpiry_" + item.objectId] && ( {isModal["extendexpiry_" + item.objectId] && (
<ModalUi <ModalUi
isOpen isOpen
title={t("btnLabel.extend-expiry-date")} title={t("btnLabel.extend-expiry-date")}
reduceWidth={"md:max-w-[450px]"} reduceWidth={"md:max-w-[450px]"}
handleClose={handleCloseModal} handleClose={() => setIsModal({})}
> >
<form <form
className="px-4 py-2 flex flex-col" className="px-4 py-2 flex flex-col"
@@ -1971,12 +1921,14 @@ const ReportTable = (props) => {
{isViewShare[item.objectId] && ( {isViewShare[item.objectId] && (
<ModalUi <ModalUi
isOpen isOpen
showHeader={isTemplateReport} showHeader={
props.ReportName === "Templates" && true
}
title={t("signers")} title={t("signers")}
reduceWidth={"md:max-w-[450px]"} reduceWidth={"md:max-w-[450px]"}
handleClose={() => setIsViewShare({})} handleClose={() => setIsViewShare({})}
> >
{!isTemplateReport && ( {props.ReportName !== "Templates" && (
<div <div
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-1 z-40" className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-1 z-40"
onClick={() => setIsViewShare({})} onClick={() => setIsViewShare({})}
@@ -1987,13 +1939,13 @@ const ReportTable = (props) => {
<table className="op-table w-full overflow-auto"> <table className="op-table w-full overflow-auto">
<thead className="h-[38px] sticky top-0 text-base-content text-sm pt-[15px] px-[20px]"> <thead className="h-[38px] sticky top-0 text-base-content text-sm pt-[15px] px-[20px]">
<tr> <tr>
{isTemplateReport && ( {props.ReportName === "Templates" && (
<th className="p-2 pl-3 w-[30%]"> <th className="p-2 pl-3 w-[30%]">
{t("roles")} {t("roles")}
</th> </th>
)} )}
<th className="pl-3 py-2"> <th className="pl-3 py-2">
{isTemplateReport {props.ReportName === "Templates"
? t("email") ? t("email")
: t("signers")} : t("signers")}
</th> </th>
@@ -2007,7 +1959,7 @@ const ReportTable = (props) => {
key={i} key={i}
className="text-sm font-medium" className="text-sm font-medium"
> >
{isTemplateReport && ( {props.ReportName === "Templates" && (
<td className="text-[12px] p-2 pl-3 w-[30%]"> <td className="text-[12px] p-2 pl-3 w-[30%]">
{x.Role && x.Role} {x.Role && x.Role}
</td> </td>
@@ -2163,7 +2115,11 @@ const ReportTable = (props) => {
<ModalUi <ModalUi
isOpen isOpen
title={t("resend-mail")} title={t("resend-mail")}
handleClose={handleResendClose} handleClose={() => {
setIsResendMail({});
setIsNextStep({});
setUserDetails({});
}}
> >
<div className=" overflow-y-auto max-h-[340px] md:max-h-[400px]"> <div className=" overflow-y-auto max-h-[340px] md:max-h-[400px]">
{item?.Placeholders?.map((user) => ( {item?.Placeholders?.map((user) => (
@@ -2265,7 +2221,7 @@ const ReportTable = (props) => {
<ModalUi <ModalUi
title={t("btnLabel.Rename")} title={t("btnLabel.Rename")}
isOpen={isModal["rename_" + item.objectId]} isOpen={isModal["rename_" + item.objectId]}
handleClose={handleCloseModal} handleClose={() => setIsModal({})}
> >
<div className=" flex flex-col px-4 pb-3 pt-2 "> <div className=" flex flex-col px-4 pb-3 pt-2 ">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@@ -2287,7 +2243,7 @@ const ReportTable = (props) => {
</button> </button>
<button <button
className="w-[100px] op-btn op-btn-secondary op-btn-md" className="w-[100px] op-btn op-btn-secondary op-btn-md"
onClick={handleCloseModal} onClick={() => setIsModal({})}
> >
{t("cancel")} {t("cancel")}
</button> </button>
@@ -1,7 +1,7 @@
function sanitizeFileName(fileName) { function sanitizeFileName(fileName) {
// Remove spaces and invalid characters // Remove spaces and invalid characters
const file = fileName?.replace(/[^a-zA-Z0-9._-]/g, ""); const file = fileName.replace(/[^a-zA-Z0-9._-]/g, "");
const removedot = file?.replace(/\.(?=.*\.)/g, ""); const removedot = file.replace(/\.(?=.*\.)/g, "");
return removedot?.replace(/[^a-zA-Z0-9._-]/g, ""); return removedot.replace(/[^a-zA-Z0-9._-]/g, "");
} }
export default sanitizeFileName; export default sanitizeFileName;
+8 -49
View File
@@ -6,10 +6,11 @@ dotenv.config();
export const cloudServerUrl = 'http://localhost:8080/app'; export const cloudServerUrl = 'http://localhost:8080/app';
export const appName = 'OpenSign™'; export const appName = 'OpenSign™';
export function customAPIurl() {
const url = new URL(cloudServerUrl);
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
}
export const MAX_NAME_LENGTH = 250;
export const MAX_NOTE_LENGTH = 200;
export const MAX_DESCRIPTION_LENGTH = 500;
export const color = [ export const color = [
'#93a3db', '#93a3db',
'#e6c3db', '#e6c3db',
@@ -52,7 +53,7 @@ export const saveFileUsage = async (size, fileUrl, userId) => {
className: '_User', className: '_User',
objectId: userId, objectId: userId,
}); });
const tenant = await tenantQuery.first({ useMasterKey: true }); const tenant = await tenantQuery.first();
if (tenant) { if (tenant) {
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id }; const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
try { try {
@@ -228,7 +229,6 @@ 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 AppName = appName;
const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`; const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`;
const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`; const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
@@ -244,56 +244,15 @@ export const mailTemplate = param => {
param.senderMail + param.senderMail +
"</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'> " + "</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'> " +
param.organization + param.organization +
"</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'>" + "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.localExpireDate + param.localExpireDate +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.note +
"</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" + "</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" +
param.sigingUrl + param.sigingUrl +
"><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;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 " + "><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;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 " +
AppName + appName +
'. For any queries regarding this email, please contact the sender ' + '. For any queries regarding this email, please contact the sender ' +
param.senderMail + param.senderMail +
` directly. If you think this email is inappropriate or spam, you may file a complaint with ${AppName}${opurl}.</p></div></div></body></html>`; ` directly. If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`;
return { subject, body }; return { subject, body };
}; };
export const selectFormat = data => {
switch (data) {
case 'L':
return 'MM/dd/yyyy';
case 'MM/DD/YYYY':
return 'MM/dd/yyyy';
case 'DD-MM-YYYY':
return 'dd-MM-yyyy';
case 'DD/MM/YYYY':
return 'dd/MM/yyyy';
case 'LL':
return 'MMMM dd, yyyy';
case 'DD MMM, YYYY':
return 'dd MMM, yyyy';
case 'YYYY-MM-DD':
return 'yyyy-MM-dd';
case 'MM-DD-YYYY':
return 'MM-dd-yyyy';
case 'MM.DD.YYYY':
return 'MM.dd.yyyy';
case 'MMM DD, YYYY':
return 'MMM dd, yyyy';
case 'MMMM DD, YYYY':
return 'MMMM dd, yyyy';
case 'DD MMMM, YYYY':
return 'dd MMMM, yyyy';
default:
return 'MM/dd/yyyy';
}
};
export function formatDateTime(date, dateFormat, timeZone, is12Hour) {
const zonedDate = toZonedTime(date, timeZone); // Convert date to the given timezone
const timeFormat = is12Hour ? 'hh:mm:ss a' : 'HH:mm:ss';
return dateFormat
? format(zonedDate, `${selectFormat(dateFormat)}, ${timeFormat} 'GMT' XXX`, { timeZone })
: formatTimeInTimezone(date, timeZone);
}
+4 -4
View File
@@ -12,6 +12,7 @@ import getDrive from './parsefunction/getDrive.js';
import getReport from './parsefunction/getReport.js'; import getReport from './parsefunction/getReport.js';
import TemplateAfterSave from './parsefunction/TemplateAfterSave.js'; import TemplateAfterSave from './parsefunction/TemplateAfterSave.js';
import GetTemplate from './parsefunction/GetTemplate.js'; import GetTemplate from './parsefunction/GetTemplate.js';
import callWebhook from './parsefunction/callWebhook.js';
import DocumentBeforesave from './parsefunction/DocumentBeforesave.js'; import DocumentBeforesave from './parsefunction/DocumentBeforesave.js';
import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js'; import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js';
import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js'; import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js';
@@ -20,6 +21,7 @@ import UserAfterFind from './parsefunction/UserAfterFInd.js';
import SignatureAfterFind from './parsefunction/SignatureAfterFind.js'; import SignatureAfterFind from './parsefunction/SignatureAfterFind.js';
import TenantAterFind from './parsefunction/TenantAfterFind.js'; import TenantAterFind from './parsefunction/TenantAfterFind.js';
import VerifyEmail from './parsefunction/VerifyEmail.js'; import VerifyEmail from './parsefunction/VerifyEmail.js';
import encryptedpdf from './parsefunction/encryptedPdf.js';
import { getSignedUrl } from './parsefunction/getSignedUrl.js'; import { getSignedUrl } from './parsefunction/getSignedUrl.js';
import createBatchDocs from './parsefunction/createBatchDocs.js'; import createBatchDocs from './parsefunction/createBatchDocs.js';
import linkContactToDoc from './parsefunction/linkContactToDoc.js'; import linkContactToDoc from './parsefunction/linkContactToDoc.js';
@@ -49,8 +51,6 @@ import fileUpload from './parsefunction/fileUpload.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js'; 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';
import saveAsTemplate from './parsefunction/saveAsTemplate.js';
import updateTenant from './parsefunction/updateTenant.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);
@@ -81,7 +81,9 @@ Parse.Cloud.define('getDocument', getDocument);
Parse.Cloud.define('getDrive', getDrive); Parse.Cloud.define('getDrive', getDrive);
Parse.Cloud.define('getReport', getReport); Parse.Cloud.define('getReport', getReport);
Parse.Cloud.define('getTemplate', GetTemplate); Parse.Cloud.define('getTemplate', GetTemplate);
Parse.Cloud.define('callwebhook', callWebhook);
Parse.Cloud.define('verifyemail', VerifyEmail); Parse.Cloud.define('verifyemail', VerifyEmail);
Parse.Cloud.define('encryptedpdf', encryptedpdf);
Parse.Cloud.define('getsignedurl', getSignedUrl); Parse.Cloud.define('getsignedurl', getSignedUrl);
Parse.Cloud.define('batchdocuments', createBatchDocs); Parse.Cloud.define('batchdocuments', createBatchDocs);
Parse.Cloud.define('linkcontacttodoc', linkContactToDoc); Parse.Cloud.define('linkcontacttodoc', linkContactToDoc);
@@ -110,5 +112,3 @@ Parse.Cloud.define('fileupload', fileUpload);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg); Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('editcontact', editContact); Parse.Cloud.define('editcontact', editContact);
Parse.Cloud.define('forwarddoc', forwardDoc); Parse.Cloud.define('forwarddoc', forwardDoc);
Parse.Cloud.define('saveastemplate', saveAsTemplate);
Parse.Cloud.define('updatetenant', updateTenant);
@@ -61,7 +61,7 @@ async function addTeamAndOrg(extUser) {
async function saveUser(userDetails) { async function saveUser(userDetails) {
const userQuery = new Parse.Query(Parse.User); const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); userQuery.equalTo('username', userDetails.email);
const userRes = await userQuery.first({ useMasterKey: true }); const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) { if (userRes) {
@@ -83,9 +83,9 @@ async function saveUser(userDetails) {
return { id: login.objectId, sessionToken: login.sessionToken }; return { id: login.objectId, sessionToken: login.sessionToken };
} else { } else {
const user = new Parse.User(); const user = new Parse.User();
user.set('username', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); user.set('username', userDetails.email);
user.set('password', userDetails.password); user.set('password', userDetails.password);
user.set('email', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); user.set('email', userDetails.email);
if (userDetails?.phone) { if (userDetails?.phone) {
user.set('phone', userDetails.phone); user.set('phone', userDetails.phone);
} }
@@ -111,6 +111,7 @@ export default async function AddAdmin(request) {
if (extUser) { if (extUser) {
return { message: 'User already exist' }; return { message: 'User already exist' };
} else { } else {
// console.log("role ", role);
const partnerQuery = new Parse.Object('partners_Tenant'); const partnerQuery = new Parse.Object('partners_Tenant');
partnerQuery.set('UserId', { partnerQuery.set('UserId', {
__type: 'Pointer', __type: 'Pointer',
@@ -122,7 +123,7 @@ export default async function AddAdmin(request) {
partnerQuery.set('ContactNumber', userDetails.phone); partnerQuery.set('ContactNumber', userDetails.phone);
} }
partnerQuery.set('TenantName', userDetails.company); partnerQuery.set('TenantName', userDetails.company);
partnerQuery.set('EmailAddress', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true); partnerQuery.set('IsActive', true);
partnerQuery.set('CreatedBy', { partnerQuery.set('CreatedBy', {
__type: 'Pointer', __type: 'Pointer',
@@ -153,7 +154,7 @@ export default async function AddAdmin(request) {
objectId: user.id, objectId: user.id,
}); });
newObj.set('UserRole', userDetails.role); newObj.set('UserRole', userDetails.role);
newObj.set('Email', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); newObj.set('Email', userDetails.email);
newObj.set('Name', userDetails.name); newObj.set('Name', userDetails.name);
if (userDetails?.phone) { if (userDetails?.phone) {
newObj.set('Phone', userDetails?.phone); newObj.set('Phone', userDetails?.phone);
@@ -176,7 +177,7 @@ export default async function AddAdmin(request) {
const extUser = { const extUser = {
objectId: extRes.id, objectId: extRes.id,
Name: userDetails.name, Name: userDetails.name,
Email: userDetails.email?.toLowerCase()?.replace(/\s/g, ''), Email: userDetails.email,
Phone: userDetails?.phone ? userDetails.phone : '', Phone: userDetails?.phone ? userDetails.phone : '',
TenantId: { objectId: tenantRes.id }, TenantId: { objectId: tenantRes.id },
UserId: { objectId: user.id }, UserId: { objectId: user.id },
@@ -1,24 +1,6 @@
import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js';
async function DocumentBeforesave(request) { async function DocumentBeforesave(request) {
if (!request.original) { if (!request.original) {
const validations = [ const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
{ field: 'Name', max: MAX_NAME_LENGTH },
{ field: 'Note', max: MAX_NOTE_LENGTH },
{ field: 'Description', max: MAX_DESCRIPTION_LENGTH },
];
for (const { field, max } of validations) {
const value = request?.object?.get(field);
if (value && value.length > max) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
`The "${field}" field must be at most ${max} characters long.`
);
}
}
const TimeToCompleteDays = request?.object?.get('TimeToCompleteDays') || 15;
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5; const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
const AutoReminder = request?.object?.get('AutomaticReminders') || false; const AutoReminder = request?.object?.get('AutomaticReminders') || false;
const reminderCount = TimeToCompleteDays / RemindOnceInEvery; const reminderCount = TimeToCompleteDays / RemindOnceInEvery;
@@ -54,6 +36,7 @@ async function DocumentBeforesave(request) {
} }
if (document?.get('Signers') && document.get('Signers').length > 0) { if (document?.get('Signers') && document.get('Signers').length > 0) {
document.set('DocSentAt', new Date()); document.set('DocSentAt', new Date());
document.save(null, { useMasterKey: true });
} }
} }
} catch (err) { } catch (err) {
@@ -28,7 +28,6 @@ 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 TenantAppName = appName;
const from = _docRes?.ExtUserPtr?.Email; const from = _docRes?.ExtUserPtr?.Email;
const replyTo = _docRes?.ExtUserPtr?.Email; const replyTo = _docRes?.ExtUserPtr?.Email;
const senderName = _docRes?.ExtUserPtr?.Name; const senderName = _docRes?.ExtUserPtr?.Name;
@@ -52,8 +51,8 @@ export default async function forwardDoc(request) {
`<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-color:white'><div>` + `<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-color:white'><div>` +
`${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` + `${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` +
`<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${docName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` + `<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${docName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` +
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` + `</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` +
`If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`, `If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`,
}; };
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, { mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
headers: { headers: {
@@ -6,17 +6,13 @@ export default async function GetLogoByDomain(request) {
try { try {
const tenantCreditsQuery = new Parse.Query('partners_Tenant'); const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('Domain', domain); tenantCreditsQuery.equalTo('Domain', domain);
const res = await tenantCreditsQuery.first({ useMasterKey: true }); const res = await tenantCreditsQuery.first();
if (res) { if (res) {
const updateRes = JSON.parse(JSON.stringify(res)); const updateRes = JSON.parse(JSON.stringify(res));
return { return { logo: updateRes?.Logo, appname: appName, user: 'exist' };
logo: updateRes?.Logo,
appname: appName,
user: 'exist',
};
} else { } else {
const tenantCreditsQuery = new Parse.Query('partners_Tenant'); const tenantCreditsQuery = new Parse.Query('partners_Tenant');
const tenantRes = await tenantCreditsQuery.first({ useMasterKey: true }); const tenantRes = await tenantCreditsQuery.first();
if (tenantRes) { if (tenantRes) {
return { logo: '', appname: appName, user: 'exist' }; return { logo: '', appname: appName, user: 'exist' };
} else { } else {
@@ -1,11 +1,15 @@
import axios from 'axios'; import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js'; import {
cloudServerUrl,
} from '../../Utils.js';
export default async function GetTemplate(request) { export default async function GetTemplate(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const templateId = request.params.templateId; const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
const sessiontoken = request.headers?.sessiontoken; const sessiontoken = request.headers?.sessiontoken;
try { try {
if (!ispublic) {
let userEmail; let userEmail;
if (sessiontoken) { if (sessiontoken) {
const userRes = await axios.get(serverUrl + '/users/me', { const userRes = await axios.get(serverUrl + '/users/me', {
@@ -62,21 +66,47 @@ export default async function GetTemplate(request) {
delete templateRes?.ExtUserPtr?.TenantId?.PfxFile; delete templateRes?.ExtUserPtr?.TenantId?.PfxFile;
return templateRes; return templateRes;
} else { } else {
return { error: "You don't have access of this template!" }; return { error: "You don't have access of this document!" };
} }
} catch (err) { } catch (err) {
console.log('err', err); console.log('err', err);
return err; return err;
} }
} else { } else {
return { error: "You don't have access of this template!" }; return { error: "You don't have access of this document!" };
}
} else if (templateId && ispublic) {
try {
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
template.include('Bcc');
const res = await template.first({ useMasterKey: true });
// console.log("res ", res)
if (res) {
const templateRes = JSON.parse(JSON.stringify(res));
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapters;
delete templateRes?.ExtUserPtr?.TenantId?.PfxFile;
return templateRes;
} else {
return { error: "You don't have access of this document!" };
}
} catch (err) {
console.log('err', err);
return err;
}
} else {
return { error: 'Please pass required parameters!' };
} }
} catch (err) { } catch (err) {
console.log('err', err); console.log('err', err);
if (err?.response?.data?.code === 209 || err.code == 209) { if (err?.response?.data?.code === 209 || err.code == 209) {
return { error: 'Invalid session token' }; return { error: 'Invalid session token' };
} else { } else {
return { error: "You don't have access of this template!" }; return { error: "You don't have access of this document!" };
} }
} }
} }
@@ -1,7 +1,7 @@
import axios from 'axios'; import axios from 'axios';
export default async function Newsletter(request) { export default async function Newsletter(request) {
const name = request.params.name; const name = request.params.name;
const email = request.params?.email?.toLowerCase()?.replace(/\s/g, ''); const email = request.params.email;
const domain = request.params.domain; const domain = request.params.domain;
try { try {
const envAppId = process.env.REACT_APP_APPID || 'opensign'; const envAppId = process.env.REACT_APP_APPID || 'opensign';
@@ -19,22 +19,22 @@ async function getDocument(docId) {
} }
async function sendMailOTPv1(request) { async function sendMailOTPv1(request) {
try { try {
//--for elearning app side
let code = Math.floor(1000 + Math.random() * 9000); let code = Math.floor(1000 + Math.random() * 9000);
let email = request.params.email; let email = request.params.email;
let TenantId = request.params.TenantId ? request.params.TenantId : undefined; var TenantId = request.params.TenantId ? request.params.TenantId : undefined;
const AppName = appName;
if (email) { if (email) {
const recipient = request.params.email; const recipient = request.params.email;
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER; const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
try { try {
await Parse.Cloud.sendEmail({ await Parse.Cloud.sendEmail({
sender: AppName + ' <' + mailsender + '>', from: appName + ' <' + mailsender + '>',
recipient: recipient, recipient: recipient,
subject: `Your ${AppName} OTP`, subject: `Your ${appName} OTP`,
text: 'otp email', text: 'This email is a test.',
html: html:
`<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui;background-color:#47a3ad;'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px;'>OTP Verification</p></div><div style='padding:20px;'><p style='font-family:system-ui;font-size:14px;'>Your OTP for ${AppName} verification is:</p><p style='text-decoration:none;font-weight:bolder;color:blue;font-size:45px;margin:20px;'>` + `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for ${appName} verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
code + code +
'</p></div> </div> </div></body></html>', '</p></div> </div> </div></body></html>',
}); });
@@ -1,23 +1,5 @@
import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js';
async function TemplateBeforeSave(request) { async function TemplateBeforeSave(request) {
if (!request.original) { if (!request.original) {
const validations = [
{ field: 'Name', max: MAX_NAME_LENGTH },
{ field: 'Note', max: MAX_NOTE_LENGTH },
{ field: 'Description', max: MAX_DESCRIPTION_LENGTH },
];
for (const { field, max } of validations) {
const value = request.object?.get(field);
if (value && value.length > max) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
`The "${field}" field must be at most ${max} characters long.`
);
}
}
const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15; const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5; const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
const AutoReminder = request?.object?.get('AutomaticReminders') || false; const AutoReminder = request?.object?.get('AutomaticReminders') || false;
@@ -0,0 +1,108 @@
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 = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
try {
const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr.TenantId');
const docRes = await docQuery.get(docId, { useMasterKey: true });
const isEnableOTP = docRes?.get('IsEnableOTP') || false;
let userId;
if (isEnableOTP) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
userId = userRes.data && userRes.data.objectId;
}
if (!isEnableOTP || userId) {
if (event === 'viewed' && contactId) {
if (docRes) {
const _docRes = docRes.toJSON();
const userPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactId,
};
const date = new Date().toISOString();
const obj = {
UserPtr: userPtr,
SignedUrl: _docRes.SignedUrl,
Activity: 'Viewed',
ipAddress: request.headers['x-real-ip'],
ViewedOn: date,
};
const isUserExist = _docRes?.AuditTrail?.some(
x => x.UserPtr.objectId === contactId && x?.ViewedOn
);
if (!isUserExist) {
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docRes.id;
if (_docRes?.AuditTrail && _docRes?.AuditTrail?.length > 0) {
updateDoc.set('AuditTrail', [..._docRes?.AuditTrail, obj]);
} else {
updateDoc.set('AuditTrail', [obj]);
}
await updateDoc.save(null, { useMasterKey: true });
}
}
}
const extendcls = new Parse.Query('contracts_Users');
extendcls.equalTo('objectId', docRes.get('ExtUserPtr')?.id);
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const resExt = await extendcls.first({ useMasterKey: true });
if (resExt) {
const extUser = JSON.parse(JSON.stringify(resExt));
if (extUser?.Webhook) {
const params = { event: event, ...body };
await axios
.post(extUser?.Webhook, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err.message);
}
})
.catch(err => {
console.log('Err send data to webhook', err.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err.message);
}
});
}
return { message: 'webhook called!' };
}
} else {
return { message: 'User not found!' };
}
} catch (err) {
console.log('Err in callwebhook', err);
return { message: 'Something went wrong!' };
}
}
@@ -21,7 +21,7 @@ export default async function createBatchContact(req) {
TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId }, TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id }, CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id },
Name: x.Name, Name: x.Name,
Email: x.Email?.toLowerCase()?.replace(/\s/g, ''), Email: x.Email,
IsDeleted: false, IsDeleted: false,
IsImported: true, IsImported: true,
...(x?.Phone ? { Phone: `${x?.Phone}` } : {}), ...(x?.Phone ? { Phone: `${x?.Phone}` } : {}),
@@ -1,8 +1,14 @@
import axios from 'axios'; import axios from 'axios';
import { cloudServerUrl, replaceMailVaribles } from '../../Utils.js'; import {
cloudServerUrl,
replaceMailVaribles,
} from '../../Utils.js';
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;
async function deductcount(docsCount, extUserId) { async function deductcount(
docsCount,
extUserId,
) {
try { try {
const extCls = new Parse.Object('contracts_Users'); const extCls = new Parse.Object('contracts_Users');
extCls.id = extUserId; extCls.id = extUserId;
@@ -12,9 +18,9 @@ async function deductcount(docsCount, extUserId) {
console.log('Err in deduct in quick send', err); console.log('Err in deduct in quick send', err);
} }
} }
async function sendMail(document, publicUrl) { async function sendMail(document) {
//sessionToken //sessionToken
const baseUrl = new URL(publicUrl); //process.env.PUBLIC_URL const baseUrl = new URL(process.env.PUBLIC_URL);
// console.log("pdfDetails", pdfDetails); // console.log("pdfDetails", pdfDetails);
const timeToCompleteDays = document?.TimeToCompleteDays || 15; const timeToCompleteDays = document?.TimeToCompleteDays || 15;
@@ -27,8 +33,10 @@ async function sendMail(document, publicUrl) {
year: 'numeric', year: 'numeric',
}); });
let signerMail = document.Placeholders; let signerMail = document.Placeholders;
const senderName = document.ExtUserPtr.Name; const senderName =
const senderEmail = document.ExtUserPtr.Email; document.ExtUserPtr.Name;
const senderEmail =
document.ExtUserPtr.Email;
if (document.SendinOrder) { if (document.SendinOrder) {
signerMail = signerMail.slice(); signerMail = signerMail.slice();
@@ -62,7 +70,6 @@ async function sendMail(document, publicUrl) {
'</body></html>'; '</body></html>';
const variables = { const variables = {
document_title: document?.Name, document_title: document?.Name,
note: document?.Note || '',
sender_name: senderName, sender_name: senderName,
sender_mail: senderEmail, sender_mail: senderEmail,
sender_phone: senderObj?.Phone || '', sender_phone: senderObj?.Phone || '',
@@ -76,7 +83,6 @@ async function sendMail(document, publicUrl) {
replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables); replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
} }
const mailparam = { const mailparam = {
note: document?.Note || '',
senderName: senderName, senderName: senderName,
senderMail: senderEmail, senderMail: senderEmail,
title: document.Name, title: document.Name,
@@ -88,7 +94,8 @@ async function sendMail(document, publicUrl) {
extUserId: document.ExtUserPtr.objectId, extUserId: document.ExtUserPtr.objectId,
recipient: objectId ? existSigner?.Email : signerMail[i].email, recipient: objectId ? existSigner?.Email : signerMail[i].email,
subject: replaceVar?.subject ? replaceVar?.subject : mailTemplate(mailparam).subject, subject: replaceVar?.subject ? replaceVar?.subject : mailTemplate(mailparam).subject,
from: document.ExtUserPtr.Email, from:
document.ExtUserPtr.Email,
replyto: senderEmail || '', replyto: senderEmail || '',
html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body, html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body,
}; };
@@ -101,7 +108,13 @@ async function sendMail(document, publicUrl) {
} }
} }
} }
async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) { async function batchQuery(
userId,
Documents,
Ip,
parseConfig,
type
) {
const extCls = new Parse.Query('contracts_Users'); const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('UserId', { extCls.equalTo('UserId', {
__type: 'Pointer', __type: 'Pointer',
@@ -182,15 +195,6 @@ async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) {
...(x?.RedirectUrl ? { RedirectUrl: x?.RedirectUrl } : {}), ...(x?.RedirectUrl ? { RedirectUrl: x?.RedirectUrl } : {}),
...(mailBody ? { RequestBody: mailBody } : {}), ...(mailBody ? { RequestBody: mailBody } : {}),
...(mailSubject ? { RequestSubject: mailSubject } : {}), ...(mailSubject ? { RequestSubject: mailSubject } : {}),
...(x?.objectId
? {
TemplateId: {
__type: 'Pointer',
className: 'contracts_Template',
objectId: x?.objectId,
},
}
: {}),
}, },
}; };
}); });
@@ -208,7 +212,7 @@ async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) {
createdAt: response.data[0]?.success?.createdAt, createdAt: response.data[0]?.success?.createdAt,
}; };
deductcount(response.data.length, resExt.id); deductcount(response.data.length, resExt.id);
sendMail(updateDocuments, publicUrl); //sessionToken sendMail(updateDocuments); //sessionToken
return 'success'; return 'success';
} }
} }
@@ -232,8 +236,6 @@ export default async function createBatchDocs(request) {
const type = request.headers?.type || 'quicksend'; const type = request.headers?.type || 'quicksend';
const Documents = JSON.parse(strDocuments); const Documents = JSON.parse(strDocuments);
const Ip = request?.headers?.['x-real-ip'] || ''; const Ip = request?.headers?.['x-real-ip'] || '';
// Access the host from the headers
const publicUrl = request.headers.public_url;
const parseConfig = { const parseConfig = {
baseURL: serverUrl, baseURL: serverUrl,
headers: { headers: {
@@ -244,8 +246,9 @@ export default async function createBatchDocs(request) {
}; };
try { try {
if (request?.user) { if (request?.user) {
return await batchQuery(request.user.id, Documents, Ip, parseConfig, '', type, publicUrl); return await batchQuery(request.user.id, Documents, Ip, parseConfig, '', type);
} else { }
else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} }
} catch (err) { } catch (err) {
@@ -15,7 +15,7 @@ export default async function editContact(request) {
const query = new Parse.Query('contracts_Contactbook'); const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', createdBy); query.equalTo('CreatedBy', createdBy);
query.notEqualTo('IsDeleted', true); query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email?.toLowerCase()?.replace(/\s/g, '')); query.equalTo('Email', email);
const isContactExist = await query.first({ useMasterKey: true }); const isContactExist = await query.first({ useMasterKey: true });
if (isContactExist) { if (isContactExist) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.'); throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.');
@@ -25,7 +25,7 @@ export default async function editContact(request) {
if (phone) { if (phone) {
contactQuery.set('Phone', phone); contactQuery.set('Phone', phone);
} }
contactQuery.set('Email', email?.toLowerCase()?.replace(/\s/g, '')); contactQuery.set('Email', email);
contactQuery.set('UserRole', 'contracts_Guest'); contactQuery.set('UserRole', 'contracts_Guest');
contactQuery.set('IsDeleted', false); contactQuery.set('IsDeleted', false);
contactQuery.set('TenantId', { contactQuery.set('TenantId', {
@@ -37,9 +37,9 @@ export default async function editContact(request) {
const _users = Parse.Object.extend('User'); const _users = Parse.Object.extend('User');
const _user = new _users(); const _user = new _users();
_user.set('name', name); _user.set('name', name);
_user.set('username', email?.toLowerCase()?.replace(/\s/g, '')); _user.set('username', email);
_user.set('email', email?.toLowerCase()?.replace(/\s/g, '')); _user.set('email', email);
_user.set('password', email?.toLowerCase()?.replace(/\s/g, '')); _user.set('password', email);
if (phone) { if (phone) {
_user.set('phone', phone); _user.set('phone', phone);
} }
@@ -0,0 +1,14 @@
import { PostHog } from 'posthog-node';
const ph_project_api_key = process.env.PH_PROJECT_API_KEY;
const client = ph_project_api_key ? new PostHog(ph_project_api_key) : '';
export default async function encryptedpdf(request) {
const email = request.params.email;
if (client) {
client?.capture({
distinctId: email,
event: 'encrypted_pdf_error',
properties: { response_code: 200 },
});
}
return { message: 'success' };
}
@@ -1,5 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js'; import {
cloudServerUrl,
} from '../../Utils.js';
export default async function getDocument(request) { export default async function getDocument(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId; const docId = request.params.docId;
@@ -45,12 +47,13 @@ export default async function getDocument(request) {
console.log('err user in not authenticated', err); console.log('err user in not authenticated', err);
return { error: "You don't have access of this document!" }; return { error: "You don't have access of this document!" };
} }
} else { }
else {
return { error: "You don't have access of this document!" }; return { error: "You don't have access of this document!" };
} }
} }
} else { } else {
return { error: "document deleted or you don't have access." }; return { error: "You don't have access of this document!" };
} }
} catch (err) { } catch (err) {
console.log('err', err); console.log('err', err);
@@ -52,8 +52,7 @@ const saveRoleContact = async contact => {
// `linkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and // `linkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// save contact pointer in placeholder, signers and ACL of Document // save contact pointer in placeholder, signers and ACL of Document
export default async function linkContactToDoc(req) { export default async function linkContactToDoc(req) {
const requestemail = req.params?.email; const email = req.params.email;
const email = requestemail?.toLowerCase()?.replace(/\s/g, '');
const docId = req.params.docId; const docId = req.params.docId;
const name = req.params.name; const name = req.params.name;
const phone = req.params.phone; const phone = req.params.phone;
@@ -1,12 +1,12 @@
import { PDFDocument, rgb } from 'pdf-lib'; import { PDFDocument, rgb } from 'pdf-lib';
import fs from 'node:fs'; import fs from 'node:fs';
import fontkit from '@pdf-lib/fontkit'; import fontkit from '@pdf-lib/fontkit';
import { formatDateTime } from '../../../Utils.js'; import {
formatTimeInTimezone,
} from '../../../Utils.js';
export default async function GenerateCertificate(docDetails) { export default async function GenerateCertificate(docDetails) {
const timezone = docDetails?.ExtUserPtr?.Timezone || ''; const timezone = docDetails?.ExtUserPtr?.Timezone || '';
const Is12Hr = docDetails?.ExtUserPtr?.Is12HourTime || false;
const DateFormat = docDetails?.ExtUserPtr?.DateFormat || 'MM/DD/YYYY';
const pdfDoc = await PDFDocument.create(); const pdfDoc = await PDFDocument.create();
// `fontBytes` is used to embed custom font in pdf // `fontBytes` is used to embed custom font in pdf
const fontBytes = fs.readFileSync('./font/times.ttf'); // const fontBytes = fs.readFileSync('./font/times.ttf'); //
@@ -22,27 +22,23 @@ export default async function GenerateCertificate(docDetails) {
const titleColor = rgb(0, 0.2, 0.4); //rgb(0, 0.53, 0.71); const titleColor = rgb(0, 0.2, 0.4); //rgb(0, 0.53, 0.71);
const titleUnderline = rgb(0, 0.2, 0.4); // rgb(0.12, 0.12, 0.12); const titleUnderline = rgb(0, 0.2, 0.4); // rgb(0.12, 0.12, 0.12);
const title = 25; const title = 25;
const subtitle = 16; const subtitle = 20;
const text = 13; const text = 14;
const signertext = 13;
const timeText = 11; const timeText = 11;
const textKeyColor = rgb(0.12, 0.12, 0.12); const textKeyColor = rgb(0.12, 0.12, 0.12);
const textValueColor = rgb(0.3, 0.3, 0.3); const textValueColor = rgb(0.3, 0.3, 0.3);
const completedAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date(); const completedAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date();
const completedAtperTimezone = formatDateTime(completedAt, DateFormat, timezone, Is12Hr); const completedAtperTimezone = formatTimeInTimezone(completedAt, timezone);
const completedUTCtime = completedAtperTimezone; const completedUTCtime = completedAtperTimezone;
const signersCount = docDetails?.Signers?.length || 1; const signersCount = docDetails?.Signers?.length || 1;
const generateAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date(); const generateAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date();
const generatedAtperTimezone = formatDateTime(generateAt, DateFormat, timezone, Is12Hr); const generatedAtperTimezone = formatTimeInTimezone(generateAt, timezone);
const generatedUTCTime = generatedAtperTimezone; const generatedUTCTime = generatedAtperTimezone;
const generatedOn = 'Generated On ' + generatedUTCTime; const generatedOn = 'Generated On ' + generatedUTCTime;
const textWidth = timesRomanFont.widthOfTextAtSize(generatedOn, 12);
const margin = 30;
const maxX = width - margin - textWidth; // Ensures text stays inside the border with 30px margin
const OriginIp = docDetails?.OriginIp || ''; const OriginIp = docDetails?.OriginIp || '';
const company = docDetails?.ExtUserPtr?.Company || ''; const company = docDetails?.ExtUserPtr?.Company || '';
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt; const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
const createdAtperTimezone = formatDateTime(createdAt, DateFormat, timezone, Is12Hr); const createdAtperTimezone = formatTimeInTimezone(createdAt, timezone);
const IsEnableOTP = docDetails?.IsEnableOTP || false; const IsEnableOTP = docDetails?.IsEnableOTP || false;
const filteredaudit = docDetails?.AuditTrail?.filter(x => x?.UserPtr?.objectId); const filteredaudit = docDetails?.AuditTrail?.filter(x => x?.UserPtr?.objectId);
const auditTrail = const auditTrail =
@@ -87,7 +83,7 @@ export default async function GenerateCertificate(docDetails) {
}); });
page.drawText(generatedOn, { page.drawText(generatedOn, {
x: Math.max(startX, maxX), // Adjusts dynamically 320 x: 320,
y: 810, y: 810,
size: 12, size: 12,
font: timesRomanFont, font: timesRomanFont,
@@ -96,13 +92,13 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Certificate of Completion', { page.drawText('Certificate of Completion', {
x: 160, x: 160,
y: 755, y: 750,
size: title, size: title,
font: timesRomanFont, font: timesRomanFont,
color: titleColor, color: titleColor,
}); });
const underlineY = 745; const underlineY = 740;
page.drawLine({ page.drawLine({
start: { x: 30, y: underlineY }, start: { x: 30, y: underlineY },
end: { x: width - 30, y: underlineY }, end: { x: width - 30, y: underlineY },
@@ -112,7 +108,7 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Summary', { page.drawText('Summary', {
x: 30, x: 30,
y: 727, y: 710,
size: subtitle, size: subtitle,
font: timesRomanFont, font: timesRomanFont,
color: titleColor, color: titleColor,
@@ -120,15 +116,15 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Document Id :', { page.drawText('Document Id :', {
x: 30, x: 30,
y: 710, y: 685,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(docDetails.objectId, { page.drawText(docDetails.objectId, {
x: 110, x: 115,
y: 710, y: 685,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
@@ -136,15 +132,15 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Document Name :', { page.drawText('Document Name :', {
x: 30, x: 30,
y: 690, y: 665,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(docDetails?.Name, { page.drawText(docDetails?.Name, {
x: 130, x: 140,
y: 690, y: 665,
size: docDetails?.Name?.length >= 78 ? 12 : text, size: docDetails?.Name?.length >= 78 ? 12 : text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
@@ -152,52 +148,52 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Organization :', { page.drawText('Organization :', {
x: 30, x: 30,
y: 670, y: 645,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(company, { page.drawText(company, {
x: 110, x: 115,
y: 670, y: 645,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Created on :', { page.drawText('Created on :', {
x: 30, x: 30,
y: 650, y: 625,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(`${createdAtperTimezone}`, { page.drawText(`${createdAtperTimezone}`, {
x: 97, x: 105,
y: 650, y: 625,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Completed on :', { page.drawText('Completed on :', {
x: 30, x: 30,
y: 630, y: 605,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(`${completedUTCtime}`, { page.drawText(`${completedUTCtime}`, {
x: 115, x: 125,
y: 630, y: 605,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Signers :', { page.drawText('Signers :', {
x: 30, x: 30,
y: 610, y: 585,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
@@ -205,76 +201,73 @@ export default async function GenerateCertificate(docDetails) {
page.drawText(`${signersCount}`, { page.drawText(`${signersCount}`, {
x: 80, x: 80,
y: 610, y: 585,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Document originator', { page.drawText('Document originator', {
x: 30, x: 30,
y: 590, y: 565,
size: 17, size: 17,
font: timesRomanFont, font: timesRomanFont,
color: titleColor, color: titleColor,
}); });
page.drawText('Name :', { page.drawText('Name :', {
x: 60, x: 60,
y: 573, y: 545,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(ownerName, { page.drawText(ownerName, {
x: 105, x: 105,
y: 573, y: 545,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Email :', { page.drawText('Email :', {
x: 60, x: 60,
y: 553, y: 525,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(ownerEmail, { page.drawText(ownerEmail, {
x: 105, x: 105,
y: 553, y: 525,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('IP address :', { page.drawText('IP address :', {
x: 60, x: 60,
y: 533, y: 505,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(`${OriginIp}`, { page.drawText(`${OriginIp}`, {
x: 125, x: 130,
y: 533, y: 505,
size: text, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawLine({ page.drawLine({
start: { x: 30, y: 527 }, start: { x: 30, y: 495 },
end: { x: width - 30, y: 527 }, end: { x: width - 30, y: 495 },
color: rgb(0.12, 0.12, 0.12), color: rgb(0.12, 0.12, 0.12),
thickness: 0.5, thickness: 0.5,
}); });
let yPosition1 = 512; let yPosition1 = 475;
let yPosition2 = 498; let yPosition2 = 455;
let yPosition3 = 478; let yPosition3 = 435;
let yPosition4 = 458; let yPosition4 = 415;
let yPosition5 = 438; let yPosition5 = 395;
let yPosition6 = 418; let yPosition6 = 360;
let yPosition7 = 398;
let yPosition8 = 363;
auditTrail.slice(0, 3).forEach(async (x, i) => { auditTrail.slice(0, 3).forEach(async (x, i) => {
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : ''; const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
page.drawText(`Signer ${i + 1}`, { page.drawText(`Signer ${i + 1}`, {
@@ -287,7 +280,7 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Name :', { page.drawText('Name :', {
x: 30, x: 30,
y: yPosition2, y: yPosition2,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
@@ -295,32 +288,32 @@ export default async function GenerateCertificate(docDetails) {
page.drawText(x?.Name, { page.drawText(x?.Name, {
x: 75, x: 75,
y: yPosition2, y: yPosition2,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
if (IsEnableOTP) { page.drawText('Viewed on :', {
page.drawText('Security level :', { x: half + 45,
x: half + 120,
y: yPosition2, y: yPosition2,
size: timeText, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText('Email, OTP Auth', {
x: half + 190, //new Date(x.ViewedOn).toUTCString()
page.drawText(`${formatTimeInTimezone(x.ViewedOn, timezone)}`, {
x: half + 102,
y: yPosition2, y: yPosition2,
size: timeText, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
}
page.drawText('Email :', { page.drawText('Email :', {
x: 30, x: 30,
y: yPosition3, y: yPosition3,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
@@ -328,70 +321,70 @@ export default async function GenerateCertificate(docDetails) {
page.drawText(x?.Email, { page.drawText(x?.Email, {
x: 75, x: 75,
y: yPosition3, y: yPosition3,
size: signertext, size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Viewed on :', {
x: 30,
y: yPosition4,
size: signertext,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${formatDateTime(x.ViewedOn, DateFormat, timezone, Is12Hr)}`, {
x: 97,
y: yPosition4,
size: signertext,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('Signed on :', { page.drawText('Signed on :', {
x: 30, x: half + 45,
y: yPosition5, y: yPosition3 + 5,
size: signertext, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(`${formatDateTime(x.SignedOn, DateFormat, timezone, Is12Hr)}`, { // new Date(x.SignedOn).toUTCString()
x: 95, page.drawText(`${formatTimeInTimezone(x.SignedOn, timezone)}`, {
y: yPosition5, x: half + 98,
size: signertext, y: yPosition3 + 5,
size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
page.drawText('IP address :', { page.drawText('IP address :', {
x: 30, x: 30,
y: yPosition6, y: yPosition4,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText(x?.ipAddress, { page.drawText(x?.ipAddress, {
x: 95, x: 100,
y: yPosition6, y: yPosition4,
size: signertext, size: 13,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
if (IsEnableOTP) {
page.drawText('Security level :', {
x: half + 45,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText('Email, OTP Auth', {
x: half + 115,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textValueColor,
});
}
page.drawText('Signature :', { page.drawText('Signature :', {
x: 30, x: 30,
y: yPosition7, y: yPosition5,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawRectangle({ page.drawRectangle({
x: 98, x: 98,
y: yPosition7 - 30, y: yPosition5 - 30,
width: 104, width: 104,
height: 44, height: 44,
borderColor: rgb(0.22, 0.18, 0.47), borderColor: rgb(0.22, 0.18, 0.47),
@@ -400,26 +393,24 @@ export default async function GenerateCertificate(docDetails) {
if (embedPng) { if (embedPng) {
page.drawImage(embedPng, { page.drawImage(embedPng, {
x: 100, x: 100,
y: yPosition7 - 27, y: yPosition5 - 27,
width: 100, width: 100,
height: 40, height: 40,
}); });
} }
page.drawLine({ page.drawLine({
start: { x: 30, y: yPosition8 }, start: { x: 30, y: yPosition6 },
end: { x: width - 30, y: yPosition8 }, end: { x: width - 30, y: yPosition6 },
color: rgb(0.12, 0.12, 0.12), color: rgb(0.12, 0.12, 0.12),
thickness: 0.5, thickness: 0.5,
}); });
yPosition1 = yPosition8 - 20; yPosition1 = yPosition6 - 20;
yPosition2 = yPosition1 - 20; yPosition2 = yPosition1 - 20;
yPosition3 = yPosition2 - 20; yPosition3 = yPosition2 - 20;
yPosition4 = yPosition3 - 20; yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20; yPosition5 = yPosition4 - 20;
yPosition6 = yPosition5 - 20; yPosition6 = yPosition6 - 140;
yPosition7 = yPosition6 - 20;
yPosition8 = yPosition8 - 174;
}); });
if (auditTrail.length > 3) { if (auditTrail.length > 3) {
@@ -429,7 +420,7 @@ export default async function GenerateCertificate(docDetails) {
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : ''; const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
// Calculate remaining space on current page // Calculate remaining space on current page
const remainingSpace = yPosition8; const remainingSpace = yPosition6;
// If there's not enough space for the next entry, create a new page // If there's not enough space for the next entry, create a new page
if (remainingSpace < 90) { if (remainingSpace < 90) {
@@ -449,10 +440,7 @@ export default async function GenerateCertificate(docDetails) {
yPosition3 = yPosition2 - 20; yPosition3 = yPosition2 - 20;
yPosition4 = yPosition3 - 20; yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20; yPosition5 = yPosition4 - 20;
yPosition5 = yPosition4 - 20; yPosition6 = currentPage.getHeight() - 160;
yPosition6 = yPosition5 - 20;
yPosition7 = yPosition6 - 20;
yPosition8 = currentPage.getHeight() - 190;
} }
currentPage.drawText(`Signer ${4 + i}`, { currentPage.drawText(`Signer ${4 + i}`, {
@@ -465,7 +453,7 @@ export default async function GenerateCertificate(docDetails) {
currentPage.drawText('Name :', { currentPage.drawText('Name :', {
x: 30, x: 30,
y: yPosition2, y: yPosition2,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
@@ -473,32 +461,32 @@ export default async function GenerateCertificate(docDetails) {
currentPage.drawText(x?.Name, { currentPage.drawText(x?.Name, {
x: 75, x: 75,
y: yPosition2, y: yPosition2,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
if (IsEnableOTP) { currentPage.drawText('Viewed on :', {
currentPage.drawText('Security level :', { x: half + 45,
x: half + 120,
y: yPosition2, y: yPosition2,
size: timeText, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
currentPage.drawText(`Email, OTP Auth`, {
x: half + 190, // new Date(x.ViewedOn).toUTCString()
currentPage.drawText(`${formatTimeInTimezone(x.ViewedOn, timezone)}`, {
x: half + 102,
y: yPosition2, y: yPosition2,
size: timeText, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
}
currentPage.drawText('Email :', { currentPage.drawText('Email :', {
x: 30, x: 30,
y: yPosition3, y: yPosition3,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
@@ -506,68 +494,70 @@ export default async function GenerateCertificate(docDetails) {
currentPage.drawText(x?.Email, { currentPage.drawText(x?.Email, {
x: 75, x: 75,
y: yPosition3, y: yPosition3,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
currentPage.drawText('Viewed on :', {
x: 30,
y: yPosition4,
size: signertext,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${formatDateTime(x.ViewedOn, DateFormat, timezone, Is12Hr)}`, {
x: 97,
y: yPosition4,
size: signertext,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Signed on :', { currentPage.drawText('Signed on :', {
x: 30, x: half + 45,
y: yPosition5, y: yPosition3 + 5,
size: signertext, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
currentPage.drawText(`${formatDateTime(x.SignedOn, DateFormat, timezone, Is12Hr)}`, { // new Date(x.SignedOn).toUTCString()
x: 95, currentPage.drawText(`${formatTimeInTimezone(x.SignedOn, timezone)}`, {
y: yPosition5, x: half + 98,
size: signertext, y: yPosition3 + 5,
size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
currentPage.drawText('IP address :', { currentPage.drawText('IP address :', {
x: 30, x: 30,
y: yPosition6, y: yPosition4,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
currentPage.drawText(x?.ipAddress, { currentPage.drawText(x?.ipAddress, {
x: 100, x: 100,
y: yPosition6, y: yPosition4,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
if (IsEnableOTP) {
currentPage.drawText('Security level :', {
x: half + 45,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`Email, OTP Auth`, {
x: half + 115,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textValueColor,
});
}
currentPage.drawText('Signature :', { currentPage.drawText('Signature :', {
x: 30, x: 30,
y: yPosition7, y: yPosition5,
size: signertext, size: text,
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
currentPage.drawRectangle({ currentPage.drawRectangle({
x: 98, x: 98,
y: yPosition7 - 27, y: yPosition5 - 27,
width: 104, width: 104,
height: 44, height: 44,
borderColor: rgb(0.22, 0.18, 0.47), borderColor: rgb(0.22, 0.18, 0.47),
@@ -576,28 +566,26 @@ export default async function GenerateCertificate(docDetails) {
if (embedPng) { if (embedPng) {
currentPage.drawImage(embedPng, { currentPage.drawImage(embedPng, {
x: 100, x: 100,
y: yPosition7 - 25, y: yPosition5 - 25,
width: 100, width: 100,
height: 40, height: 40,
}); });
} }
currentPage.drawLine({ currentPage.drawLine({
start: { x: 30, y: yPosition8 }, start: { x: 30, y: yPosition6 },
end: { x: width - 30, y: yPosition8 }, end: { x: width - 30, y: yPosition6 },
color: rgb(0.12, 0.12, 0.12), color: rgb(0.12, 0.12, 0.12),
thickness: 0.5, thickness: 0.5,
}); });
// Update y positions for the next entry // Update y positions for the next entry
yPosition1 = yPosition8 - 20; yPosition1 = yPosition6 - 20;
yPosition2 = yPosition1 - 20; yPosition2 = yPosition1 - 20;
yPosition3 = yPosition2 - 20; yPosition3 = yPosition2 - 20;
yPosition4 = yPosition3 - 20; yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20; yPosition5 = yPosition4 - 20;
yPosition6 = yPosition5 - 20; yPosition6 = yPosition6 - 140;
yPosition7 = yPosition6 - 20;
yPosition8 = yPosition8 - 174;
}); });
} }
@@ -19,6 +19,9 @@ 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 eSigncontact = 'hello@opensignlabs.com';
const logo =
"<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>`;
// `updateDoc` is used to create url in from pdfFile // `updateDoc` is used to create url in from pdfFile
async function uploadFile(pdfName, filepath) { async function uploadFile(pdfName, filepath) {
@@ -90,12 +93,8 @@ async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
} }
// `sendNotifyMail` is used to send notification mail of signer signed the document // `sendNotifyMail` is used to send notification mail of signer signed the document
async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) { async function sendNotifyMail(doc, signUser, mailProvider) {
try { try {
const TenantAppName = appName;
const logo =
"<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 auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0; const auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0;
const signersCount = doc?.Placeholders?.length; const signersCount = doc?.Placeholders?.length;
const remaingsign = signersCount - auditTrailCount; const remaingsign = signersCount - auditTrailCount;
@@ -106,18 +105,18 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
const creatorEmail = doc.ExtUserPtr.Email; const creatorEmail = doc.ExtUserPtr.Email;
const signerName = signUser.Name; const signerName = signUser.Name;
const signerEmail = signUser.Email; const signerEmail = signUser.Email;
const viewDocUrl = `${publicUrl}/recipientSignPdf/${doc.objectId}`; // ` ${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`; const viewDocUrl = `${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`;
const subject = `Document "${pdfName}" has been signed by ${signerName}`; const subject = `Document "${pdfName}" has been signed by ${signerName}`;
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-color:white'>" + "<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-color:white'>" +
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed by ${signerName}</p>` + `<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed by ${signerName}</p>` +
`</div><div style='padding:20px;font-family:system-ui;font-size:14px'><p>Dear ${creatorName},</p><p>${pdfName} has been signed by ${signerName} "${signerEmail}" successfully</p>` + `</div><div style='padding:20px;font-family:system-ui;font-size:14px'><p>Dear ${creatorName},</p><p>${pdfName} has been signed by ${signerName} "${signerEmail}" successfully</p>` +
`<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, ` + `<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, ` +
`please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`; `please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`;
const params = { const params = {
extUserId: sender.objectId, extUserId: sender.objectId,
from: TenantAppName, from: appName,
recipient: creatorEmail, recipient: creatorEmail,
subject: subject, subject: subject,
pdfName: pdfName, pdfName: pdfName,
@@ -143,10 +142,6 @@ async function sendCompletedMail(obj) {
const doc = obj.doc; const doc = obj.doc;
const sender = obj.doc.ExtUserPtr; const sender = obj.doc.ExtUserPtr;
const pdfName = doc.Name; const pdfName = doc.Name;
const TenantAppName = appName;
const logo =
"<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>`;
let signersMail; let signersMail;
if (doc?.Signers?.length > 0) { if (doc?.Signers?.length > 0) {
const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email); const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email);
@@ -162,8 +157,8 @@ async function sendCompletedMail(obj) {
"<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-color:white'>" + "<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-color:white'>" +
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed successfully</p></div><div>` + `<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed successfully</p></div><div>` +
`<p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document <b>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` + `<p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document <b>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` +
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` + `</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` +
`If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`; `If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`;
if (obj?.isCustomMail) { if (obj?.isCustomMail) {
const tenant = sender?.TenantId; const tenant = sender?.TenantId;
@@ -180,7 +175,7 @@ async function sendCompletedMail(obj) {
className: '_User', className: '_User',
objectId: userId, objectId: userId,
}); });
const tenantRes = await tenantQuery.first({ useMasterKey: true }); const tenantRes = await tenantQuery.first();
if (tenantRes) { if (tenantRes) {
const _tenantRes = JSON.parse(JSON.stringify(tenantRes)); const _tenantRes = JSON.parse(JSON.stringify(tenantRes));
subject = _tenantRes?.CompletionSubject || ''; subject = _tenantRes?.CompletionSubject || '';
@@ -214,19 +209,18 @@ async function sendCompletedMail(obj) {
subject = replaceVar.subject; subject = replaceVar.subject;
body = replaceVar.body; body = replaceVar.body;
} }
const Bcc = doc?.Bcc?.length > 0 ? doc.Bcc.map(x => x.Email) : []; const Bcc = doc?.Bcc?.length > 0 ? doc.Bcc.map(x => x.Email) : '';
const updatedBcc = doc?.SenderMail ? [...Bcc, doc?.SenderMail] : Bcc;
const params = { const params = {
extUserId: sender.objectId, extUserId: sender.objectId,
url: url, url: url,
from: TenantAppName, from: appName,
replyto: doc?.ExtUserPtr?.Email || '', replyto: doc?.ExtUserPtr?.Email || '',
recipient: recipient, recipient: recipient,
subject: subject, subject: subject,
pdfName: pdfName, pdfName: pdfName,
html: body, html: body,
mailProvider: obj.mailProvider, mailProvider: obj.mailProvider,
bcc: updatedBcc?.length > 0 ? updatedBcc : '', bcc: Bcc,
certificatePath: `./exports/signed_certificate_${doc.objectId}.pdf`, certificatePath: `./exports/signed_certificate_${doc.objectId}.pdf`,
filename: obj?.filename, filename: obj?.filename,
}; };
@@ -306,7 +300,6 @@ async function PDF(req) {
const isCustomMail = req.params.isCustomCompletionMail || false; const isCustomMail = req.params.isCustomCompletionMail || false;
const mailProvider = req.params.mailProvider || ''; const mailProvider = req.params.mailProvider || '';
const sign = req.params.signature || ''; const sign = req.params.signature || '';
const publicUrl = req.headers.public_url;
// below bode is used to get info of docId // below bode is used to get info of docId
const docQuery = new Parse.Query('contracts_Document'); const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId,Bcc'); docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId,Bcc');
@@ -432,7 +425,7 @@ async function PDF(req) {
className, // className based on flow className, // className based on flow
sign // sign base64 sign // sign base64
); );
sendNotifyMail(_resDoc, signUser, mailProvider, publicUrl); sendNotifyMail(_resDoc, signUser, mailProvider);
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 };
@@ -25,9 +25,9 @@ export default function reportJson(id, userId) {
'Signers.Phone', 'Signers.Phone',
'Placeholders', 'Placeholders',
'IsSignyourself', 'IsSignyourself',
'TemplateId',
], ],
}; };
// Need your sign report // Need your sign report
case '4Hhwbp482K': case '4Hhwbp482K':
return { return {
@@ -61,7 +61,6 @@ export default function reportJson(id, userId) {
'AuditTrail', 'AuditTrail',
'Placeholders', 'Placeholders',
'SignedUrl', 'SignedUrl',
'TemplateId',
'ExpiryDate', 'ExpiryDate',
], ],
}; };
@@ -95,7 +94,6 @@ export default function reportJson(id, userId) {
'SendMail', 'SendMail',
'Placeholders', 'Placeholders',
'SignedUrl', 'SignedUrl',
'TemplateId',
], ],
}; };
// completed documents report // completed documents report
@@ -142,7 +140,6 @@ export default function reportJson(id, userId) {
'Placeholders', 'Placeholders',
'IsSignyourself', 'IsSignyourself',
'IsCompleted', 'IsCompleted',
'TemplateId',
], ],
}; };
// declined documents report // declined documents report
@@ -167,7 +164,6 @@ export default function reportJson(id, userId) {
'Placeholders', 'Placeholders',
'DeclineReason', 'DeclineReason',
'SignedUrl', 'SignedUrl',
'TemplateId',
], ],
}; };
// Expired Documents report // Expired Documents report
@@ -194,7 +190,6 @@ export default function reportJson(id, userId) {
'Signers.Phone', 'Signers.Phone',
'Placeholders', 'Placeholders',
'SignedUrl', 'SignedUrl',
'TemplateId',
'ExpiryDate', 'ExpiryDate',
], ],
}; };
@@ -226,7 +221,6 @@ export default function reportJson(id, userId) {
'ExpiryDate', 'ExpiryDate',
'Placeholders', 'Placeholders',
'SignedUrl', 'SignedUrl',
'TemplateId',
], ],
}; };
// Recent signature requests report show on dashboard // Recent signature requests report show on dashboard
@@ -260,7 +254,6 @@ export default function reportJson(id, userId) {
'Signers.Phone', 'Signers.Phone',
'Placeholders', 'Placeholders',
'SignedUrl', 'SignedUrl',
'TemplateId',
'ExpiryDate', 'ExpiryDate',
], ],
}; };
@@ -286,7 +279,6 @@ export default function reportJson(id, userId) {
'Signers.Email', 'Signers.Email',
'Signers.Phone', 'Signers.Phone',
'Placeholders', 'Placeholders',
'TemplateId',
], ],
}; };
// contact book report // contact book report
@@ -1,87 +0,0 @@
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function saveAsTemplate(request) {
const docId = request.params.docId;
const Ip = request?.headers?.['x-real-ip'] || '';
if (!request.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'user is not authenticated.');
}
try {
const docQuery = new Parse.Query('contracts_Document');
docQuery.equalTo('objectId', docId);
docQuery.equalTo('CreatedBy', request.user);
docQuery.include('ExtUserPtr');
docQuery.include('ExtUserPtr.TenantId');
docQuery.notEqualTo('IsArchive', true);
const docRes = await docQuery.first({ useMasterKey: true });
if (docRes) {
const _docRes = docRes?.toJSON();
const templateCls = new Parse.Object('contracts_Template');
templateCls.set('URL', _docRes?.URL);
templateCls.set('Name', _docRes?.Name);
templateCls.set('Note', _docRes?.Note);
templateCls.set('Description', _docRes?.Description);
templateCls.set('OriginIp', Ip);
templateCls.set('SendinOrder', _docRes?.SendinOrder || false);
templateCls.set('AutomaticReminders', _docRes?.AutomaticReminders || false);
templateCls.set('ExtUserPtr', _docRes?.ExtUserPtr);
templateCls.set('CreatedBy', _docRes?.CreatedBy);
templateCls.set('IsEnableOTP', _docRes?.IsEnableOTP === true ? true : false);
templateCls.set('IsTourEnabled', _docRes?.IsTourEnabled === true ? true : false);
templateCls.set('AllowModifications', _docRes?.AllowModifications || false);
templateCls.set('EmailSenderName', _docRes?.EmailSenderName);
templateCls.set('SenderName', _docRes?.SenderName);
templateCls.set('SenderMail', _docRes?.SenderMail);
templateCls.set('FileAdapterId', _docRes?.FileAdapterId);
templateCls.set('RequestBody', _docRes?.RequestBody);
templateCls.set('RequestSubject', _docRes?.RequestSubject);
templateCls.set('NextReminderDate', _docRes?.NextReminderDate);
templateCls.set('RedirectUrl', _docRes?.RedirectUrl);
templateCls.set(
'NotifyOnSignatures',
_docRes?.NotifyOnSignatures !== undefined ? _docRes?.NotifyOnSignatures : false
);
templateCls.set(
'TimeToCompleteDays',
_docRes?.TimeToCompleteDays ? parseInt(_docRes?.TimeToCompleteDays) : 15
);
if (_docRes?.RemindOnceInEvery) {
templateCls.set('RemindOnceInEvery', parseInt(_docRes?.RemindOnceInEvery));
}
if (_docRes?.Placeholders?.length > 0) {
if (_docRes?.IsSignyourself) {
const placeHolders = {
signerObjId: '',
signerPtr: {},
Id: randomId(),
blockColor: '#93a3db',
Role: 'Role 1',
email: '',
placeHolder: _docRes?.Placeholders,
};
templateCls.set('Placeholders', [placeHolders]);
} else {
const placeHolders = _docRes?.Placeholders?.map((x, i) => {
const email = x.email ? { email: '' } : {};
return { ...x, signerObjId: '', signerPtr: {}, Role: 'Role ' + (i + 1), ...email };
});
templateCls.set('Placeholders', placeHolders);
}
}
if (_docRes?.SignatureType?.length > 0) {
templateCls.set('SignatureType', _docRes?.SignatureType);
}
if (_docRes?.Bcc?.length > 0) {
templateCls.set('Bcc', _docRes?.Bcc);
}
const res = await templateCls.save(null, { useMasterKey: true });
return res;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'document not found.');
}
} catch (err) {
console.log('Err in save as template', err);
throw err;
}
}
@@ -1,8 +1,7 @@
export default async function savecontact(request) { export default async function savecontact(request) {
const name = request.params.name; const name = request.params.name;
const phone = request.params.phone; const phone = request.params.phone;
const requestemail = request.params?.email; const email = request.params.email;
const email = requestemail?.toLowerCase()?.replace(/\s/g, '');
const tenantId = request.params.tenantId; const tenantId = request.params.tenantId;
if (request.user) { if (request.user) {
@@ -31,7 +31,6 @@ async function sendMailProvider(req, plan, monthchange) {
if (req.params.url) { if (req.params.url) {
const randomNumber = Math.floor(Math.random() * 5000); const randomNumber = Math.floor(Math.random() * 5000);
const testPdf = `test_${randomNumber}.pdf`; const testPdf = `test_${randomNumber}.pdf`;
try {
let Pdf = fs.createWriteStream(testPdf); let Pdf = fs.createWriteStream(testPdf);
const writeToLocalDisk = () => { const writeToLocalDisk = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -184,19 +183,6 @@ async function sendMailProvider(req, plan, monthchange) {
} }
} }
} }
} catch (err) {
console.log('err in sendmailv3', err);
if (fs.existsSync(testPdf)) {
try {
fs.unlinkSync(testPdf);
} catch (err) {
console.log('Err in unlink pdf sendmailv3');
}
}
if (err) {
return { status: 'error' };
}
}
} else { } else {
const from = req.params.from || ''; const from = req.params.from || '';
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER; const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
@@ -48,12 +48,6 @@ export default async function updatePreferences(request) {
if (request.params.IsTourEnabled !== undefined) { if (request.params.IsTourEnabled !== undefined) {
newOrg.set('IsTourEnabled', request.params.IsTourEnabled); newOrg.set('IsTourEnabled', request.params.IsTourEnabled);
} }
if (request.params.DateFormat) {
newOrg.set('DateFormat', request.params.DateFormat);
}
if (request.params.Is12HourTime !== undefined) {
newOrg.set('Is12HourTime', request.params.Is12HourTime);
}
const updateUserRes = await newOrg.save(null, { useMasterKey: true }); const updateUserRes = await newOrg.save(null, { useMasterKey: true });
if (updateUserRes) { if (updateUserRes) {
const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes)); const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes));
@@ -1,27 +0,0 @@
export default async function updateTenant(request) {
const { tenantId, details } = request.params;
if (!tenantId || !details) {
throw new Parse.Error(400, 'Missing tenantId or details.');
}
if (!request.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized');
}
try {
const tenant = new Parse.Object('partners_Tenant');
tenant.id = tenantId;
// Update tenant details
Object.keys(details).forEach(key => {
tenant.set(key, details?.[key]);
});
const tenantRes = await tenant.save(null, { useMasterKey: true });
if (tenantRes) {
const res = JSON.parse(JSON.stringify(tenantRes));
return res;
}
} catch (error) {
throw error;
}
}
@@ -1,5 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js'; import {
cloudServerUrl,
} from '../../Utils.js';
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;
@@ -30,7 +32,7 @@ async function saveUser(userDetails) {
const user = new Parse.User(); const user = new Parse.User();
user.set('username', userDetails.email); user.set('username', userDetails.email);
user.set('password', userDetails.password); user.set('password', userDetails.password);
user.set('email', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); user.set('email', userDetails.email);
if (userDetails?.phone) { if (userDetails?.phone) {
user.set('phone', userDetails.phone); user.set('phone', userDetails.phone);
} }
@@ -71,7 +73,7 @@ export default async function usersignup(request) {
partnerQuery.set('ContactNumber', userDetails.phone); partnerQuery.set('ContactNumber', userDetails.phone);
} }
partnerQuery.set('TenantName', userDetails.company); partnerQuery.set('TenantName', userDetails.company);
partnerQuery.set('EmailAddress', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true); partnerQuery.set('IsActive', true);
partnerQuery.set('CreatedBy', { partnerQuery.set('CreatedBy', {
__type: 'Pointer', __type: 'Pointer',
@@ -103,7 +105,7 @@ export default async function usersignup(request) {
objectId: user.id, objectId: user.id,
}); });
newObj.set('UserRole', userDetails.role); newObj.set('UserRole', userDetails.role);
newObj.set('Email', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); newObj.set('Email', userDetails.email);
newObj.set('Name', userDetails.name); newObj.set('Name', userDetails.name);
if (userDetails?.phone) { if (userDetails?.phone) {
newObj.set('Phone', userDetails?.phone); newObj.set('Phone', userDetails?.phone);
@@ -129,3 +131,4 @@ export default async function usersignup(request) {
console.log('Err ', err); console.log('Err ', err);
} }
} }
@@ -1,21 +0,0 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.addPointer('TemplateId', 'contracts_Template');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.deleteField('TemplateId');
return schema.update();
};
+12 -2
View File
@@ -15,6 +15,7 @@ import AWS from 'aws-sdk';
import { app as customRoute } from './cloud/customRoute/customApp.js'; import { app as customRoute } from './cloud/customRoute/customApp.js';
import { exec } from 'child_process'; import { exec } from 'child_process';
import { createTransport } from 'nodemailer'; import { createTransport } from 'nodemailer';
import { PostHog } from 'posthog-node';
import { appName, cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js'; import { appName, cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js';
import { SSOAuth } from './auth/authadapter.js'; import { SSOAuth } from './auth/authadapter.js';
import createContactIndex from './migrationdb/createContactIndex.js'; import createContactIndex from './migrationdb/createContactIndex.js';
@@ -160,8 +161,6 @@ app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(function (req, res, next) { app.use(function (req, res, next) {
req.headers['x-real-ip'] = getUserIP(req); req.headers['x-real-ip'] = getUserIP(req);
const publicUrl = req?.protocol + '://' + req?.get('host');
req.headers['public_url'] = publicUrl; // process.env.PUBLIC_URL
next(); next();
}); });
function getUserIP(request) { function getUserIP(request) {
@@ -176,6 +175,16 @@ function getUserIP(request) {
return request.socket.remoteAddress; return request.socket.remoteAddress;
} }
} }
app.use(function (req, res, next) {
const ph_project_api_key = process.env.PH_PROJECT_API_KEY;
try {
req.posthog = new PostHog(ph_project_api_key);
} catch (err) {
// console.log('Err', err);
req.posthog = '';
}
next();
});
app.use(async function (req, res, next) { app.use(async function (req, res, next) {
const isFilePath = req.path.includes('files') || false; const isFilePath = req.path.includes('files') || false;
@@ -216,6 +225,7 @@ if (!process.env.TESTING) {
// Mount your custom express app // Mount your custom express app
app.use('/', customRoute); app.use('/', customRoute);
// Parse Server plays nicely with the rest of your web routes // Parse Server plays nicely with the rest of your web routes
app.get('/', function (req, res) { app.get('/', function (req, res) {
res.status(200).send('opensign-server is running !!!'); res.status(200).send('opensign-server is running !!!');
+2099 -2352
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -18,26 +18,26 @@
"watch": "nodemon index.js" "watch": "nodemon index.js"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.775.0", "@aws-sdk/client-s3": "^3.741.0",
"@aws-sdk/s3-request-presigner": "^3.775.0", "@aws-sdk/s3-request-presigner": "^3.741.0",
"@parse/fs-files-adapter": "^3.0.0", "@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^4.1.0", "@parse/s3-files-adapter": "^4.1.0",
"@pdf-lib/fontkit": "^1.1.1", "@pdf-lib/fontkit": "^1.1.1",
"@signpdf/placeholder-pdf-lib": "^3.2.4", "@signpdf/placeholder-pdf-lib": "^3.2.4",
"@signpdf/signer-p12": "^3.2.4", "@signpdf/signer-p12": "^3.2.4",
"@signpdf/signpdf": "^3.2.5", "@signpdf/signpdf": "^3.2.4",
"aws-sdk": "^2.1692.0", "aws-sdk": "^2.1692.0",
"axios": "^1.8.4", "axios": "^1.7.9",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns-tz": "^3.2.0", "date-fns-tz": "^3.2.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"express": "^4.21.1", "express": "^4.21.1",
"form-data": "^4.0.2", "form-data": "^4.0.1",
"generate-api-key": "^1.0.2", "generate-api-key": "^1.0.2",
"googleapis": "^148.0.0", "googleapis": "^144.0.0",
"mailgun.js": "^11.1.0", "mailgun.js": "^11.1.0",
"mongodb": "^6.15.0", "mongodb": "^6.13.0",
"multer": "^1.4.5-lts.2", "multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1", "multer-s3": "^3.0.1",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"nodemailer": "^6.10.0", "nodemailer": "^6.10.0",
@@ -46,18 +46,18 @@
"parse-server": "^7.4.0", "parse-server": "^7.4.0",
"parse-server-api-mail-adapter": "^4.1.0", "parse-server-api-mail-adapter": "^4.1.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"posthog-node": "^4.10.2", "posthog-node": "^4.5.0",
"ws": "^8.18.1" "ws": "^8.18.0"
}, },
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.27.0", "@babel/eslint-parser": "^7.26.5",
"eslint": "^9.23.0", "eslint": "^9.20.0",
"jasmine": "^5.6.0", "jasmine": "^5.6.0",
"mongodb-runner": "^5.8.0", "mongodb-runner": "^5.7.1",
"nodemon": "^3.1.9", "nodemon": "^3.1.9",
"nyc": "^17.1.0", "nyc": "^17.1.0",
"prettier": "^3.5.3" "prettier": "^3.5.0"
}, },
"overrides": { "overrides": {
"ws": "$ws", "ws": "$ws",