mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
Generated
+16871
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "open_sign",
|
||||
"version": "2.34.0",
|
||||
"version": "2.35.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@imgly/background-removal": "^1.7.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.17.10",
|
||||
"@lottiefiles/dotlottie-react": "^0.17.12",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
"@reactour/tour": "^3.8.0",
|
||||
@@ -30,7 +30,7 @@
|
||||
"react": "^19.2.3",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-datepicker": "^9.0.0",
|
||||
"react-datepicker": "^9.1.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
@@ -39,10 +39,10 @@
|
||||
"react-i18next": "^16.5.0",
|
||||
"react-konva": "^19.2.1",
|
||||
"react-pdf": "^10.2.0",
|
||||
"react-quill-new": "^3.6.0",
|
||||
"react-quill-new": "^3.7.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router": "^7.10.1",
|
||||
"react-router": "^7.11.0",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.2",
|
||||
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||
@@ -102,7 +102,7 @@
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/react": "^19.2.7",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitest/ui": "^4.0.15",
|
||||
"@vitest/ui": "^4.0.16",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"babel-loader": "^10.0.0",
|
||||
"commitizen": "^4.3.1",
|
||||
@@ -112,7 +112,7 @@
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"jsdom": "^27.3.0",
|
||||
"jsdom": "^27.4.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.7.4",
|
||||
@@ -122,8 +122,8 @@
|
||||
"tmp": "^0.2.5",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-svgr": "^4.5.0",
|
||||
"vite-tsconfig-paths": "^6.0.1",
|
||||
"vitest": "^4.0.15"
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16"
|
||||
},
|
||||
"overrides": {
|
||||
"tmp": "$tmp"
|
||||
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "Radiobutton",
|
||||
"image": "Bild",
|
||||
"email": "E-Mail",
|
||||
"number": "Zahl"
|
||||
"number": "Zahl",
|
||||
"draw": "Zeichnen"
|
||||
},
|
||||
"fields": "Felder",
|
||||
"recipients": "Empfänger",
|
||||
@@ -516,7 +517,7 @@
|
||||
"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-you": "Nachfolgende Unterzeichner erhalten E-Mails, sobald Sie das Dokument unterschreiben.",
|
||||
"placeholder-alert-5": "Möchten Sie die Dokumente jetzt unterschreiben?",
|
||||
"placeholder-alert-5": "Sie können jetzt unterschreiben oder es an Ihre E-Mail senden, um es später abzuschließen.",
|
||||
"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!",
|
||||
"email-subject": "E-Mail-Betreff",
|
||||
@@ -1243,6 +1244,7 @@
|
||||
},
|
||||
"read-only-date-error": "Das schreibgeschützte Datums-Widget muss ein Standarddatum haben.",
|
||||
"set-date": "Datum festlegen",
|
||||
"default-date": "Standarddatum",
|
||||
"set-today": "Unterzeichnungsdatum",
|
||||
"enter-name": "Name eingeben",
|
||||
"enter-email": "E-Mail eingeben",
|
||||
@@ -1328,5 +1330,15 @@
|
||||
"working": "In Bearbeitung",
|
||||
"test-security-key": "Sicherheitsschlüssel testen",
|
||||
"generate": "Generieren",
|
||||
"regenerate": "Neu generieren"
|
||||
"regenerate": "Neu generieren",
|
||||
"signing-date": "Signierdatum",
|
||||
"date-widget": "Datums-Widget",
|
||||
"mail-failed": "Beim Senden der E-Mail ist etwas schiefgelaufen. Bitte versuchen Sie es erneut.",
|
||||
"mail-status-head": "Nächster Schritt: Ihr Dokument unterschreiben?",
|
||||
"send-to-email": "Per E-Mail senden",
|
||||
"date-pref-help-sub-title": "Mit den Datums-Widget-Einstellungen können Sie die Standardkonfiguration für das Datums-Widget festlegen. Diese Standards werden automatisch angewendet, wenn Sie ein neues Datums-Widget zu einem Dokument oder einer Vorlage hinzufügen. Sie können jedes Datums-Widget jederzeit individuell anpassen, indem Sie auf das Zahnradsymbol klicken.",
|
||||
"date-pref-help-format": "Wählen Sie das standardmäßige Datumsformat für Datums-Widgets.",
|
||||
"date-pref-help-default-date": "Legen Sie einen Standard-Datumswert für Datums-Widgets fest.",
|
||||
"date-pref-help-signing-date": "Wenn aktiviert, verwendet das Datums-Widget automatisch das Datum, an dem der Unterzeichner das Dokument signiert.",
|
||||
"date-pref-help-read-only": "Wenn aktiviert, ist das Datums-Widget für den Unterzeichner schreibgeschützt."
|
||||
}
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "radio button",
|
||||
"image": "image",
|
||||
"email": "email",
|
||||
"number": "number"
|
||||
"number": "number",
|
||||
"draw": "draw"
|
||||
},
|
||||
"fields": "Fields",
|
||||
"recipients": "Recipients",
|
||||
@@ -517,7 +518,7 @@
|
||||
"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": "Subsequent signers will get email(s) once you sign the document.",
|
||||
"placeholder-alert-5": "Do you want to sign the document right now?",
|
||||
"placeholder-alert-5": "You can sign now or send it to your email to complete later.",
|
||||
"placeholder-alert-6": "Please setup mail adapter to send mail!",
|
||||
"placeholder-alert-7": "Please select signer for add placeholder!",
|
||||
"email-subject": "email subject",
|
||||
@@ -1244,6 +1245,7 @@
|
||||
},
|
||||
"read-only-date-error": "Read-only date widget must have a default date.",
|
||||
"set-date": "Set Date",
|
||||
"default-date": "Default Date",
|
||||
"set-today": "Signing Date",
|
||||
"enter-name": "Enter name",
|
||||
"enter-email": "Enter email",
|
||||
@@ -1329,5 +1331,15 @@
|
||||
"working": "Working",
|
||||
"test-security-key": "Test security key",
|
||||
"generate": "Generate",
|
||||
"regenerate": "Regenerate"
|
||||
"regenerate": "Regenerate",
|
||||
"signing-date": "Signing Date",
|
||||
"date-widget": "Date Widget",
|
||||
"mail-failed": "Something went wrong while sending the email. Please try again.",
|
||||
"mail-status-head": "Next step: Sign your document ?",
|
||||
"send-to-email": "Send to Email",
|
||||
"date-pref-help-sub-title": "Date Widget Preferences let you define the default settings for the Date widget. These defaults are automatically applied whenever you add a new Date widget to a document or template. You can still customize any individual Date widget at any time by clicking the gear icon.",
|
||||
"date-pref-help-format": "Choose the default date format used for Date widgets.",
|
||||
"date-pref-help-default-date": "Set a default date value for Date widgets.",
|
||||
"date-pref-help-signing-date": "When enabled, the Date widget automatically uses the date the signer signs the document.",
|
||||
"date-pref-help-read-only": "When enabled, the Date widget is read-only for the signer."
|
||||
}
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo",
|
||||
"number": "número"
|
||||
"number": "número",
|
||||
"draw": "Dibujar"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
@@ -517,7 +518,7 @@
|
||||
"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-you": "Los firmantes siguientes recibirán un correo electrónico una vez que usted firme el documento.",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-5": "Puede firmar ahora o enviarlo a su correo electrónico para completarlo más tarde.",
|
||||
"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!",
|
||||
"email-subject": "asunto del correo",
|
||||
@@ -1244,6 +1245,7 @@
|
||||
},
|
||||
"read-only-date-error": "El widget de fecha de solo lectura debe tener una fecha predeterminada.",
|
||||
"set-date": "Establecer fecha",
|
||||
"default-date": "Fecha predeterminada",
|
||||
"set-today": "Fecha de firma",
|
||||
"enter-name": "Ingrese nombre",
|
||||
"enter-email": "Ingrese correo electrónico",
|
||||
@@ -1329,5 +1331,15 @@
|
||||
"working": "En funcionamiento",
|
||||
"test-security-key": "Probar clave de seguridad",
|
||||
"generate": "Generar",
|
||||
"regenerate": "Regenerar"
|
||||
"regenerate": "Regenerar",
|
||||
"signing-date": "Fecha de firma",
|
||||
"date-widget": "Widget de fecha",
|
||||
"mail-failed": "Algo salió mal al enviar el correo electrónico. Por favor, inténtelo de nuevo.",
|
||||
"mail-status-head": "Siguiente paso: ¿Firmar su documento?",
|
||||
"send-to-email": "Enviar por correo electrónico",
|
||||
"date-pref-help-sub-title": "Las preferencias del widget de fecha le permiten definir la configuración predeterminada del widget de fecha. Estos valores se aplican automáticamente cada vez que añade un nuevo widget de fecha a un documento o plantilla. Aún puede personalizar cualquier widget de fecha individual en cualquier momento haciendo clic en el icono de engranaje.",
|
||||
"date-pref-help-format": "Elija el formato de fecha predeterminado utilizado para los widgets de fecha.",
|
||||
"date-pref-help-default-date": "Establezca un valor de fecha predeterminado para los widgets de fecha.",
|
||||
"date-pref-help-signing-date": "Cuando está habilitado, el widget de fecha utiliza automáticamente la fecha en que el firmante firma el documento.",
|
||||
"date-pref-help-read-only": "Cuando está habilitado, el widget de fecha es de solo lectura para el firmante."
|
||||
}
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "bouton radio",
|
||||
"image": "image",
|
||||
"email": "e-mail",
|
||||
"number": "nombre"
|
||||
"number": "nombre",
|
||||
"draw": "Dessiner"
|
||||
},
|
||||
"fields": "Des champs",
|
||||
"recipients": "Destinataires",
|
||||
@@ -516,7 +517,7 @@
|
||||
"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-you": "Les signataires suivants recevront un e-mail une fois que vous aurez signé le document.",
|
||||
"placeholder-alert-5": "Voulez-vous signer des documents maintenant ?",
|
||||
"placeholder-alert-5": "Vous pouvez signer maintenant ou l’envoyer à votre e-mail pour le terminer plus tard.",
|
||||
"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é !",
|
||||
"email-subject": "Sujet du courriel",
|
||||
@@ -1243,6 +1244,7 @@
|
||||
},
|
||||
"read-only-date-error": "Le widget de date en lecture seule doit avoir une date par défaut.",
|
||||
"set-date": "Définir la date",
|
||||
"default-date": "Date par défaut",
|
||||
"set-today": "Date de signature",
|
||||
"enter-name": "Saisir le nom",
|
||||
"enter-email": "Saisir l'e-mail",
|
||||
@@ -1328,5 +1330,15 @@
|
||||
"working": "En cours",
|
||||
"test-security-key": "Tester la clé de sécurité",
|
||||
"generate": "Générer",
|
||||
"regenerate": "Régénérer"
|
||||
"regenerate": "Régénérer",
|
||||
"signing-date": "Date de signature",
|
||||
"date-widget": "Widget de date",
|
||||
"mail-failed": "Une erreur s'est produite lors de l'envoi de l'e-mail. Veuillez réessayer.",
|
||||
"mail-status-head": "Étape suivante : signer votre document ?",
|
||||
"send-to-email": "Envoyer par e-mail",
|
||||
"date-pref-help-sub-title": "Les préférences du widget de date vous permettent de définir les paramètres par défaut du widget de date. Ces paramètres sont automatiquement appliqués lorsque vous ajoutez un nouveau widget de date à un document ou à un modèle. Vous pouvez toujours personnaliser chaque widget de date individuellement en cliquant sur l'icône d'engrenage.",
|
||||
"date-pref-help-format": "Choisissez le format de date par défaut utilisé pour les widgets de date.",
|
||||
"date-pref-help-default-date": "Définissez une valeur de date par défaut pour les widgets de date.",
|
||||
"date-pref-help-signing-date": "Lorsqu'elle est activée, le widget de date utilise automatiquement la date à laquelle le signataire signe le document.",
|
||||
"date-pref-help-read-only": "Lorsqu'elle est activée, le widget de date est en lecture seule pour le signataire."
|
||||
}
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "रेडियो बटन",
|
||||
"image": "छवि",
|
||||
"email": "ईमेल",
|
||||
"number": "संख्या"
|
||||
"number": "संख्या",
|
||||
"draw": "आरेखित करें"
|
||||
},
|
||||
"fields": "क्षेत्र",
|
||||
"recipients": "प्राप्तकर्ता",
|
||||
@@ -516,7 +517,7 @@
|
||||
"placeholder-alert-4": "आपने सभी प्राप्तकर्ताओं को सफलतापूर्वक मेल भेज दिए हैं!",
|
||||
"placeholder-mail-alert": "आपने {{name}} को सफलतापूर्वक ईमेल भेज दिया है। {{name}} द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे",
|
||||
"placeholder-mail-alert-you": "आपके दस्तावेज़ पर हस्ताक्षर करने के बाद अगले हस्ताक्षरकर्ता को ईमेल प्राप्त होगा।",
|
||||
"placeholder-alert-5": "क्या आप अभी दस्तावेज़ पर हस्ताक्षर करना चाहते हैं?",
|
||||
"placeholder-alert-5": "आप अभी हस्ताक्षर कर सकते हैं या इसे बाद में पूरा करने के लिए अपने ईमेल पर भेज सकते हैं।",
|
||||
"placeholder-alert-6": "मेल भेजने के लिए कृपया मेल एडॉप्टर सेटअप करें!",
|
||||
"placeholder-alert-7": "प्लेसहोल्डर जोड़ने के लिए कृपया हस्ताक्षरकर्ता चुनें!",
|
||||
"email-subject": "ईमेल विषय",
|
||||
@@ -1243,6 +1244,7 @@
|
||||
},
|
||||
"read-only-date-error": "रीड-ओनली दिनांक विजेट में एक डिफ़ॉल्ट दिनांक होना आवश्यक है।",
|
||||
"set-date": "दिनांक सेट करें",
|
||||
"default-date": "डिफ़ॉल्ट तिथि",
|
||||
"set-today": "हस्ताक्षर की तिथि",
|
||||
"enter-name": "नाम दर्ज करें",
|
||||
"enter-email": "ईमेल दर्ज करें",
|
||||
@@ -1328,5 +1330,15 @@
|
||||
"working": "कार्यरत",
|
||||
"test-security-key": "सुरक्षा कुंजी का परीक्षण करें",
|
||||
"generate": "जनरेट करें",
|
||||
"regenerate": "पुनः जनरेट करें"
|
||||
"regenerate": "पुनः जनरेट करें",
|
||||
"signing-date": "हस्ताक्षर तिथि",
|
||||
"date-widget": "तिथि विजेट",
|
||||
"mail-failed": "ईमेल भेजते समय कुछ गलत हो गया। कृपया पुनः प्रयास करें।",
|
||||
"mail-status-head": "अगला कदम: अपने दस्तावेज़ पर हस्ताक्षर करें?",
|
||||
"send-to-email": "ईमेल पर भेजें",
|
||||
"date-pref-help-sub-title": "डेट विजेट प्राथमिकताएँ आपको डेट विजेट के लिए डिफ़ॉल्ट सेटिंग्स निर्धारित करने की अनुमति देती हैं। जब भी आप किसी दस्तावेज़ या टेम्पलेट में नया डेट विजेट जोड़ते हैं, ये डिफ़ॉल्ट सेटिंग्स स्वतः लागू हो जाती हैं। आप गियर आइकन पर क्लिक करके किसी भी डेट विजेट को कभी भी अनुकूलित कर सकते हैं।",
|
||||
"date-pref-help-format": "डेट विजेट के लिए उपयोग किए जाने वाले डिफ़ॉल्ट तिथि प्रारूप का चयन करें।",
|
||||
"date-pref-help-default-date": "डेट विजेट के लिए एक डिफ़ॉल्ट तिथि मान सेट करें।",
|
||||
"date-pref-help-signing-date": "सक्षम होने पर, डेट विजेट स्वतः उस तिथि का उपयोग करता है जब साइनर दस्तावेज़ पर हस्ताक्षर करता है।",
|
||||
"date-pref-help-read-only": "सक्षम होने पर, डेट विजेट साइनर के लिए केवल-पढ़ने योग्य होता है।"
|
||||
}
|
||||
@@ -393,7 +393,8 @@
|
||||
"radio button": "pulsante di opzione",
|
||||
"image": "immagine",
|
||||
"email": "email",
|
||||
"number": "numero"
|
||||
"number": "numero",
|
||||
"draw": "Disegno"
|
||||
},
|
||||
"fields": "Campi",
|
||||
"recipients": "Destinatari",
|
||||
@@ -516,7 +517,7 @@
|
||||
"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-you": "I firmatari successivi riceveranno un'email dopo che avrai firmato il documento.",
|
||||
"placeholder-alert-5": "Vuoi firmare i documenti ora?",
|
||||
"placeholder-alert-5": "Puoi firmare ora oppure inviarlo alla tua email per completarlo in seguito.",
|
||||
"placeholder-alert-6": "Configura l'adattatore email per inviare mail!",
|
||||
"placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!",
|
||||
"email-subject": "Oggetto email",
|
||||
@@ -1243,6 +1244,7 @@
|
||||
},
|
||||
"read-only-date-error": "Il widget data di sola lettura deve avere una data predefinita.",
|
||||
"set-date": "Imposta data",
|
||||
"default-date": "Data predefinita",
|
||||
"set-today": "Data di firma",
|
||||
"enter-name": "Inserisci nome",
|
||||
"enter-email": "Inserisci e-mail",
|
||||
@@ -1328,5 +1330,15 @@
|
||||
"working": "In funzione",
|
||||
"test-security-key": "Testa la chiave di sicurezza",
|
||||
"generate": "Genera",
|
||||
"regenerate": "Rigenera"
|
||||
"regenerate": "Rigenera",
|
||||
"signing-date": "Data di firma",
|
||||
"date-widget": "Widget data",
|
||||
"mail-failed": "Si è verificato un errore durante l'invio dell'e-mail. Per favore riprova.",
|
||||
"mail-status-head": "Passaggio successivo: firmare il tuo documento?",
|
||||
"send-to-email": "Invia tramite email",
|
||||
"date-pref-help-sub-title": "Le preferenze del widget Data consentono di definire le impostazioni predefinite per il widget Data. Queste impostazioni vengono applicate automaticamente ogni volta che si aggiunge un nuovo widget Data a un documento o a un modello. È comunque possibile personalizzare qualsiasi widget Data in qualsiasi momento facendo clic sull'icona a forma di ingranaggio.",
|
||||
"date-pref-help-format": "Scegli il formato di data predefinito utilizzato per i widget Data.",
|
||||
"date-pref-help-default-date": "Imposta un valore di data predefinito per i widget Data.",
|
||||
"date-pref-help-signing-date": "Quando abilitato, il widget Data utilizza automaticamente la data in cui il firmatario firma il documento.",
|
||||
"date-pref-help-read-only": "Quando abilitato, il widget Data è in sola lettura per il firmatario."
|
||||
}
|
||||
Binary file not shown.
@@ -11,6 +11,8 @@ import {
|
||||
import {
|
||||
useTranslation
|
||||
} from "react-i18next";
|
||||
import { withSessionValidation } from "../utils";
|
||||
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
@@ -67,7 +69,7 @@ const AddUser = (props) => {
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
const handleSubmit = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
@@ -130,7 +132,7 @@ const AddUser = (props) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DateFormat = ({ selectDate, dateFormatList, handleChangeFormat }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:items-center md:flex-row gap-y-3 my-2">
|
||||
<span className="capitalize">{t("format")}: </span>
|
||||
<select
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs md:ml-2"
|
||||
defaultValue={""}
|
||||
onChange={(e) => handleChangeFormat(e)}
|
||||
>
|
||||
<option value="" disabled>
|
||||
{t("select-date-format")}
|
||||
</option>
|
||||
{dateFormatList.map((data, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={ind} key={ind}>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span className="text-xs text-gray-400 ml-1 uppercase">{selectDate.format}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateFormat;
|
||||
@@ -0,0 +1,86 @@
|
||||
import { forwardRef } from "react";
|
||||
import ReactDatePicker from "react-datepicker";
|
||||
import {
|
||||
getDefaultDate,
|
||||
getMonth,
|
||||
getYear,
|
||||
months,
|
||||
years
|
||||
} from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DatePicker = ({
|
||||
selectDate,
|
||||
format,
|
||||
minDate,
|
||||
maxDate,
|
||||
onChange,
|
||||
handleClear
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const CustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className="border-gray-400 rounded-[50px] border-[1px] px-3 text-xs py-2 focus:outline-none hover:border-base-content "
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className={`${value ? "ml-[5px]" : ""} fa-light fa-calendar `}></i>
|
||||
</div>
|
||||
));
|
||||
CustomInput.displayName = "CustomInput";
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span>{t("default-date")}: </span>
|
||||
<ReactDatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
<div className="flex justify-start md:ml-2">
|
||||
<select
|
||||
className="bg-transparent outline-none"
|
||||
value={months[getMonth(date)]}
|
||||
onChange={({ target: { value } }) =>
|
||||
changeMonth(months.indexOf(value))
|
||||
}
|
||||
>
|
||||
{months.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
className="bg-transparent outline-none"
|
||||
value={getYear(date)}
|
||||
onChange={({ target: { value } }) => changeYear(value)}
|
||||
>
|
||||
{years.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
closeOnScroll={true}
|
||||
selected={getDefaultDate(selectDate?.date, selectDate?.format)}
|
||||
popperPlacement="top-end"
|
||||
customInput={<CustomInput />}
|
||||
onChange={(date) => onChange(date)}
|
||||
dateFormat={
|
||||
selectDate ? selectDate?.format : format ? format : "MM/dd/yyyy"
|
||||
}
|
||||
portalId="root-portal"
|
||||
/>
|
||||
<span
|
||||
onClick={() => handleClear()}
|
||||
className="underline text-blue-500 cursor-pointer ml-2"
|
||||
>
|
||||
{t("clear")}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
@@ -5,6 +5,7 @@ import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||
import { useNavigate } from "react-router";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
const DashboardCard = (props) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -14,7 +15,7 @@ const DashboardCard = (props) => {
|
||||
const [response, setresponse] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const renderData = async () => {
|
||||
const renderData = withSessionValidation(async () => {
|
||||
if (props.Data.queryType === "function") {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -186,9 +187,9 @@ const DashboardCard = (props) => {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const filterRender = async () => {
|
||||
const filterRender = withSessionValidation(async () => {
|
||||
if (props.FilterData && props.FilterData.queryType === "function") {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -266,7 +267,7 @@ const DashboardCard = (props) => {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const setFormat = (val) => {
|
||||
switch (props.Format) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import reportJson from "../../json/ReportJson";
|
||||
import axios from "axios";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
function DashboardReport(props) {
|
||||
const { t } = useTranslation();
|
||||
const [List, setList] = useState([]);
|
||||
@@ -46,7 +47,7 @@ function DashboardReport(props) {
|
||||
// eslint-disable-next-line
|
||||
}, [isNextRecord]);
|
||||
|
||||
const handleSearchChange = async (e) => {
|
||||
const handleSearchChange = withSessionValidation(async (e) => {
|
||||
const term = e.target.value.toLowerCase();
|
||||
setSearchTerm(term);
|
||||
if (debounceTimer.current) {
|
||||
@@ -82,7 +83,7 @@ function DashboardReport(props) {
|
||||
}
|
||||
}, 300);
|
||||
setIsSearchResult(false);
|
||||
};
|
||||
});
|
||||
|
||||
const handleSearchPaste = (e) => {
|
||||
setTimeout(() => {
|
||||
@@ -98,96 +99,95 @@ function DashboardReport(props) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getReportData = async (
|
||||
id,
|
||||
skipUserRecord = 0,
|
||||
limit = 20,
|
||||
term = searchTerm
|
||||
) => {
|
||||
setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
setActions(json.actions);
|
||||
setReportName(json.reportName);
|
||||
setHeading(json.heading);
|
||||
const currentUser = Parse.User.current().id;
|
||||
const getReportData = withSessionValidation(
|
||||
async (id, skipUserRecord = 0, limit = 20, term = searchTerm) => {
|
||||
setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
setActions(json.actions);
|
||||
setReportName(json.reportName);
|
||||
setHeading(json.heading);
|
||||
const currentUser = Parse.User.current().id;
|
||||
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const skipRecord = id === "5Go51Q7T8r" ? 0 : skipUserRecord;
|
||||
const limitRecord = id === "5Go51Q7T8r" ? 200 : limit;
|
||||
const params = { reportId: id, skip: skipRecord, limit: limitRecord };
|
||||
if (term) {
|
||||
params.searchTerm = term;
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
});
|
||||
if (id === "5Go51Q7T8r") {
|
||||
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj.Signers?.some(
|
||||
(item) => item.UserId.objectId === currentUser
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const skipRecord = id === "5Go51Q7T8r" ? 0 : skipUserRecord;
|
||||
const limitRecord = id === "5Go51Q7T8r" ? 200 : limit;
|
||||
const params = { reportId: id, skip: skipRecord, limit: limitRecord };
|
||||
if (term) {
|
||||
params.searchTerm = term;
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
});
|
||||
if (id === "5Go51Q7T8r") {
|
||||
const listData = res.data?.result.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj.Signers?.some(
|
||||
(item) => item.UserId.objectId === currentUser
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arr.length === docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
|
||||
);
|
||||
} else {
|
||||
if (res.data.result.length >= docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setIsNextRecord(false);
|
||||
if (!res.data.result.error) {
|
||||
if (arr.length === docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
|
||||
);
|
||||
} else {
|
||||
if (res.data.result.length >= docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setIsNextRecord(false);
|
||||
if (!res.data.result.error) {
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
const isCancel = axios.isCancel(err);
|
||||
if (!isCancel) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
const isCancel = axios.isCancel(err);
|
||||
if (!isCancel) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{isLoader ? (
|
||||
|
||||
@@ -10,6 +10,7 @@ import FolderModal from "../shared/fields/FolderModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { handleDownloadPdf, isMobile } from "../../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
function DriveBody(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -52,7 +53,7 @@ function DriveBody(props) {
|
||||
props.setSkip(0);
|
||||
};
|
||||
//function for change doc name and update doc name in _document class
|
||||
const handledRenameDoc = async (data) => {
|
||||
const handledRenameDoc = withSessionValidation(async (data) => {
|
||||
setRename("");
|
||||
const trimmedValue = renameValue.trim();
|
||||
if (trimmedValue.length > 0) {
|
||||
@@ -89,7 +90,7 @@ function DriveBody(props) {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
@@ -155,7 +156,7 @@ function DriveBody(props) {
|
||||
}
|
||||
};
|
||||
//function for delete document
|
||||
const handleDeleteDocument = async (docData) => {
|
||||
const handleDeleteDocument = withSessionValidation(async (docData) => {
|
||||
setIsDeleteDoc({});
|
||||
const docId = docData.objectId;
|
||||
const data = { IsArchive: true };
|
||||
@@ -186,13 +187,13 @@ function DriveBody(props) {
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
const handleMoveDocument = async (docData) => {
|
||||
setIsOpenMoveModal(true);
|
||||
setSelectDoc(docData);
|
||||
};
|
||||
//function for move document from one folder to another folder
|
||||
const handleMoveFolder = async (selectFolderData) => {
|
||||
const handleMoveFolder = withSessionValidation(async (selectFolderData) => {
|
||||
const selecFolderId = selectDoc?.Folder?.objectId;
|
||||
const moveFolderId = selectFolderData?.ObjectId;
|
||||
let updateDocId = selectDoc?.objectId;
|
||||
@@ -229,7 +230,6 @@ function DriveBody(props) {
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.then((Listdata) => {
|
||||
const res = Listdata.data;
|
||||
if (res) {
|
||||
@@ -248,7 +248,7 @@ function DriveBody(props) {
|
||||
alert(t("folder-already-exist!"));
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
|
||||
@@ -13,7 +13,6 @@ import { useNavigate } from "react-router";
|
||||
|
||||
function CustomizeMail(props) {
|
||||
const { t } = useTranslation();
|
||||
const editorRef = useRef();
|
||||
const navigate = useNavigate();
|
||||
const copyUrlRef = useRef(null);
|
||||
const [isCustomize, setIsCustomize] = useState(false);
|
||||
@@ -27,20 +26,17 @@ function CustomizeMail(props) {
|
||||
props?.setIsMailModal(false);
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
};
|
||||
const handleOnchangeRequest = () => {
|
||||
if (editorRef.current) {
|
||||
const html = editorRef.current.editor.root.innerHTML;
|
||||
props?.setCustomizeMail((prev) => ({
|
||||
...prev,
|
||||
body: html
|
||||
}));
|
||||
}
|
||||
const handleOnchangeRequest = (value) => {
|
||||
props?.setCustomizeMail((prev) => ({
|
||||
...prev,
|
||||
body: value
|
||||
}));
|
||||
};
|
||||
const handleEmailSendToSigners = async () => {
|
||||
setIsLoader(true);
|
||||
const documentData = await contractDocument(props?.documentId);
|
||||
if (documentData && documentData?.length > 0) {
|
||||
props?.setDocumentDetails(documentData[0]);
|
||||
props?.setDocumentDetails && props?.setDocumentDetails(documentData[0]);
|
||||
if (
|
||||
documentData?.[0]?.SendinOrder &&
|
||||
documentData?.[0]?.SendinOrder === true
|
||||
@@ -49,7 +45,7 @@ function CustomizeMail(props) {
|
||||
const ownerDetails = documentData[0].Signers.find(
|
||||
(x) => x.Email === ownerEmail
|
||||
);
|
||||
props?.setCurrUserId(ownerDetails?.objectId);
|
||||
props?.setCurrUserId && props?.setCurrUserId(ownerDetails?.objectId);
|
||||
}
|
||||
//function is used to send email to signers for sign the document
|
||||
const mailRes = await sendEmailToSigners(
|
||||
@@ -94,9 +90,8 @@ function CustomizeMail(props) {
|
||||
isCustomize && (
|
||||
<>
|
||||
<EmailBody
|
||||
editorRef={editorRef}
|
||||
requestBody={props?.customizeMail.body}
|
||||
requestSubject={props?.customizeMail.subject}
|
||||
requestBody={props?.customizeMail?.body}
|
||||
requestSubject={props?.customizeMail?.subject}
|
||||
handleOnchangeRequest={handleOnchangeRequest}
|
||||
setCustomizeMail={props?.setCustomizeMail}
|
||||
/>
|
||||
@@ -135,7 +130,7 @@ function CustomizeMail(props) {
|
||||
!isCustomize && (
|
||||
<span
|
||||
className="op-link op-link-accent text-sm"
|
||||
onClick={() => setIsCustomize(!isCustomize)}
|
||||
onClick={() => setIsCustomize(true)}
|
||||
>
|
||||
{t("cutomize-email")}
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { radioButtonWidget } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -270,7 +270,8 @@ function DropdownWidgetOption(props) {
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
{props.type !== "checkbox" && !isPrefillExist && (
|
||||
{((props.type !== "checkbox" && !isPrefillExist) ||
|
||||
isPrefillExist) && (
|
||||
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
|
||||
{statusArr.map((data, ind) => (
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import ReactQuill from "react-quill-new";
|
||||
import "../../styles/quill.css";
|
||||
@@ -37,7 +36,6 @@ export function EmailBody(props) {
|
||||
theme="snow"
|
||||
value={props.requestBody}
|
||||
placeholder={t("email-placeholder")}
|
||||
ref={props.editorRef}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={props.handleOnchangeRequest}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
handleToPrint,
|
||||
} from "../../constant/Utils";
|
||||
|
||||
@@ -92,23 +92,25 @@ function Placeholder(props) {
|
||||
|
||||
//function is used to convert date according to selected format
|
||||
const handleGetFormatDate = (data, isDateChange) => {
|
||||
const format =
|
||||
data?.format ||
|
||||
selectDate?.format ||
|
||||
props.pos?.options?.validation?.format;
|
||||
const isSpecialDateFormat =
|
||||
data?.format &&
|
||||
["dd-MM-yyyy", "dd.MM.yyyy", "dd/MM/yyyy"].includes(data?.format);
|
||||
["dd-MM-yyyy", "dd.MM.yyyy", "dd/MM/yyyy"].includes(format);
|
||||
let updateDate = data.date;
|
||||
let date = "";
|
||||
if (isSpecialDateFormat && updateDate) {
|
||||
date = isDateChange
|
||||
? moment(updateDate).format(changeDateToMomentFormat(data.format))
|
||||
? moment(updateDate).format(changeDateToMomentFormat(format))
|
||||
: updateDate;
|
||||
} else if (updateDate) {
|
||||
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
|
||||
const newDate = new Date(updateDate);
|
||||
date = moment(newDate.getTime()).format(
|
||||
changeDateToMomentFormat(data?.format)
|
||||
);
|
||||
date = moment(newDate.getTime()).format(changeDateToMomentFormat(format));
|
||||
}
|
||||
setSelectDate({ date: date, format: data?.format });
|
||||
setSelectDate({ date: date, format: format });
|
||||
return date;
|
||||
};
|
||||
const saveDateSetting = (data) => {
|
||||
@@ -125,6 +127,10 @@ function Placeholder(props) {
|
||||
const handleSaveDate = (data, isDateChange) => {
|
||||
const date = isToday ? "today" : handleGetFormatDate(data, isDateChange);
|
||||
//`onChangeInput` is used to save data related to date in a placeholder field
|
||||
const format =
|
||||
data?.format ||
|
||||
selectDate?.format ||
|
||||
props.pos?.options?.validation?.format;
|
||||
onChangeInput(
|
||||
date,
|
||||
props.pos,
|
||||
@@ -133,7 +139,7 @@ function Placeholder(props) {
|
||||
props.setXyPosition,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
data?.format,
|
||||
format,
|
||||
props.fontSize || props.pos?.options?.fontSize || 12,
|
||||
props.fontColor || props.pos?.options?.fontColor || "black",
|
||||
formdata,
|
||||
@@ -221,7 +227,7 @@ function Placeholder(props) {
|
||||
if (props?.currWidgetsDetails?.key === props.pos?.key) {
|
||||
props.handleLinkUser(props.data.Id);
|
||||
}
|
||||
props.setUniqueId(props.data.Id);
|
||||
props.setUniqueId && props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPosition.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
@@ -938,14 +944,14 @@ function Placeholder(props) {
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span className="text-xs text-gray-400 ml-1">
|
||||
<span className="text-xs text-gray-400 ml-1 uppercase">
|
||||
{selectDate.format}
|
||||
</span>
|
||||
</div>
|
||||
{props?.data?.Role !== "prefill" && props?.isPlaceholder && (
|
||||
<>
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-2">
|
||||
<span>{t("set-date")} :</span>
|
||||
<span>{t("default-date")} :</span>
|
||||
<DatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
<div className="flex justify-start md:ml-2">
|
||||
@@ -1021,37 +1027,36 @@ function Placeholder(props) {
|
||||
{t("clear")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-[10px]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({
|
||||
...formdata,
|
||||
status: data.toLowerCase()
|
||||
})
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px]">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center gap-[10px]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({
|
||||
...formdata,
|
||||
status: data.toLowerCase()
|
||||
})
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px]">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-y-2 md:gap-y-0 gap-x-2">
|
||||
<div className="flex flex-row items-center">
|
||||
<span className="capitalize">{t("font-size")} :</span>
|
||||
@@ -1096,9 +1101,10 @@ function Placeholder(props) {
|
||||
</div>
|
||||
</div>
|
||||
{props?.isPlaceholder && props?.data?.Role !== "prefill" && (
|
||||
<label className="flex items-center gap-1 cursor-pointer">
|
||||
<div className="flex items-center gap-1 w-max">
|
||||
<input
|
||||
name="isReadOnly"
|
||||
id="isReadOnly"
|
||||
type="checkbox"
|
||||
checked={formdata?.isReadOnly}
|
||||
className="op-checkbox op-checkbox-xs"
|
||||
@@ -1109,8 +1115,13 @@ function Placeholder(props) {
|
||||
})
|
||||
}
|
||||
/>
|
||||
<span className="capitalize ml-[2px]">{t("read-only")}</span>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="isReadOnly"
|
||||
className="capitalize ml-[2px] mb-0 cursor-pointer"
|
||||
>
|
||||
{t("read-only")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { handleCopyNextToWidget, randomId } from "../../constant/Utils";
|
||||
import {
|
||||
drawWidget,
|
||||
handleCopyNextToWidget,
|
||||
randomId
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setPrefillImg } from "../../redux/reducers/widgetSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const { prefillImg } = useSelector((state) => state.widget);
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const copyType = [
|
||||
@@ -151,12 +156,17 @@ function PlaceholderCopy(props) {
|
||||
if (
|
||||
filterSignerPosition[0]?.Role === "prefill" &&
|
||||
currentPlaceholder?.options?.response &&
|
||||
currentPlaceholder?.type === "image"
|
||||
(currentPlaceholder?.type === "image" ||
|
||||
currentPlaceholder?.type === drawWidget)
|
||||
) {
|
||||
const getPrefillImg = prefillImg?.find(
|
||||
(x) => x.id === currentPlaceholder?.key
|
||||
);
|
||||
const base64Img = getPrefillImg?.base64;
|
||||
dispatch(
|
||||
setPrefillImg({
|
||||
id: newId,
|
||||
base64: currentPlaceholder?.options?.response
|
||||
base64: base64Img
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -254,15 +264,20 @@ function PlaceholderCopy(props) {
|
||||
props.setXyPosition,
|
||||
props?.Id
|
||||
);
|
||||
if (
|
||||
if (
|
||||
filterSignerPosition[0]?.Role === "prefill" &&
|
||||
currentXYposition?.options?.response &&
|
||||
currentXYposition?.type === "image"
|
||||
(currentXYposition?.type === "image" ||
|
||||
currentXYposition?.type === drawWidget)
|
||||
) {
|
||||
const getPrefillImg = prefillImg?.find(
|
||||
(x) => x.id === currentXYposition?.key
|
||||
);
|
||||
const base64Img = getPrefillImg?.base64;
|
||||
dispatch(
|
||||
setPrefillImg({
|
||||
id: newId,
|
||||
base64: currentXYposition?.options?.response
|
||||
base64: base64Img
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
months,
|
||||
years,
|
||||
selectCheckbox,
|
||||
isBase64
|
||||
isBase64,
|
||||
drawWidget
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
@@ -463,7 +464,7 @@ function PlaceholderType(props) {
|
||||
) : (
|
||||
<div
|
||||
style={textWidgetStyle}
|
||||
className="select-none-cls overflow-hidden"
|
||||
className="select-none-cls overflow-hidden uppercase"
|
||||
>
|
||||
<span>
|
||||
{props?.selectDate?.format ||
|
||||
@@ -581,6 +582,34 @@ function PlaceholderType(props) {
|
||||
className="w-full resize-none overflow-hidden text-base-content item-center outline-none"
|
||||
/>
|
||||
);
|
||||
case drawWidget:
|
||||
return prefillImgLoad[props.pos?.key] ? (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-white/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
) : widgetValue ? (
|
||||
<img
|
||||
alt="signature"
|
||||
draggable="false"
|
||||
src={widgetValue}
|
||||
className={`${props.pos.signatureType !== "type" ? "object-contain" : ""} w-full h-full select-none-cls`}
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{formatWidgetName()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return props.pos.SignUrl ? (
|
||||
<div className="pointer-events-none">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import {
|
||||
getMonth,
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
changeDateToMomentFormat,
|
||||
convertBase64ToFile,
|
||||
generatePdfName,
|
||||
drawWidget,
|
||||
getBase64MimeType,
|
||||
isBase64
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
@@ -27,6 +29,8 @@ import {
|
||||
setPrefillImg
|
||||
} from "../../redux/reducers/widgetSlice";
|
||||
import * as utils from "../../utils";
|
||||
import Draw from "./tab/Draw";
|
||||
import PenColorComponent from "./tab/PenColorComponent";
|
||||
|
||||
const widgetTitle = "font-medium";
|
||||
|
||||
@@ -111,6 +115,8 @@ const ImageComponent = (props) => {
|
||||
function PrefillWidgetModal(props) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const canvasRefs = useRef({});
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
// Track already loaded image keys so they don't increment multiple times
|
||||
const loadedSet = useRef(new Set());
|
||||
const initializedRef = useRef(false); // prevent rerun on state updates
|
||||
@@ -164,6 +170,7 @@ function PrefillWidgetModal(props) {
|
||||
}, [props?.isPrefillModal]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
//function is used to save all image base64 in redux state to display prefill images
|
||||
const savePrefillImg = async () => {
|
||||
const prefillImg = await utils?.savePrefillImg(props.xyPosition);
|
||||
@@ -261,14 +268,15 @@ function PrefillWidgetModal(props) {
|
||||
handleWidgetDetails(position, newDate);
|
||||
};
|
||||
|
||||
const handleSavePrefillImg = async (widgetDetails) => {
|
||||
const handleSavePrefillImg = async (base64) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const imageName = generatePdfName(16);
|
||||
const imgType = image?.imgType || getBase64MimeType(base64);
|
||||
const imageUrl = await convertBase64ToFile(
|
||||
imageName,
|
||||
image.src,
|
||||
image.imgType
|
||||
base64 || image.src,
|
||||
imgType
|
||||
);
|
||||
if (imageUrl) {
|
||||
return imageUrl;
|
||||
@@ -284,7 +292,7 @@ function PrefillWidgetModal(props) {
|
||||
const getPlaceholder = getPrefill?.placeHolder;
|
||||
let imgUrl;
|
||||
if (widgetDetails?.type === "image") {
|
||||
imgUrl = await handleSavePrefillImg(widgetDetails);
|
||||
imgUrl = await handleSavePrefillImg(response);
|
||||
}
|
||||
const updatedData = getPlaceholder.map((page) => ({
|
||||
...page,
|
||||
@@ -382,7 +390,7 @@ function PrefillWidgetModal(props) {
|
||||
if (item.options.name === position.options.name) {
|
||||
return {
|
||||
...item,
|
||||
SignUrl: "",
|
||||
...(item.SignUrl !== undefined && { SignUrl: "" }),
|
||||
options: {
|
||||
...position.options,
|
||||
response: ""
|
||||
@@ -412,6 +420,23 @@ function PrefillWidgetModal(props) {
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = (drawImage, position) => {
|
||||
handleWidgetDetails(position, drawImage);
|
||||
};
|
||||
const getCanvasRef = (widgetId) => {
|
||||
if (!canvasRefs.current[widgetId]) {
|
||||
canvasRefs.current[widgetId] = React.createRef();
|
||||
}
|
||||
return canvasRefs.current[widgetId];
|
||||
};
|
||||
const clearCanvasById = (widgetId) => {
|
||||
const canvasRef = canvasRefs.current?.[widgetId];
|
||||
|
||||
if (canvasRef?.current) {
|
||||
canvasRef.current.clear();
|
||||
}
|
||||
};
|
||||
const handleWidgetType = (position, id) => {
|
||||
switch (position?.type) {
|
||||
case "checkbox":
|
||||
@@ -558,6 +583,34 @@ function PrefillWidgetModal(props) {
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case drawWidget:
|
||||
return (
|
||||
<div>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<Draw
|
||||
penColor={penColor}
|
||||
canvasRef={getCanvasRef(position.key)}
|
||||
currWidgetsDetails={position}
|
||||
handleSignatureChange={handleSignatureChange}
|
||||
prefillCls={"prefillCanvas"}
|
||||
/>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent
|
||||
penColor={penColor}
|
||||
setPenColor={setPenColor}
|
||||
/>
|
||||
<span
|
||||
onClick={() => {
|
||||
clearCanvasById(position?.key);
|
||||
handleClearImage(position);
|
||||
}}
|
||||
className="flex justify-start text-blue-500 underline cursor-pointer"
|
||||
>
|
||||
{t("clear")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return position?.SignUrl ? (
|
||||
<div className="pointer-events-none">
|
||||
@@ -577,6 +630,66 @@ function PrefillWidgetModal(props) {
|
||||
};
|
||||
|
||||
const handleEmbedPrefill = async (item) => {
|
||||
//checking is there any draw widget response have base64 url then generate that base64 to url and save it
|
||||
const prefillWidgets = props.xyPosition?.find((x) => x.Role === "prefill");
|
||||
let updatedXyPosition = [];
|
||||
if (prefillWidgets) {
|
||||
const isDrawWidget = (prefillWidgets.placeHolder ?? []).some((ph) =>
|
||||
(ph?.pos ?? []).some((p) => p?.type === drawWidget)
|
||||
);
|
||||
if (isDrawWidget) {
|
||||
const getPrefill = props.xyPosition.find((x) => x?.Role === "prefill");
|
||||
const getPlaceholder = getPrefill?.placeHolder ?? [];
|
||||
const updatedData = await Promise.all(
|
||||
getPlaceholder.map(async (page) => ({
|
||||
...page,
|
||||
pos: await Promise.all(
|
||||
page.pos.map(async (item) => {
|
||||
if (item?.type === drawWidget) {
|
||||
const widgetResponse = item?.options?.response;
|
||||
|
||||
// Skip if no response
|
||||
if (!widgetResponse) return item;
|
||||
|
||||
// If already URL, do not re-upload
|
||||
if (
|
||||
typeof widgetResponse === "string" &&
|
||||
widgetResponse.startsWith("http")
|
||||
) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// Convert THIS widget's base64 → URL
|
||||
const drawUrl = await handleSavePrefillImg(widgetResponse);
|
||||
return {
|
||||
...item,
|
||||
options: {
|
||||
...item.options,
|
||||
response: drawUrl
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
}))
|
||||
);
|
||||
|
||||
updatedXyPosition = props.xyPosition.map((obj) => {
|
||||
if (obj.Role === "prefill") {
|
||||
return {
|
||||
...obj,
|
||||
placeHolder: updatedData
|
||||
};
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
|
||||
props.setXyPosition(updatedXyPosition);
|
||||
}
|
||||
}
|
||||
|
||||
await props.handleCreateDocument();
|
||||
};
|
||||
//`loadOptions` function to use show all list of signer in dropdown
|
||||
@@ -584,9 +697,9 @@ function PrefillWidgetModal(props) {
|
||||
try {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsigners`;
|
||||
const token = {
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
textInputWidget,
|
||||
cellsWidget,
|
||||
textWidget,
|
||||
widgets
|
||||
widgets,
|
||||
drawWidget
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useWidgetDrag } from "../../hook/useWidgetDrag";
|
||||
@@ -34,7 +35,8 @@ function WidgetComponent(props) {
|
||||
{ id: 13, text: "checkbox" },
|
||||
{ id: 14, text: "dropdown" },
|
||||
{ id: 15, text: radioButtonWidget },
|
||||
{ id: 16, text: "image" }
|
||||
{ id: 16, text: "image" },
|
||||
{ id: 17, text: drawWidget }
|
||||
];
|
||||
|
||||
// Create all drag refs in one go
|
||||
@@ -65,7 +67,8 @@ function WidgetComponent(props) {
|
||||
textInputWidget,
|
||||
"date",
|
||||
"image",
|
||||
"checkbox"
|
||||
"checkbox",
|
||||
drawWidget
|
||||
].includes(data.type)
|
||||
);
|
||||
// allow only (signature, stamp, initials, text, cells) widget when isAllowModification true and user does not have session token
|
||||
@@ -81,18 +84,19 @@ function WidgetComponent(props) {
|
||||
"name",
|
||||
"email",
|
||||
"job title",
|
||||
"company"
|
||||
"company",
|
||||
drawWidget
|
||||
].includes(data.type)
|
||||
);
|
||||
const selfSignWidgets = widgetList.filter(
|
||||
(data) =>
|
||||
!["dropdown", radioButtonWidget, textInputWidget].includes(data.type)
|
||||
!["dropdown", radioButtonWidget, textInputWidget,drawWidget].includes(data.type)
|
||||
);
|
||||
//if user select prefill role then allow only date,image,text,checkbox,radio,dropdownAdd commentMore actions
|
||||
//dropdown widget should only be show in template flow
|
||||
const prefillAllowWidgets = widgetList.filter((data) =>
|
||||
(props.isPrefillDropdown ? ["dropdown"] : [])
|
||||
.concat([radioButtonWidget, textWidget, "date", "image", "checkbox"])
|
||||
.concat([radioButtonWidget, textWidget, "date", "image", "checkbox",drawWidget])
|
||||
.includes(data.type)
|
||||
);
|
||||
//function to show widget on the base of conditionAdd commentMore actions
|
||||
@@ -108,7 +112,7 @@ function WidgetComponent(props) {
|
||||
return unlogedInUserWidgets;
|
||||
}
|
||||
} else if (props?.roleName !== "prefill") {
|
||||
return widgetList.filter((data) => ![textWidget].includes(data.type));
|
||||
return widgetList.filter((data) => ![textWidget,drawWidget].includes(data.type));
|
||||
}
|
||||
};
|
||||
const handleSelectRecipient = () => {
|
||||
|
||||
@@ -310,8 +310,7 @@ const WidgetNameModal = (props) => {
|
||||
</>
|
||||
)}
|
||||
{!props?.isSelfSign &&
|
||||
!isSignOrInitials &&
|
||||
props?.roleName !== "prefill" && (
|
||||
!isSignOrInitials && (
|
||||
<div className={showFontControls ? "mb-[0.5rem]" : "mb-[0.75rem]"}>
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
|
||||
@@ -24,12 +24,13 @@ import {
|
||||
generatePdfName,
|
||||
getDefaultDate,
|
||||
getDefaultFormat,
|
||||
getBase64MimeType
|
||||
getBase64MimeType,
|
||||
drawWidget,
|
||||
getBase64FromUrl
|
||||
} from "../../constant/Utils";
|
||||
import CellsWidget from "./CellsWidget";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import moment from "moment";
|
||||
import {
|
||||
setSaveSignCheckbox,
|
||||
@@ -49,7 +50,8 @@ import { emailRegex } from "../../constant/const";
|
||||
import RegexParser from "regex-parser";
|
||||
import {
|
||||
saveToMySign,
|
||||
getInitials
|
||||
getInitials,
|
||||
isValidBase64
|
||||
} from "../../utils";
|
||||
import Draw from "./tab/Draw";
|
||||
import DefaultSignature from "./tab/DefaultSignature";
|
||||
@@ -159,7 +161,6 @@ function WidgetsValueModal(props) {
|
||||
const blockedTab = ["mysignature", "myinitials", "myStamp"];
|
||||
const showClearbtn =
|
||||
(!blocked.includes(type) && !blockedTab.includes(isTab));
|
||||
|
||||
const [cellsValue, setCellsValue] = useState(() => {
|
||||
const count = currWidgetsDetails?.options?.cellCount || 5;
|
||||
const val =
|
||||
@@ -231,7 +232,9 @@ function WidgetsValueModal(props) {
|
||||
}
|
||||
}
|
||||
else if (isSignOrInitials) {
|
||||
setIsTab(currWidgetsDetails?.signatureType);
|
||||
if (currWidgetsDetails?.signatureType) {
|
||||
setIsTab(currWidgetsDetails?.signatureType);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currWidgetsDetails]); // Added currWidgetsDetails to dependency array for reset logic
|
||||
@@ -298,17 +301,23 @@ function WidgetsValueModal(props) {
|
||||
compressedFileSize(file, setImage);
|
||||
}
|
||||
};
|
||||
const handleSavePrefillImg = async () => {
|
||||
const handleSavePrefillImg = async (drawImg) => {
|
||||
setIsLoader(true);
|
||||
|
||||
try {
|
||||
dispatch(
|
||||
setPrefillImg({ id: currWidgetsDetails?.key, base64: image?.src })
|
||||
setPrefillImg({
|
||||
id: currWidgetsDetails?.key,
|
||||
base64: drawImg || image?.src
|
||||
})
|
||||
);
|
||||
const imageName = generatePdfName(16);
|
||||
|
||||
const imgType = image?.imgType || getBase64MimeType(drawImg);
|
||||
const imageUrl = await convertBase64ToFile(
|
||||
imageName,
|
||||
image.src,
|
||||
image.imgType
|
||||
drawImg || image.src,
|
||||
imgType
|
||||
);
|
||||
setIsLoader(false);
|
||||
if (imageUrl) {
|
||||
@@ -411,13 +420,20 @@ function WidgetsValueModal(props) {
|
||||
? myInitial && (await convertJpegToPng(myInitial, "myinitials")) // default initials set through my signature tab
|
||||
: await convertJpegToPng(defaultSignImg, "mysign") // default signature set through my signature tab
|
||||
: signature; // signature done by user thorugh draw, upload, typed
|
||||
const signatureImg = signUrl;
|
||||
let signatureImg = signUrl;
|
||||
let imgWH = { width: width ? width : "", height: height ? height : "" };
|
||||
setIsImageSelect(false);
|
||||
setImage();
|
||||
// `isApplyAll` is used when user edit signature/initial then updated signature apply all existing drawn signatures
|
||||
const isApplyAll = true;
|
||||
if (uniqueId) {
|
||||
const isPrefill = xyPosition.some(
|
||||
(x) => x.Id === uniqueId && x?.Role === "prefill"
|
||||
);
|
||||
if (isPrefill) {
|
||||
dispatch(setPrefillImgLoad({ [currWidgetsDetails?.key]: true }));
|
||||
signatureImg = await handleSavePrefillImg(signatureImg);
|
||||
}
|
||||
setXyPosition((prevState) =>
|
||||
prevState.map((signer) => {
|
||||
if (signer.Id !== uniqueId) return signer;
|
||||
@@ -446,6 +462,7 @@ function WidgetsValueModal(props) {
|
||||
};
|
||||
})
|
||||
);
|
||||
dispatch(setPrefillImgLoad({}));
|
||||
} else {
|
||||
const index = props?.xyPosition?.findIndex((object) => {
|
||||
return object.pageNumber === pageNumber;
|
||||
@@ -535,7 +552,7 @@ function WidgetsValueModal(props) {
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
if (
|
||||
["signature", "initials", "stamp", "image"].includes(
|
||||
["signature", "initials", "stamp", "image", drawWidget].includes(
|
||||
currWidgetsDetails?.type
|
||||
)
|
||||
) {
|
||||
@@ -704,9 +721,25 @@ function WidgetsValueModal(props) {
|
||||
}, [fontSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
handleWidgetsResponse();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
|
||||
const handleWidgetsResponse = async () => {
|
||||
if (currWidgetsDetails?.options?.response) {
|
||||
let url = currWidgetsDetails?.options?.response;
|
||||
if (isSignOrInitials) {
|
||||
if (currWidgetsDetails?.type === drawWidget) {
|
||||
if (canvasRef.current) {
|
||||
const iBase64 = isValidBase64(url);
|
||||
if (iBase64) {
|
||||
canvasRef.current.fromDataURL(url);
|
||||
} else {
|
||||
const isAddSuffix = true;
|
||||
const base64Url = await getBase64FromUrl(url, isAddSuffix);
|
||||
canvasRef.current.fromDataURL(base64Url);
|
||||
}
|
||||
}
|
||||
} else if (isSignOrInitials) {
|
||||
if (isTab === "draw" && currWidgetsDetails?.signatureType === "draw") {
|
||||
setSignature(url);
|
||||
// Load the default signature after the component mounts
|
||||
@@ -756,9 +789,7 @@ function WidgetsValueModal(props) {
|
||||
setTypedSignature(signatureValue || userName || "");
|
||||
convertToImg(fontSelect, signatureValue || userName || "");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
|
||||
};
|
||||
// function for convert input text value in image
|
||||
const convertToImg = async (fontStyle, text, color) => {
|
||||
const maxWidth = currWidgetsDetails?.Width;
|
||||
@@ -1000,7 +1031,6 @@ function WidgetsValueModal(props) {
|
||||
handleInputBlur();
|
||||
};
|
||||
|
||||
|
||||
//function is used to show widgets on modal according to selected widget type checkbox/date/radio/drodown/textbox/signature/image
|
||||
const getWidgetType = (type) => {
|
||||
const isStampOrImage =
|
||||
@@ -1476,7 +1506,7 @@ function WidgetsValueModal(props) {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<span className="text-gray-300">
|
||||
<span className="text-gray-300 uppercase">
|
||||
{currWidgetsDetails?.options?.validation?.format}
|
||||
</span>
|
||||
</div>
|
||||
@@ -1539,6 +1569,23 @@ function WidgetsValueModal(props) {
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
/>
|
||||
);
|
||||
case drawWidget:
|
||||
return (
|
||||
<div>
|
||||
<Draw
|
||||
penColor={penColor}
|
||||
canvasRef={canvasRef}
|
||||
currWidgetsDetails={props?.currWidgetsDetails}
|
||||
handleSignatureChange={handleSignatureChange}
|
||||
/>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent
|
||||
penColor={penColor}
|
||||
setPenColor={setPenColor}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<input
|
||||
@@ -1668,7 +1715,7 @@ function WidgetsValueModal(props) {
|
||||
//function too use on click on next/finish button then update modal UI according to current widgets
|
||||
const handleClickOnNext = async (isFinishDoc) => {
|
||||
if (
|
||||
["signature", "stamp", "image", "initials"].includes(
|
||||
["signature", "stamp", "image", "initials", drawWidget].includes(
|
||||
currWidgetsDetails?.type
|
||||
) &&
|
||||
(signature || image || myInitial || defaultSignImg || myStamp)
|
||||
@@ -1733,7 +1780,7 @@ function WidgetsValueModal(props) {
|
||||
if (isOptional && uniqueId) {
|
||||
return false;
|
||||
} else if (
|
||||
["signature", "stamp", "image", "initials"].includes(
|
||||
["signature", "stamp", "image", "initials", drawWidget].includes(
|
||||
currWidgetsDetails?.type
|
||||
)
|
||||
) {
|
||||
@@ -1925,7 +1972,9 @@ function WidgetsValueModal(props) {
|
||||
></button>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
{!isSave && <HandleRequiredField />}
|
||||
{(!isSave || props?.role === "prefill") && (
|
||||
<HandleRequiredField />
|
||||
)}
|
||||
{isSave ? (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,16 +1,47 @@
|
||||
import { useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import { drawWidget } from "../../../constant/Utils";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
//this component is used for signature widget "Draw tab" and prefill user Draw widget
|
||||
function Draw(props) {
|
||||
const { prefillImg } = useSelector((state) => state.widget);
|
||||
useEffect(() => {
|
||||
handleDrawResponse();
|
||||
}, [prefillImg]);
|
||||
|
||||
//function is used to show draw widget response
|
||||
const handleDrawResponse = async () => {
|
||||
if (
|
||||
props?.currWidgetsDetails?.type === drawWidget &&
|
||||
props?.canvasRef.current
|
||||
) {
|
||||
const getPrefillImg = prefillImg?.find(
|
||||
(x) => x.id === props?.currWidgetsDetails?.key
|
||||
);
|
||||
const base64 = getPrefillImg?.base64;
|
||||
props?.canvasRef.current.fromDataURL(base64);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={props?.canvasRef}
|
||||
penColor={props?.penColor}
|
||||
canvasProps={{
|
||||
className: `${props?.currWidgetsDetails?.type === "initials" ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-gray-300 rounded-[4px]`
|
||||
className: `${
|
||||
props?.prefillCls
|
||||
? "prefillCanvas"
|
||||
: props?.currWidgetsDetails?.type === "initials"
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
} border-[1.3px] border-gray-300 rounded-[10px]`
|
||||
}}
|
||||
onEnd={() =>
|
||||
props?.handleSignatureChange(props?.canvasRef.current?.toDataURL())
|
||||
props?.handleSignatureChange(
|
||||
props?.canvasRef.current?.toDataURL(),
|
||||
props?.currWidgetsDetails
|
||||
)
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
|
||||
@@ -45,7 +45,8 @@ function PenColorComponent({
|
||||
aria-label={`Select ${color} pen`}
|
||||
onClick={() => {
|
||||
setPenColor?.(color);
|
||||
convertToImg?.(fontSelect, typedSignature, color);
|
||||
convertToImg &&
|
||||
convertToImg?.(fontSelect, typedSignature, color);
|
||||
}}
|
||||
className={`${COLOR_CLASS[color] || "text-base-content"} ${selected ? "border-current" : "border-white"} border-b-[2px] pb-0.5 cursor-pointer text-[16px] fa-light fa-pen-nib`}
|
||||
></i>
|
||||
|
||||
@@ -7,6 +7,7 @@ import EditorToolbar, { module1, module2, formats } from "../pdf/EditorToolbar";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import Alert from "../../primitives/Alert";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
const MailTemplateEditor = ({
|
||||
info,
|
||||
@@ -79,7 +80,7 @@ const MailTemplateEditor = ({
|
||||
}
|
||||
};
|
||||
//function to save completion email template
|
||||
const handleSaveCompletionEmail = async (e) => {
|
||||
const handleSaveCompletionEmail = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = completionBody.replace(/"/g, "'");
|
||||
@@ -103,9 +104,9 @@ const MailTemplateEditor = ({
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
});
|
||||
//function to save request email template
|
||||
const handleSaveRequestEmail = async (e) => {
|
||||
const handleSaveRequestEmail = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = requestBody.replace(/"/g, "'");
|
||||
@@ -135,10 +136,10 @@ const MailTemplateEditor = ({
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//function to use reset form
|
||||
const handleReset = async (request, completion) => {
|
||||
const handleReset = withSessionValidation(async (request, completion) => {
|
||||
let extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
@@ -185,7 +186,7 @@ const MailTemplateEditor = ({
|
||||
setIsMailLoader((p) => ({ ...p, completion: false }));
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
//function for handle ontext change and save again text in delta
|
||||
const handleOnchangeRequest = () => {
|
||||
if (editorRef.current) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import MailTemplateEditor from "../MailTemplateEditor";
|
||||
|
||||
const EmailTab = () => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
tenantInfo
|
||||
} = useSelector((state) => state.user);
|
||||
return (
|
||||
<div className="flex flex-col mb-4">
|
||||
<MailTemplateEditor
|
||||
info={
|
||||
tenantInfo
|
||||
}
|
||||
tenantId={tenantInfo?.objectId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailTab;
|
||||
@@ -0,0 +1,281 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { formatDate, withSessionValidation } from "../../../utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
setAlertInfo,
|
||||
setLoader,
|
||||
setUserInfo
|
||||
} from "../../../redux/reducers/userReducer";
|
||||
import DatePicker from "../../DatePicker";
|
||||
import DateFormat from "../../DateFormat";
|
||||
import {
|
||||
changeDateToMomentFormat,
|
||||
selectFormat
|
||||
} from "../../../constant/Utils";
|
||||
import moment from "moment";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
|
||||
const dateFormatArr = [
|
||||
"L",
|
||||
"DD-MM-YYYY",
|
||||
"YYYY-MM-DD",
|
||||
"MM.DD.YYYY",
|
||||
"MM-DD-YYYY",
|
||||
"MMM DD, YYYY",
|
||||
"LL",
|
||||
"DD MMM, YYYY",
|
||||
"DD MMMM, YYYY",
|
||||
"DD.MM.YYYY",
|
||||
"DD/MM/YYYY"
|
||||
];
|
||||
|
||||
const WidgetsTab = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const { userInfo } = useSelector((state) => state.user);
|
||||
const [selectDate, setSelectDate] = useState({
|
||||
date: "",
|
||||
format: "MM/dd/yyyy"
|
||||
});
|
||||
const [dateFormatList, setDateFormatList] = useState([]);
|
||||
const [dateWidget, setDateWidget] = useState({
|
||||
isSigningDate: false,
|
||||
isReadOnly: false,
|
||||
date: "",
|
||||
format: "MM/dd/yyyy"
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
updateStates();
|
||||
}, [userInfo]);
|
||||
|
||||
const updateStates = () => {
|
||||
const widgetPreferences = userInfo?.WidgetPreferences || [];
|
||||
const dateWidgetPref = widgetPreferences.find((w) => w.type === "date") || {
|
||||
isSigningDate: false,
|
||||
isReadOnly: false
|
||||
};
|
||||
setDateWidget({
|
||||
isSigningDate: !!dateWidgetPref.isSigningDate,
|
||||
isReadOnly: !!dateWidgetPref.isReadOnly,
|
||||
date: dateWidgetPref.date,
|
||||
format: dateWidgetPref.format
|
||||
});
|
||||
setSelectDate({ date: dateWidgetPref.date, format: dateWidgetPref.format });
|
||||
const formatted = moment(
|
||||
dateWidgetPref.date,
|
||||
changeDateToMomentFormat(dateWidgetPref.format),
|
||||
true
|
||||
).toISOString();
|
||||
formatDateList(formatted);
|
||||
};
|
||||
|
||||
const handleSigningDateChange = (e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectDate({ date: "", format: selectDate.format });
|
||||
}
|
||||
setDateWidget((prev) => ({
|
||||
...prev,
|
||||
date: "",
|
||||
isSigningDate: e.target.checked
|
||||
}));
|
||||
};
|
||||
|
||||
const handleReadOnlyChange = (e) => {
|
||||
setDateWidget({ ...dateWidget, isReadOnly: e.target.checked });
|
||||
};
|
||||
|
||||
const showAlert = (type, msg) => {
|
||||
dispatch(setAlertInfo({ type, msg }));
|
||||
setTimeout(() => {
|
||||
dispatch(setAlertInfo({ type: "success", msg: "" }));
|
||||
}, 2000);
|
||||
};
|
||||
// `handleSave` is used save updated value signature type
|
||||
const handleSave = withSessionValidation(async () => {
|
||||
dispatch(setLoader(true));
|
||||
try {
|
||||
if (
|
||||
dateWidget?.isReadOnly &&
|
||||
!selectDate.date &&
|
||||
!dateWidget.isSigningDate
|
||||
) {
|
||||
alert(t("read-only-date-error"));
|
||||
return;
|
||||
}
|
||||
const res = await Parse.Cloud.run("setwidgetpreferences", {
|
||||
dateWidget: dateWidget
|
||||
});
|
||||
if (res) {
|
||||
const widgetPreferences = userInfo?.WidgetPreferences || [];
|
||||
// Normalize booleans (simple + safe)
|
||||
const dateOpt = { type: "date", ...dateWidget };
|
||||
// Upsert: replace if exists, otherwise append
|
||||
const updatedWidgetPreferences =
|
||||
widgetPreferences?.length > 0
|
||||
? widgetPreferences.map((w) => (w.type === "date" ? dateOpt : w))
|
||||
: [...widgetPreferences, dateOpt];
|
||||
const userdata = { ...userInfo };
|
||||
userdata.WidgetPreferences = updatedWidgetPreferences;
|
||||
dispatch(setUserInfo(userdata));
|
||||
showAlert("success", t("saved-successfully"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Widgets preferences error: ", error);
|
||||
showAlert("danger", error.message);
|
||||
} finally {
|
||||
dispatch(setLoader(false));
|
||||
}
|
||||
});
|
||||
const handleDateChange = (date) => {
|
||||
//function to save date and format in local array
|
||||
const formattedDate = formatDate({
|
||||
date: date,
|
||||
format: selectDate.format
|
||||
});
|
||||
setDateWidget({
|
||||
...dateWidget,
|
||||
isSigningDate: false,
|
||||
date: formattedDate,
|
||||
format: selectDate.format
|
||||
});
|
||||
setSelectDate({ date: formattedDate, format: selectDate.format });
|
||||
formatDateList(date);
|
||||
};
|
||||
|
||||
const formatDateList = (selecteddate) => {
|
||||
let date = selecteddate || new Date();
|
||||
const list = dateFormatArr.map((dateFormat) => {
|
||||
const format = selectFormat(dateFormat);
|
||||
return { date: formatDate({ date, format }), format: format };
|
||||
});
|
||||
setDateFormatList(list);
|
||||
};
|
||||
const handleChangeFormat = (e) => {
|
||||
e.stopPropagation();
|
||||
const selectedIndex = Number(e.target.value);
|
||||
const format = dateFormatList[selectedIndex]?.format;
|
||||
const dateObj =
|
||||
selectDate.date && !dateWidget?.isSigningDate
|
||||
? { date: dateFormatList[selectedIndex]?.date, format: format }
|
||||
: { format: format };
|
||||
setDateWidget((prev) => ({ ...prev, ...dateObj }));
|
||||
setSelectDate((prev) => ({ ...prev, ...dateObj }));
|
||||
};
|
||||
const handleClear = () => {
|
||||
setSelectDate((prev) => ({ ...prev, date: "" }));
|
||||
setDateWidget((prev) => ({ ...prev, date: "" }));
|
||||
};
|
||||
return (
|
||||
<div id="panel-widgets">
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 md:gap-x-8 gap-y-6">
|
||||
{/* Left Column - Signature Settings */}
|
||||
<div className="md:col-span-6 flex flex-col">
|
||||
<div>
|
||||
<label className="inline-flex text-[14px] mb-0 font-medium">
|
||||
{t("date-widget")}
|
||||
<a data-tooltip-id="date-widget-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="date-widget-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold"> {t("date-widget")}</p>
|
||||
<p>{t("date-pref-help-sub-title")}</p>
|
||||
<div className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold capitalize">
|
||||
{t("format")}:{" "}
|
||||
</span>
|
||||
<span>{t("date-pref-help-format")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold capitalize">
|
||||
{t("default-date")}:{" "}
|
||||
</span>
|
||||
<span>{t("date-pref-help-default-date")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold capitalize">
|
||||
{t("signing-date")}:{" "}
|
||||
</span>
|
||||
<span>{t("date-pref-help-signing-date")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold capitalize">
|
||||
{t("read-only")}:{" "}
|
||||
</span>
|
||||
<span>{t("date-pref-help-read-only")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<DateFormat
|
||||
selectDate={selectDate}
|
||||
dateFormatList={dateFormatList}
|
||||
handleChangeFormat={handleChangeFormat}
|
||||
/>
|
||||
<DatePicker
|
||||
selectDate={selectDate}
|
||||
onChange={handleDateChange}
|
||||
handleClear={handleClear}
|
||||
/>
|
||||
<div className="mt-3 flex flex-col gap-2">
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
className="op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
id="date-widget-signingdate"
|
||||
name="isSigningDate"
|
||||
onChange={handleSigningDateChange}
|
||||
checked={dateWidget.isSigningDate}
|
||||
/>
|
||||
<label
|
||||
htmlFor="date-widget-signingdate"
|
||||
className="text-sm font-medium text-base-content hover:underline underline-offset-2 cursor-pointer capitalize mb-0"
|
||||
title={t("signing-date")}
|
||||
>
|
||||
{t("signing-date")}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
className="op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
id="date-widget-readonly"
|
||||
name="isReadOnly"
|
||||
onChange={handleReadOnlyChange}
|
||||
checked={dateWidget.isReadOnly}
|
||||
/>
|
||||
<label
|
||||
htmlFor="date-widget-readonly"
|
||||
className="text-sm font-medium text-base-content hover:underline underline-offset-2 cursor-pointer capitalize mb-0"
|
||||
title={t("read-only")}
|
||||
>
|
||||
{t("read-only")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Save Button - Full Width */}
|
||||
<div className="md:col-span-12 flex justify-start mt-3">
|
||||
<button
|
||||
className="op-btn op-btn-primary w-[110px]"
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetsTab;
|
||||
@@ -0,0 +1,4 @@
|
||||
import EmailTab from "./Email";
|
||||
import WidgetsTab from "./Widgets";
|
||||
|
||||
export { EmailTab, WidgetsTab };
|
||||
@@ -3,6 +3,7 @@ import Parse from "parse";
|
||||
import Alert from "../../../primitives/Alert";
|
||||
import Loader from "../../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { withSessionValidation } from "../../../utils";
|
||||
|
||||
const CreateFolder = ({ parentFolderId, onSuccess, folderCls, onBack }) => {
|
||||
const folderPtr = {
|
||||
@@ -18,7 +19,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls, onBack }) => {
|
||||
setAlert({ type: type, message: msg });
|
||||
setTimeout(() => setAlert({ type: type, message: "" }), 1000);
|
||||
};
|
||||
const handleCreateFolder = async (event) => {
|
||||
const handleCreateFolder = withSessionValidation(async (event) => {
|
||||
event.preventDefault();
|
||||
handleLoader(true);
|
||||
if (name) {
|
||||
@@ -41,6 +42,12 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls, onBack }) => {
|
||||
template.set("Folder", folderPtr);
|
||||
}
|
||||
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
template.set("ExtUserPtr", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Users",
|
||||
objectId: ExtCls[0].objectId
|
||||
});
|
||||
const res = await template.save();
|
||||
if (res) {
|
||||
handleLoader(false);
|
||||
@@ -52,7 +59,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls, onBack }) => {
|
||||
handleLoader(false);
|
||||
showToast("info", t("fill-folder-name"));
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleLoader = (status) => setIsLoader(status);
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,11 @@ import fontkit from "@pdf-lib/fontkit";
|
||||
import { themeColor } from "./const";
|
||||
import { format, toZonedTime } from "date-fns-tz";
|
||||
import i18n from "../i18n";
|
||||
import { applyNumberFormulasToPages, buildDownloadFilename } from "../utils";
|
||||
import {
|
||||
applyNumberFormulasToPages,
|
||||
buildDownloadFilename,
|
||||
addPreferenceOpt
|
||||
} from "../utils";
|
||||
|
||||
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||
export const fontColorArr = ["red", "black", "blue", "yellow"];
|
||||
@@ -18,6 +22,7 @@ export const isTab = 767 < window.innerWidth < 1023;
|
||||
export const isHighResolution = window.innerWidth > 1023;
|
||||
export const isTabAndMobile = window.innerWidth < 1023;
|
||||
export const textInputWidget = "text input";
|
||||
export const drawWidget = "draw";
|
||||
export const textWidget = "text";
|
||||
export const radioButtonWidget = "radio button";
|
||||
export const cellsWidget = "cells";
|
||||
@@ -356,7 +361,8 @@ export const widgets = [
|
||||
iconSize: "19px"
|
||||
},
|
||||
{ type: radioButtonWidget, icon: "fa-light fa-circle-dot", iconSize: "20px" },
|
||||
{ type: "image", icon: "fa-light fa-image", iconSize: "20px" }
|
||||
{ type: "image", icon: "fa-light fa-image", iconSize: "20px" },
|
||||
{ type: drawWidget, icon: "fa-light fa-pen-nib", iconSize: "20px" }
|
||||
];
|
||||
|
||||
export const getDate = (dateformat) => {
|
||||
@@ -516,10 +522,15 @@ export const addWidgetOptions = (
|
||||
const dateFormat = signer?.DateFormat
|
||||
? selectFormat(signer?.DateFormat)
|
||||
: "MM/dd/yyyy";
|
||||
const options = addPreferenceOpt(signer, "date", role);
|
||||
return {
|
||||
...defaultOpt,
|
||||
response: role === "prefill" ? getDate(signer?.DateFormat) : "",
|
||||
validation: { format: dateFormat, type: "date-format" }
|
||||
response: options?.response || "",
|
||||
isReadOnly: options?.isReadOnly || false,
|
||||
validation: {
|
||||
format: options?.format || dateFormat,
|
||||
type: "date-format"
|
||||
}
|
||||
};
|
||||
}
|
||||
case "image":
|
||||
@@ -540,6 +551,8 @@ export const addWidgetOptions = (
|
||||
};
|
||||
case textWidget:
|
||||
return defaultOpt;
|
||||
case drawWidget:
|
||||
return defaultOpt;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@@ -549,7 +562,8 @@ export const addWidgetSelfsignOptions = (
|
||||
type,
|
||||
getWidgetValue,
|
||||
owner,
|
||||
placeholder
|
||||
placeholder,
|
||||
isSignyourself
|
||||
) => {
|
||||
let defaultOpt;
|
||||
//condition to handle widgets name field
|
||||
@@ -604,10 +618,17 @@ export const addWidgetSelfsignOptions = (
|
||||
const dateFormat = owner?.DateFormat
|
||||
? selectFormat(owner?.DateFormat)
|
||||
: "MM/dd/yyyy";
|
||||
const options = addPreferenceOpt(owner, "date", "", isSignyourself);
|
||||
return {
|
||||
...defaultOpt,
|
||||
response: getDate(owner?.DateFormat),
|
||||
validation: { format: dateFormat, type: "date-format" }
|
||||
response: options?.response || "",
|
||||
...(!isSignyourself
|
||||
? { isReadOnly: options?.isReadOnly || false }
|
||||
: {}),
|
||||
validation: {
|
||||
format: options?.format || dateFormat,
|
||||
type: "date-format"
|
||||
}
|
||||
};
|
||||
}
|
||||
case "image":
|
||||
@@ -655,10 +676,13 @@ export const defaultWidthHeight = (type) => {
|
||||
return { width: 5, height: 10 };
|
||||
case textWidget:
|
||||
return { width: 150, height: 19 };
|
||||
case drawWidget:
|
||||
return { width: 150, height: 60 };
|
||||
default:
|
||||
return { width: 150, height: 60 };
|
||||
}
|
||||
};
|
||||
//convert url to base64
|
||||
export async function getBase64FromUrl(url, autosign) {
|
||||
const data = await fetch(url);
|
||||
const blob = await data.blob();
|
||||
@@ -1458,19 +1482,26 @@ export function onSaveSign(
|
||||
const signImg = typedSignature
|
||||
? convertTextToImg(typeFont, typedSignature, fontColor, widgetDims)
|
||||
: signatureImg;
|
||||
return {
|
||||
...position,
|
||||
...(type === "type" ? { Width: posWidth } : {}),
|
||||
...(type === "type" ? { Height: posHeight } : {}),
|
||||
SignUrl: signImg,
|
||||
...(isSignOrInitials && { signatureType: type || "" }),
|
||||
options: { ...position.options, response: signImg },
|
||||
...(typedSignature && {
|
||||
typeSignature: typedSignature,
|
||||
typeFont: typeFont ?? "Fasthand",
|
||||
fontColor: fontColor ?? "blue"
|
||||
})
|
||||
};
|
||||
if (widgetsType === drawWidget) {
|
||||
return {
|
||||
...position,
|
||||
options: { ...position.options, response: signImg }
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...position,
|
||||
...(type === "type" ? { Width: posWidth } : {}),
|
||||
...(type === "type" ? { Height: posHeight } : {}),
|
||||
SignUrl: signImg,
|
||||
...(isSignOrInitials && { signatureType: type || "" }),
|
||||
options: { ...position.options, response: signImg },
|
||||
...(typedSignature && {
|
||||
typeSignature: typedSignature,
|
||||
typeFont: typeFont ?? "Fasthand",
|
||||
fontColor: fontColor ?? "blue"
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
return position;
|
||||
});
|
||||
@@ -1482,7 +1513,7 @@ export function onSaveSign(
|
||||
return obj;
|
||||
});
|
||||
//condition when draw/upload signature/initials then apply it all related to widgets (draw, typed signature or default signature)
|
||||
if (isApplyAll || isAutoSign) {
|
||||
if ((isApplyAll || isAutoSign) && widgetsType !== drawWidget) {
|
||||
const updatedArray = updateXYposition.map((page) => ({
|
||||
...page,
|
||||
pos: page.pos.map((item) => {
|
||||
@@ -1883,6 +1914,13 @@ export const embedWidgetsToDoc = async (
|
||||
} else {
|
||||
updateItem = item.pos;
|
||||
}
|
||||
const ImgTypeWidget = [
|
||||
"signature",
|
||||
"stamp",
|
||||
"initials",
|
||||
"image",
|
||||
drawWidget
|
||||
];
|
||||
const pageNo = item.pageNumber;
|
||||
const widgetsPositionArr = updateItem;
|
||||
const pages = pdfDoc.getPages();
|
||||
@@ -1908,14 +1946,13 @@ export const embedWidgetsToDoc = async (
|
||||
images = await Promise.all(
|
||||
widgetsPositionArr.map(async (widget) => {
|
||||
// `SignUrl` this is wrong nomenclature and maintain for older code in this options we save base64 of signature image from sign pad
|
||||
let signbase64 = widget.SignUrl && widget.SignUrl;
|
||||
let signbase64 = widget?.options?.response;
|
||||
const widgetDims = { Width: widget.Width, Height: widget.Height };
|
||||
if (signbase64) {
|
||||
if (signbase64 && ImgTypeWidget.includes(widget.type)) {
|
||||
if (!isBase64(signbase64) && prefillImg) {
|
||||
const imgData = prefillImg?.find((x) => x?.id === widget?.key);
|
||||
signbase64 = imgData?.base64;
|
||||
}
|
||||
|
||||
// let arr = signbase64.split(",");
|
||||
// const mime = arr[0].match(/:(.*?);/)[1];
|
||||
const signatureImg = await convertBase64ToImg(
|
||||
@@ -1947,9 +1984,7 @@ export const embedWidgetsToDoc = async (
|
||||
if (hasError) break; // Stop the inner loop if an error occurred
|
||||
try {
|
||||
let img;
|
||||
if (
|
||||
["signature", "stamp", "initials", "image"].includes(position.type)
|
||||
) {
|
||||
if (ImgTypeWidget.includes(position.type)) {
|
||||
if (images[id].mimetype === "image/png") {
|
||||
img = await pdfDoc.embedPng(images[id].arrayBuffer);
|
||||
} else {
|
||||
@@ -3333,8 +3368,8 @@ export async function handleSignatureType(tenantSignTypes, signatureType) {
|
||||
return updatedSignatureType;
|
||||
}
|
||||
|
||||
// `formatDate` is used to format date to dd-mmm-yyy
|
||||
export const formatDate = (date) => {
|
||||
// `formatDateToDdMmmYyyy` is used to format date to dd-mmm-yyyy
|
||||
export const formatDateToDdMmmYyyy = (date) => {
|
||||
// Create a Date object
|
||||
const newDate = new Date(date);
|
||||
// Format the date
|
||||
@@ -3479,7 +3514,7 @@ export const mailTemplate = (param) => {
|
||||
const appName = "OpenSign™";
|
||||
const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
|
||||
|
||||
const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
|
||||
const opurl = ` <a href="mailto:complaint@opensiglabs.com" target=_blank>here</a>.</p></div></div></body></html>`;
|
||||
const subject = `${param.senderName} has requested you to sign "${param.title}"`;
|
||||
const body =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'>" +
|
||||
@@ -3502,7 +3537,7 @@ export const mailTemplate = (param) => {
|
||||
appName +
|
||||
". For any queries regarding this email, please contact the sender " +
|
||||
param.senderMail +
|
||||
" directly. If you think this email is inappropriate or spam, you may file a complaint with " +
|
||||
" directly. If you think this email is inappropriate or spam, you may file a complaints with " +
|
||||
appName +
|
||||
opurl;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
import { withSessionValidation } from "../utils";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
@@ -132,7 +133,7 @@ const Forms = (props) => {
|
||||
e.target.value = "";
|
||||
}
|
||||
};
|
||||
const handleFileInput = async (e) => {
|
||||
const handleFileInput = withSessionValidation(async (e) => {
|
||||
setpercentage(0);
|
||||
try {
|
||||
const files = Array.from(e.target.files);
|
||||
@@ -353,7 +354,7 @@ const Forms = (props) => {
|
||||
setSelectedFiles([]);
|
||||
removeFile(e);
|
||||
}
|
||||
};
|
||||
});
|
||||
// `isValidURL` is used to check valid webhook url
|
||||
function isValidURL(value) {
|
||||
try {
|
||||
@@ -363,7 +364,7 @@ const Forms = (props) => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const handleSubmit = async (e) => {
|
||||
const handleSubmit = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (fileupload) {
|
||||
@@ -512,7 +513,7 @@ const Forms = (props) => {
|
||||
} else {
|
||||
alert(t("file-alert-3"));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const handleFolder = (data) => {
|
||||
setFolder(data);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { sanitizeFileName } from "../utils";
|
||||
import { sanitizeFileName, withSessionValidation } from "../utils";
|
||||
|
||||
const COLOR_CLASS = {
|
||||
blue: "text-blue-600",
|
||||
@@ -140,7 +140,7 @@ const ManageSign = () => {
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
const handleSubmit = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
const isUrl = image?.includes("https") || image?.includes("http");
|
||||
if (!isvalue) {
|
||||
@@ -205,7 +205,7 @@ const ManageSign = () => {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
function base64StringtoFile(base64String, filename) {
|
||||
let arr = base64String.split(","),
|
||||
// type of uploaded image
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useRef, useCallback } from "react";
|
||||
import { lazyWithRetry } from "../utils";
|
||||
import { lazyWithRetry, withSessionValidation } from "../utils";
|
||||
import "../styles/opensigndrive.css";
|
||||
import {
|
||||
getThemeIconColor,
|
||||
@@ -279,7 +279,7 @@ function Opensigndrive() {
|
||||
setNewFolderName(value);
|
||||
};
|
||||
//function for create folder
|
||||
const handleAddFolder = async (e) => {
|
||||
const handleAddFolder = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
if (newFolderName) {
|
||||
setIsFolderLoader(true);
|
||||
@@ -313,7 +313,12 @@ function Opensigndrive() {
|
||||
const template = new Parse.Object(foldercls);
|
||||
template.set("Name", newFolderName);
|
||||
template.set("Type", "Folder");
|
||||
|
||||
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
template.set("ExtUserPtr", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Users",
|
||||
objectId: ExtCls[0].objectId
|
||||
});
|
||||
if (parentId) {
|
||||
template.set("Folder", folderPtr);
|
||||
}
|
||||
@@ -338,7 +343,7 @@ function Opensigndrive() {
|
||||
} else {
|
||||
setError(t("fill-field"));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//function to use sorting document list according to type and order
|
||||
const sortedBy = (appInfo, type, order) => {
|
||||
|
||||
@@ -11,7 +11,6 @@ import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import ShareButton from "../primitives/ShareButton";
|
||||
import {
|
||||
replaceMailVaribles,
|
||||
pdfNewWidthFun,
|
||||
contractDocument,
|
||||
contractUsers,
|
||||
@@ -39,7 +38,6 @@ import {
|
||||
handleSignatureType,
|
||||
getBase64FromUrl,
|
||||
generatePdfName,
|
||||
mailTemplate,
|
||||
getOriginalWH,
|
||||
defaultMailBody,
|
||||
defaultMailSubject
|
||||
@@ -60,13 +58,13 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import { EmailBody } from "../components/pdf/EmailBody";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import Alert from "../primitives/Alert";
|
||||
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
|
||||
import * as utils from "../utils";
|
||||
import { resetWidgetState, setPrefillImg } from "../redux/reducers/widgetSlice";
|
||||
import EditDocument from "../components/pdf/EditTemplate";
|
||||
import CustomizeMail from "../components/pdf/CustomizeMail";
|
||||
|
||||
function PlaceHolderSign() {
|
||||
const { t } = useTranslation();
|
||||
@@ -76,14 +74,9 @@ function PlaceHolderSign() {
|
||||
const isShowModal = useSelector((state) => state.widget.isShowModal);
|
||||
const editorRef = useRef();
|
||||
const navigate = useNavigate();
|
||||
const [defaultBody, setDefaultBody] = useState("");
|
||||
const [defaultSubject, setDefaultSubject] = useState("");
|
||||
const [requestSubject, setRequestSubject] = useState("");
|
||||
const [requestBody, setRequestBody] = useState("");
|
||||
const [tenantMailTemplate, setTenantMailTemplate] = useState({
|
||||
body: "",
|
||||
subject: ""
|
||||
});
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [pdfDetails, setPdfDetails] = useState([]);
|
||||
const [isMailSend, setIsMailSend] = useState(false);
|
||||
const [allPages, setAllPages] = useState(null);
|
||||
@@ -148,7 +141,6 @@ function PlaceHolderSign() {
|
||||
status: false,
|
||||
message: ""
|
||||
});
|
||||
const [isCustomize, setIsCustomize] = useState(false);
|
||||
const [zoomPercent, setZoomPercent] = useState(0);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [pdfBase64Url, setPdfBase64Url] = useState("");
|
||||
@@ -184,12 +176,6 @@ function PlaceHolderSign() {
|
||||
);
|
||||
if (user) {
|
||||
try {
|
||||
const defaultRequestBody = defaultMailBody;
|
||||
const defaultSubject = defaultMailSubject;
|
||||
setDefaultBody(defaultRequestBody);
|
||||
setDefaultSubject(defaultSubject);
|
||||
setRequestBody(defaultRequestBody);
|
||||
setRequestSubject(defaultSubject);
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
@@ -198,14 +184,22 @@ function PlaceHolderSign() {
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
if (tenantDetails?.RequestBody) {
|
||||
setRequestBody(tenantDetails?.RequestBody);
|
||||
setRequestSubject(tenantDetails?.RequestSubject);
|
||||
setTenantMailTemplate({
|
||||
body: tenantDetails?.RequestBody,
|
||||
subject: tenantDetails?.RequestSubject
|
||||
});
|
||||
}
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
const subject = tenantDetails?.RequestSubject ?? "";
|
||||
const body = tenantDetails?.RequestBody ?? "";
|
||||
//customize mail state is handle to when user want to customize already set tenant email format then use that format
|
||||
const userSubject =
|
||||
subject;
|
||||
const userBody =
|
||||
body;
|
||||
|
||||
setCustomizeMail({
|
||||
subject: userSubject ?? defaultMailSubject,
|
||||
body: userBody ?? defaultMailBody
|
||||
});
|
||||
setDefaultMail({ subject: userSubject, body: userBody });
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -319,9 +313,9 @@ function PlaceHolderSign() {
|
||||
updatedPdfDetails[0].SignatureType = updatedSignatureType;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
if (documentData?.[0]?.RequestBody && documentData?.[0]?.RequestSubject) {
|
||||
setTenantMailTemplate({
|
||||
body: documentData?.[0]?.RequestBody,
|
||||
subject: documentData?.[0]?.RequestSubject
|
||||
setDefaultMail({
|
||||
subject: documentData?.[0]?.RequestSubject,
|
||||
body: documentData?.[0]?.RequestBody
|
||||
});
|
||||
}
|
||||
if (
|
||||
@@ -872,7 +866,6 @@ function PlaceHolderSign() {
|
||||
});
|
||||
const isSignYourSelfFlow = false;
|
||||
try {
|
||||
const docId = pdfDetails[0]?.objectId;
|
||||
//pdfOriginalWH contained all pdf's pages width,height & pagenumber in array format
|
||||
const pdfBase64 = await embedWidgetsToDoc(
|
||||
placeholder,
|
||||
@@ -924,12 +917,16 @@ function PlaceHolderSign() {
|
||||
//if empty widget exist and user try to send document then throw tour message
|
||||
for (const placeholder of getPlaceholder || []) {
|
||||
const requiredWidgets = placeholder.pos.filter(
|
||||
(position) => position.type !== "checkbox"
|
||||
(position) => position?.options?.status === "required"
|
||||
);
|
||||
for (const widget of requiredWidgets) {
|
||||
const hasResponse = widget?.options?.response;
|
||||
const hasDefault = widget?.options?.defaultValue;
|
||||
if (!hasResponse && !hasDefault && !isPrefillEmpty) {
|
||||
const response =
|
||||
widget?.options?.response || widget?.options?.defaultValue;
|
||||
const isEmptyResponse =
|
||||
!response ||
|
||||
(typeof response === "string" && response.trim() === "") ||
|
||||
(Array.isArray(response) && response.length === 0);
|
||||
if (isEmptyResponse && !isPrefillEmpty) {
|
||||
isPrefillEmpty = true;
|
||||
unfilledTextWidgetId = widget.key;
|
||||
setPageNumber(placeholder.pageNumber);
|
||||
@@ -1034,7 +1031,7 @@ function PlaceHolderSign() {
|
||||
}
|
||||
};
|
||||
//function to use save placeholder details in contracts_document
|
||||
const saveDocumentDetails = async () => {
|
||||
const saveDocumentDetails = utils.withSessionValidation(async () => {
|
||||
setIsUiLoading(true);
|
||||
let signerMail = signersdata.slice();
|
||||
// For "Send in order", only consider the first signer
|
||||
@@ -1093,7 +1090,6 @@ function PlaceHolderSign() {
|
||||
}
|
||||
}
|
||||
);
|
||||
setIsMailSend(true);
|
||||
setIsLoading({ isLoad: false });
|
||||
setIsUiLoading(false);
|
||||
setIsSendAlert({ mssg: "confirm", alert: true });
|
||||
@@ -1102,6 +1098,17 @@ function PlaceHolderSign() {
|
||||
updatedPdfDetails[0].Name = docTitle;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
}
|
||||
const ownerId = pdfDetails[0].ExtUserPtr?.UserId?.objectId;
|
||||
const firstSigner = signersdata[0];
|
||||
const isOwner = firstSigner?.UserId?.objectId === ownerId;
|
||||
const currentUserMail = Parse.User.current()?.getEmail();
|
||||
const isCurrentUser = signerMail?.[0]?.Email === currentUserMail;
|
||||
if (pdfDetails[0]?.SendinOrder && isOwner) {
|
||||
setIsSend(true);
|
||||
setIsCurrUser(isCurrentUser);
|
||||
} else {
|
||||
setIsMailModal(true);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
@@ -1109,7 +1116,7 @@ function PlaceHolderSign() {
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
@@ -1151,7 +1158,7 @@ function PlaceHolderSign() {
|
||||
className="flex flex-row items-center op-link op-link-primary"
|
||||
>
|
||||
<i className="fa-light fa-copy" />
|
||||
<span className="hidden md:block ml-1">{t("copy-link")}</span>
|
||||
<span className="hidden md:block ml-1 ">{t("copy-link")}</span>
|
||||
</button>
|
||||
<ShareButton
|
||||
title={t("sign-url")}
|
||||
@@ -1165,201 +1172,10 @@ function PlaceHolderSign() {
|
||||
);
|
||||
});
|
||||
};
|
||||
const sendEmailToSigners = async () => {
|
||||
let htmlReqBody;
|
||||
setIsUiLoading(true);
|
||||
setIsSendAlert({});
|
||||
let sendMail;
|
||||
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
|
||||
const newDate = new Date(expireDate);
|
||||
const localExpireDate = newDate.toLocaleDateString("en-US", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric"
|
||||
});
|
||||
|
||||
let senderEmail =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
let signerMail = signersdata.slice();
|
||||
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
|
||||
signerMail.splice(1);
|
||||
}
|
||||
|
||||
for (let i = 0; i < signerMail.length; i++) {
|
||||
try {
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const objectId = signerMail[i].objectId;
|
||||
const hostUrl = window.location.origin;
|
||||
//encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}`
|
||||
);
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const senderName =
|
||||
pdfDetails?.[0].ExtUserPtr.Name;
|
||||
const documentName = `${pdfDetails?.[0].Name}`;
|
||||
let replaceVar;
|
||||
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
isCustomize
|
||||
) {
|
||||
const replacedRequestBody = requestBody.replace(/"/g, "'");
|
||||
htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
|
||||
const variables = {
|
||||
document_title: documentName,
|
||||
note: pdfDetails?.[0]?.Note,
|
||||
sender_name: senderName,
|
||||
sender_mail: senderEmail,
|
||||
sender_phone: senderPhone || "",
|
||||
receiver_name: signerMail[i]?.Name || "",
|
||||
receiver_email: signerMail[i].Email,
|
||||
receiver_phone: signerMail[i]?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: signPdf
|
||||
};
|
||||
replaceVar = replaceMailVaribles(
|
||||
requestSubject,
|
||||
htmlReqBody,
|
||||
variables
|
||||
);
|
||||
} else if (
|
||||
tenantMailTemplate?.body &&
|
||||
tenantMailTemplate?.subject
|
||||
) {
|
||||
const mailBody = tenantMailTemplate?.body;
|
||||
const mailSubject = tenantMailTemplate?.subject;
|
||||
const replacedRequestBody = mailBody.replace(/"/g, "'");
|
||||
const htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
const variables = {
|
||||
document_title: documentName,
|
||||
note: pdfDetails?.[0]?.Note,
|
||||
sender_name: senderName,
|
||||
sender_mail: senderEmail,
|
||||
sender_phone: senderPhone || "",
|
||||
receiver_name: signerMail[i]?.Name || "",
|
||||
receiver_email: signerMail[i].Email,
|
||||
receiver_phone: signerMail[i]?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: signPdf
|
||||
};
|
||||
replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
|
||||
}
|
||||
const mailparam = {
|
||||
senderName: senderName,
|
||||
note: pdfDetails?.[0]?.Note || "",
|
||||
senderMail: senderEmail,
|
||||
title: documentName,
|
||||
organization: orgName,
|
||||
localExpireDate: localExpireDate,
|
||||
signingUrl: signPdf
|
||||
};
|
||||
let params = {
|
||||
extUserId: owner?.objectId,
|
||||
recipient: signerMail[i].Email,
|
||||
subject: replaceVar?.subject
|
||||
? replaceVar?.subject
|
||||
: mailTemplate(mailparam).subject,
|
||||
replyto: senderEmail,
|
||||
from:
|
||||
senderEmail,
|
||||
html: replaceVar?.body
|
||||
? replaceVar?.body
|
||||
: mailTemplate(mailparam).body
|
||||
};
|
||||
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
}
|
||||
if (sendMail?.data?.result?.status === "success") {
|
||||
setMailStatus("success");
|
||||
try {
|
||||
let data;
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
isCustomize
|
||||
) {
|
||||
data = {
|
||||
RequestBody: htmlReqBody,
|
||||
RequestSubject: requestSubject,
|
||||
SendMail: true
|
||||
};
|
||||
} else if (
|
||||
tenantMailTemplate?.body &&
|
||||
tenantMailTemplate?.subject
|
||||
) {
|
||||
data = {
|
||||
RequestBody: tenantMailTemplate?.body,
|
||||
RequestSubject: tenantMailTemplate?.subject,
|
||||
SendMail: true
|
||||
};
|
||||
} else {
|
||||
data = { SendMail: true };
|
||||
}
|
||||
try {
|
||||
await axios.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${documentId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("axois err ", err);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
}
|
||||
setIsSend(true);
|
||||
setIsMailSend(true);
|
||||
setIsLoading({ isLoad: false });
|
||||
setIsUiLoading(false);
|
||||
} else if (sendMail?.data?.result?.status === "quota-reached") {
|
||||
setMailStatus("quotareached");
|
||||
setIsSend(true);
|
||||
setIsMailSend(true);
|
||||
setIsUiLoading(false);
|
||||
}
|
||||
else {
|
||||
setMailStatus("failed");
|
||||
setIsSend(true);
|
||||
setIsMailSend(true);
|
||||
setIsUiLoading(false);
|
||||
}
|
||||
};
|
||||
const handleDontShow = (isChecked) => {
|
||||
setIsDontShow(isChecked);
|
||||
};
|
||||
//here you can add your messages in content and selector is key of particular steps
|
||||
|
||||
const tourConfig = [
|
||||
{
|
||||
selector: '[data-tut="prefillTour"]',
|
||||
@@ -1460,6 +1276,9 @@ function PlaceHolderSign() {
|
||||
isHideLabel,
|
||||
layout
|
||||
) => {
|
||||
const isPrefill = signerPos.some(
|
||||
(x) => x?.Role === "prefill" && x.Id === uniqueId
|
||||
);
|
||||
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
|
||||
if (filterSignerPos.length > 0) {
|
||||
const getPlaceHolder = filterSignerPos[0].placeHolder;
|
||||
@@ -1525,16 +1344,21 @@ function PlaceHolderSign() {
|
||||
...position.options,
|
||||
name: dropdownName,
|
||||
values: dropdownOptions,
|
||||
validation: {
|
||||
minRequiredCount: minCount,
|
||||
maxRequiredCount: maxCount
|
||||
},
|
||||
...(!isPrefill
|
||||
? {
|
||||
validation: {
|
||||
minRequiredCount: minCount,
|
||||
maxRequiredCount: maxCount
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
defaultValue: defaultValue,
|
||||
layout: layout,
|
||||
isReadOnly: isReadOnly || false,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize: textSize || 12,
|
||||
fontColor: textColor || "black"
|
||||
fontColor: textColor || "black",
|
||||
...(isPrefill ? { status } : {})
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2076,90 +1900,17 @@ function PlaceHolderSign() {
|
||||
pdfDetails={pdfDetails}
|
||||
/>
|
||||
<div className=" w-full md:w-[95%] ">
|
||||
{/* this modal is used show alert set placeholder for all signers before send mail */}
|
||||
<ModalUi
|
||||
isOpen={isSendAlert.alert && isSendAlert.mssg === "confirm"}
|
||||
title={isSendAlert.mssg === "confirm" && t("send-mail")}
|
||||
handleClose={() => handleCloseSendmailModal()}
|
||||
>
|
||||
<div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content">
|
||||
{isSendAlert.mssg === "confirm" && (
|
||||
<>
|
||||
{!isCustomize && <span>{t("placeholder-alert-3")}</span>}
|
||||
{
|
||||
isCustomize && (
|
||||
<>
|
||||
<EmailBody
|
||||
editorRef={editorRef}
|
||||
requestBody={requestBody}
|
||||
requestSubject={requestSubject}
|
||||
handleOnchangeRequest={handleOnchangeRequest}
|
||||
setRequestSubject={setRequestSubject}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||
onClick={() => {
|
||||
setRequestBody(defaultBody);
|
||||
setRequestSubject(defaultSubject);
|
||||
}}
|
||||
>
|
||||
<span>{t("reset-to-default")}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
onClick={() => sendEmailToSigners()}
|
||||
className="op-btn op-btn-primary font-[500] text-sm shadow"
|
||||
>
|
||||
{t("send")}
|
||||
</button>
|
||||
{isCustomize && (
|
||||
<button
|
||||
onClick={() => setIsCustomize(false)}
|
||||
className="op-btn op-btn-ghost font-[500] text-sm"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
!isCustomize && (
|
||||
<span
|
||||
className="op-link op-link-accent text-sm"
|
||||
onClick={() => setIsCustomize(!isCustomize)}
|
||||
>
|
||||
{t("cutomize-email")}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isSendAlert.mssg === "confirm" && (
|
||||
<>
|
||||
<div className="flex justify-center items-center mt-3">
|
||||
<span className="h-[1px] w-[20%] bg-[#ccc]"></span>
|
||||
<span className="ml-[5px] mr-[5px]">{t("or")}</span>
|
||||
<span className="h-[1px] w-[20%] bg-[#ccc]"></span>
|
||||
</div>
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
<p id="copyUrl" ref={copyUrlRef} className="hidden"></p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
{/* this modal is used show send mail message and after send mail success message */}
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
title={
|
||||
mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
!pdfDetails[0]?.SendinOrder
|
||||
? mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
: t("mail-status-head")
|
||||
}
|
||||
handleClose={() => {
|
||||
setIsSend(false);
|
||||
@@ -2168,70 +1919,103 @@ function PlaceHolderSign() {
|
||||
}}
|
||||
>
|
||||
<div className="h-[100%] p-[20px] text-base-content">
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{pdfDetails[0].SendinOrder ? (
|
||||
<p>
|
||||
{isCurrUser
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signersdata[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-5">
|
||||
<div>
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{pdfDetails[0].SendinOrder ? (
|
||||
<p>
|
||||
{isCurrUser
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signersdata[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
)}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : mailStatus === "failed" ? (
|
||||
<p>{t("mail-failed")} </p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
<div className="mb-[10px]">
|
||||
{!pdfDetails[0]?.SendinOrder &&
|
||||
(mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
))}
|
||||
{isCurrUser && (
|
||||
<span className="mt-1">
|
||||
{t("placeholder-alert-5")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isCurrUser && <p>{t("placeholder-alert-5")}</p>}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-[10px]">
|
||||
{mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
)}
|
||||
{isCurrUser && (
|
||||
<p className="mt-1">{t("placeholder-alert-5")}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!mailStatus && (
|
||||
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
|
||||
)}
|
||||
{mailStatus !== "quotareached" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success"
|
||||
? "flex justify-center mt-1"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{isCurrUser && (
|
||||
<button
|
||||
onClick={() => handleRecipientSign()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
|
||||
{mailStatus !== "quotareached" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success"
|
||||
? "flex justify-center mt-1"
|
||||
: "flex items-center justify-center mt-7"
|
||||
}
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
{isCurrUser && (
|
||||
<button
|
||||
onClick={() => handleRecipientSign()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
>
|
||||
{t("sign-now")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setSignerPos([]);
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{isCurrUser ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setSignerPos([]);
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{isCurrUser ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
{mailStatus !== "success" &&
|
||||
isCurrUser &&
|
||||
pdfDetails[0]?.SendinOrder && (
|
||||
<div className="op-divider text-base-content mx-[0%] my-1 font-medium">
|
||||
{t("or")}
|
||||
</div>
|
||||
)}
|
||||
{mailStatus !== "success" &&
|
||||
pdfDetails[0]?.SendinOrder &&
|
||||
isCurrUser && (
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!mailStatus && (
|
||||
<div className="op-divider text-base-content mx-[0%] mt-3"></div>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
@@ -2478,6 +2262,18 @@ function PlaceHolderSign() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<CustomizeMail
|
||||
setIsMailModal={setIsMailModal}
|
||||
setCustomizeMail={setCustomizeMail}
|
||||
documentId={documentId}
|
||||
signerList={signersdata}
|
||||
setIsSend={setIsSend}
|
||||
setMailStatus={setMailStatus}
|
||||
customizeMail={customizeMail}
|
||||
defaultMail={defaultMail}
|
||||
isMailModal={isMailModal}
|
||||
handleShareList={handleShareList}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isShowModal[currWidgetsDetails?.key] && (
|
||||
@@ -2495,6 +2291,7 @@ function PlaceHolderSign() {
|
||||
setUniqueId={setUniqueId}
|
||||
signatureTypes={signatureType}
|
||||
penColors={pdfDetails?.[0]?.PenColors}
|
||||
role={roleName}
|
||||
/>
|
||||
)}
|
||||
<ModalUi
|
||||
|
||||
@@ -6,7 +6,6 @@ import Tooltip from "../primitives/Tooltip";
|
||||
import {
|
||||
getTenantDetails,
|
||||
handleSignatureType,
|
||||
openInNewTab,
|
||||
signatureTypes,
|
||||
usertimezone
|
||||
} from "../constant/Utils";
|
||||
@@ -16,15 +15,25 @@ import TimezoneSelector from "../components/preferences/TimezoneSelector";
|
||||
import DateFormatSelector from "../components/preferences/DateFormatSelector";
|
||||
import FilenameFormatSelector from "../components/preferences/FilenameFormatSelector";
|
||||
import axios from "axios";
|
||||
import MailTemplateEditor from "../components/preferences/MailTemplateEditor";
|
||||
import { withSessionValidation } from "../utils";
|
||||
import { WidgetsTab, EmailTab } from "../components/preferences/tabs";
|
||||
import {
|
||||
setUserInfo,
|
||||
setTenantInfo,
|
||||
setLoader,
|
||||
setTopLoader,
|
||||
setAlertInfo
|
||||
} from "../redux/reducers/userReducer";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
const Preferences = () => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTopLoader, setIsTopLoader] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const { isLoader, isTopLoader, alertInfo } = useSelector(
|
||||
(state) => state.user
|
||||
);
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState();
|
||||
@@ -42,21 +51,28 @@ const Preferences = () => {
|
||||
const [is12HourTime, setIs12HourTime] = useState(false);
|
||||
const [isLTVEnabled, setIsLTVEnabled] = useState(false);
|
||||
const [fileNameFormat, setFileNameFormat] = useState("DOCNAME");
|
||||
const [tenantInfo, setTenantInfo] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
fetchSignType();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const fetchSignType = async () => {
|
||||
setIsTopLoader(true);
|
||||
const showAlert = (type, msg) => {
|
||||
dispatch(setAlertInfo({ type, msg }));
|
||||
setTimeout(() => {
|
||||
dispatch(setAlertInfo({ type: "success", msg: "" }));
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const fetchSignType = withSessionValidation(async () => {
|
||||
dispatch(setTopLoader(true));
|
||||
const EmailTab = [
|
||||
{ name: "email", title: t("email"), icon: "fa-light fa-envelope" }
|
||||
];
|
||||
|
||||
const arr = [
|
||||
generaltab,
|
||||
{ name: "widgets", title: t("widgets"), icon: "fa-light fa-list" },
|
||||
...EmailTab,
|
||||
];
|
||||
setTab(arr);
|
||||
@@ -67,7 +83,7 @@ const Preferences = () => {
|
||||
)
|
||||
);
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
setTenantInfo(tenantDetails);
|
||||
dispatch(setTenantInfo(tenantDetails));
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const tenantSignTypes = signatureType?.filter((x) => x.enabled === true);
|
||||
const extUser = await axios.post(
|
||||
@@ -81,68 +97,74 @@ const Preferences = () => {
|
||||
}
|
||||
}
|
||||
);
|
||||
const getUser = extUser.data?.result;
|
||||
if (getUser) {
|
||||
const _getUser = JSON.parse(JSON.stringify(getUser));
|
||||
setIsNotifyOnSignatures(
|
||||
_getUser?.NotifyOnSignatures !== undefined
|
||||
? _getUser?.NotifyOnSignatures
|
||||
: true
|
||||
);
|
||||
setTimezone(_getUser?.Timezone || usertimezone);
|
||||
if (tenantSignTypes?.length > 0) {
|
||||
const signatureType = _getUser?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
signatureType
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
} else {
|
||||
const SignatureType = _getUser?.SignatureType || signatureTypes;
|
||||
setSignatureType(SignatureType);
|
||||
}
|
||||
setSendinOrder(
|
||||
_getUser?.SendinOrder !== undefined ? _getUser?.SendinOrder : true
|
||||
);
|
||||
setIsTourEnabled(
|
||||
_getUser?.IsTourEnabled !== undefined ? _getUser?.IsTourEnabled : true
|
||||
);
|
||||
setDateFormat(
|
||||
_getUser?.DateFormat !== undefined
|
||||
? _getUser?.DateFormat
|
||||
: "MM/DD/YYYY"
|
||||
);
|
||||
setIs12HourTime(
|
||||
_getUser?.Is12HourTime !== undefined ? _getUser?.Is12HourTime : false
|
||||
);
|
||||
setIsLTVEnabled(
|
||||
_getUser?.IsLTVEnabled !== undefined ? _getUser?.IsLTVEnabled : false
|
||||
);
|
||||
const downloadFilenameFormat =
|
||||
_getUser?.DownloadFilenameFormat || "DOCNAME";
|
||||
setFileNameFormat(downloadFilenameFormat);
|
||||
const getUser = extUser?.data?.result;
|
||||
if (!getUser) {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
return;
|
||||
}
|
||||
const _getUser = JSON.parse(JSON.stringify(getUser));
|
||||
dispatch(setUserInfo(_getUser));
|
||||
setIsNotifyOnSignatures(
|
||||
_getUser?.NotifyOnSignatures !== undefined
|
||||
? _getUser?.NotifyOnSignatures
|
||||
: true
|
||||
);
|
||||
setTimezone(_getUser?.Timezone || usertimezone);
|
||||
if (tenantSignTypes?.length > 0) {
|
||||
const signatureType = _getUser?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
signatureType
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
} else {
|
||||
setSignatureType(_getUser?.SignatureType || signatureTypes);
|
||||
}
|
||||
setSendinOrder(
|
||||
_getUser?.SendinOrder !== undefined ? _getUser?.SendinOrder : true
|
||||
);
|
||||
setIsTourEnabled(
|
||||
_getUser?.IsTourEnabled !== undefined ? _getUser?.IsTourEnabled : true
|
||||
);
|
||||
setDateFormat(
|
||||
_getUser?.DateFormat !== undefined ? _getUser?.DateFormat : "MM/DD/YYYY"
|
||||
);
|
||||
setIs12HourTime(
|
||||
_getUser?.Is12HourTime !== undefined ? _getUser?.Is12HourTime : false
|
||||
);
|
||||
setIsLTVEnabled(
|
||||
_getUser?.IsLTVEnabled !== undefined ? _getUser?.IsLTVEnabled : false
|
||||
);
|
||||
const downloadFilenameFormat =
|
||||
_getUser?.DownloadFilenameFormat || "DOCNAME";
|
||||
setFileNameFormat(downloadFilenameFormat);
|
||||
} catch (err) {
|
||||
console.error("Error while getting user details: ", err);
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
setIsTopLoader(false);
|
||||
dispatch(setTopLoader(false));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleCheckboxChange` is trigger when user enable/disable checkbox of respective type
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Create a copy of the signatureType array
|
||||
const updatedSignatureType = [...signatureType];
|
||||
// Toggle the enabled value for the clicked item
|
||||
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// Update the state with the modified array
|
||||
setSignatureType(updatedSignatureType);
|
||||
// // Create a copy of the signatureType array
|
||||
// const updatedSignatureType = [...signatureType];
|
||||
// // Toggle the enabled value for the clicked item
|
||||
// updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// // Update the state with the modified array
|
||||
// setSignatureType(updatedSignatureType);
|
||||
|
||||
setSignatureType((prev) =>
|
||||
prev.map((item, i) =>
|
||||
i === index ? { ...item, enabled: !item.enabled } : item
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// `handleSave` is used save updated value signature type
|
||||
const handleSave = async () => {
|
||||
setIsLoader(true);
|
||||
const handleSave = withSessionValidation(async () => {
|
||||
dispatch(setLoader(true));
|
||||
const Timezone = timezone || usertimezone;
|
||||
if (
|
||||
signatureType.length > 0 ||
|
||||
@@ -156,20 +178,12 @@ const Preferences = () => {
|
||||
enabledSignTypes?.length === 1 &&
|
||||
enabledSignTypes[0]?.name === "default";
|
||||
if (enabledSignTypes.length === 0) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("at-least-one-signature-type")
|
||||
});
|
||||
setIsLoader(false);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
showAlert("danger", t("at-least-one-signature-type"));
|
||||
dispatch(setLoader(false));
|
||||
return;
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("expect-default-one-signature-type")
|
||||
});
|
||||
setIsLoader(false);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
showAlert("danger", t("expect-default-one-signature-type"));
|
||||
dispatch(setLoader(false));
|
||||
return;
|
||||
} else {
|
||||
params = { ...params, SignatureType: signatureType };
|
||||
@@ -190,7 +204,7 @@ const Preferences = () => {
|
||||
};
|
||||
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
||||
if (updateRes) {
|
||||
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
||||
showAlert("success", t("saved-successfully"));
|
||||
let extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
@@ -207,13 +221,11 @@ const Preferences = () => {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error updating signature type: ", err);
|
||||
setIsAlert({ type: "danger", msg: err.message });
|
||||
showAlert("danger", err.message);
|
||||
}
|
||||
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
dispatch(setLoader(false));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
||||
const handleNotifySignChange = (value) => {
|
||||
@@ -227,7 +239,7 @@ const Preferences = () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
|
||||
{alertInfo.msg && <Alert type={alertInfo.type}>{alertInfo.msg}</Alert>}
|
||||
{isTopLoader ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<Loader />
|
||||
@@ -261,20 +273,25 @@ const Preferences = () => {
|
||||
onClick={() => setactiveTab(ind)}
|
||||
key={ind}
|
||||
role="tab"
|
||||
className={`${activeTab === ind ? "op-tab-active" : ""} op-tab text-xs md:text-base pb-2 md:pb-0 transition-all `}
|
||||
className={` op-tab text-xs md:text-base pb-2 md:pb-0 transition-all`}
|
||||
aria-selected={activeTab === ind}
|
||||
aria-controls={`panel-${tabData.title}`}
|
||||
>
|
||||
<i className={tabData.icon}></i>
|
||||
<span className="ml-1 md:ml-2">{tabData.title}</span>
|
||||
<span
|
||||
className={`${activeTab === ind ? "block" : "hidden"} md:block ml-1`}
|
||||
title={tabData?.title}
|
||||
>
|
||||
{tabData.title}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="panel-general"
|
||||
id={`panel-${activeTab}`}
|
||||
className="px-6 pt-4 pb-6"
|
||||
aria-labelledby="tab-general"
|
||||
aria-labelledby={`tab-${activeTab}`}
|
||||
role="tabpanel"
|
||||
>
|
||||
{tabName(activeTab) === "general" && (
|
||||
@@ -308,7 +325,9 @@ const Preferences = () => {
|
||||
<div className="p-[5px] ml-2">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">Draw: </span>
|
||||
<span className="font-bold">
|
||||
{t("draw")}:{" "}
|
||||
</span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l1")}
|
||||
</span>
|
||||
@@ -320,7 +339,9 @@ const Preferences = () => {
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Upload: </span>
|
||||
<span className="font-bold">
|
||||
{t("upload")}:{" "}
|
||||
</span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l3")}
|
||||
</span>
|
||||
@@ -616,20 +637,8 @@ const Preferences = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tabName(activeTab) === "email" && (
|
||||
<div className="flex flex-col mb-4">
|
||||
<MailTemplateEditor
|
||||
info={
|
||||
tenantInfo
|
||||
}
|
||||
tenantId={tenantInfo?.objectId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{tabName(activeTab) === "security" && (
|
||||
<>
|
||||
</>
|
||||
)}
|
||||
{tabName(activeTab) === "widgets" && <WidgetsTab />}
|
||||
{tabName(activeTab) === "email" && <EmailTab />}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ColumnSelector from "../components/ColumnSelector";
|
||||
import TemplatesReport from "../reports/template/TemplatesReport";
|
||||
import DocumentsReport from "../reports/document/DocumentsReport";
|
||||
import { templateReportTour } from "../json/ReportTour";
|
||||
import { withSessionValidation } from "../utils";
|
||||
|
||||
const Report = () => {
|
||||
const { id } = useParams();
|
||||
@@ -31,6 +32,7 @@ const Report = () => {
|
||||
const [columnLabels, setColumnLabels] = useState({});
|
||||
const [defaultColumns, setDefaultColumns] = useState([]);
|
||||
const [isColumnModal, setIsColumnModal] = useState(false);
|
||||
const [searchLoader, setSearchLoader] = useState(false);
|
||||
const debounceTimer = useRef(null);
|
||||
// Number of documents to display per page (should always be half of docLimit for proper pagination)
|
||||
const docPerPage = 10;
|
||||
@@ -73,13 +75,14 @@ const Report = () => {
|
||||
// eslint-disable-next-line
|
||||
}, [isNextRecord]);
|
||||
|
||||
const handleSearchChange = async (e) => {
|
||||
const handleSearchChange = withSessionValidation(async (e) => {
|
||||
const term = e.target.value.toLowerCase();
|
||||
setSearchTerm(term);
|
||||
if (debounceTimer.current) {
|
||||
clearTimeout(debounceTimer.current);
|
||||
}
|
||||
debounceTimer.current = setTimeout(async () => {
|
||||
setSearchLoader(true);
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -98,13 +101,15 @@ const Report = () => {
|
||||
setIsMoreDocs(data.length >= docPerPage);
|
||||
setIsNextRecord(false);
|
||||
setIsSearchResult(true);
|
||||
setSearchLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Search error:", err);
|
||||
setSearchLoader(false);
|
||||
}
|
||||
}, 300);
|
||||
setIsSearchResult(false);
|
||||
};
|
||||
});
|
||||
|
||||
const handleSearchPaste = (e) => {
|
||||
setTimeout(() => {
|
||||
@@ -119,113 +124,113 @@ const Report = () => {
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
const getReportData = async (
|
||||
skipUserRecord = 0,
|
||||
limit = 20,
|
||||
term = searchTerm
|
||||
) => {
|
||||
// setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
setActions(json.actions);
|
||||
const savedCols = JSON.parse(
|
||||
localStorage.getItem("reportColumns") || "{}"
|
||||
);
|
||||
const visible = savedCols[id]?.visible || json.heading;
|
||||
const labels = savedCols[id]?.labels || {};
|
||||
if (!savedCols[id] || id === "contacts") {
|
||||
savedCols[id] = { visible: json.heading, labels: {} };
|
||||
localStorage.setItem("reportColumns", JSON.stringify(savedCols));
|
||||
}
|
||||
setVisibleColumns(visible);
|
||||
setColumnLabels(labels);
|
||||
setHeading(visible);
|
||||
setDefaultColumns(json.heading);
|
||||
setReportName(json.reportName);
|
||||
setReportHelp(json?.helpMsg);
|
||||
const currentUser = Parse.User.current().id;
|
||||
const getReportData = withSessionValidation(
|
||||
async (skipUserRecord = 0, limit = 20, term = searchTerm) => {
|
||||
// setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
setActions(json.actions);
|
||||
const savedCols = JSON.parse(
|
||||
localStorage.getItem("reportColumns") || "{}"
|
||||
);
|
||||
const visible = savedCols[id]?.visible || json.heading;
|
||||
const labels = savedCols[id]?.labels || {};
|
||||
if (!savedCols[id] || id === "contacts") {
|
||||
savedCols[id] = { visible: json.heading, labels: {} };
|
||||
localStorage.setItem("reportColumns", JSON.stringify(savedCols));
|
||||
}
|
||||
setVisibleColumns(visible);
|
||||
setColumnLabels(labels);
|
||||
setHeading(visible);
|
||||
setDefaultColumns(json.heading);
|
||||
setReportName(json.reportName);
|
||||
setReportHelp(json?.helpMsg);
|
||||
const currentUser = Parse.User.current().id;
|
||||
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const skipRecord = id === "4Hhwbp482K" ? 0 : skipUserRecord;
|
||||
const limitRecord = id === "4Hhwbp482K" ? 200 : limit;
|
||||
const params = { reportId: id, skip: skipRecord, limit: limitRecord };
|
||||
if (term) {
|
||||
params.searchTerm = term;
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
});
|
||||
const extraHeads =
|
||||
id === "4Hhwbp482K" ? [...extraCols, "Expiry Date"] : extraCols;
|
||||
setAllColumns(Array.from(new Set([...json.heading, ...extraHeads])));
|
||||
if (id === "6TeaPr321t") {
|
||||
setTourData(templateReportTour);
|
||||
}
|
||||
if (id === "4Hhwbp482K") {
|
||||
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj?.Signers?.some(
|
||||
(item) => item.UserId.objectId === currentUser
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const skipRecord = id === "4Hhwbp482K" ? 0 : skipUserRecord;
|
||||
const limitRecord = id === "4Hhwbp482K" ? 200 : limit;
|
||||
const params = { reportId: id, skip: skipRecord, limit: limitRecord };
|
||||
if (term) {
|
||||
params.searchTerm = term;
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
});
|
||||
const extraHeads =
|
||||
id === "4Hhwbp482K" ? [...extraCols, "Expiry Date"] : extraCols;
|
||||
setAllColumns(Array.from(new Set([...json.heading, ...extraHeads])));
|
||||
if (id === "6TeaPr321t") {
|
||||
setTourData(templateReportTour);
|
||||
}
|
||||
if (id === "4Hhwbp482K") {
|
||||
const listData = res.data?.result.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
let arr = [];
|
||||
for (const obj of listData) {
|
||||
const isSigner = obj?.Signers?.some(
|
||||
(item) => item.UserId.objectId === currentUser
|
||||
);
|
||||
if (isSigner) {
|
||||
let isRecord;
|
||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||
isRecord = obj?.AuditTrail.some(
|
||||
(item) =>
|
||||
item?.UserPtr?.UserId?.objectId === currentUser &&
|
||||
item.Activity === "Signed"
|
||||
);
|
||||
} else {
|
||||
isRecord = false;
|
||||
}
|
||||
if (isRecord === false) {
|
||||
arr.push(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arr.length === docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
|
||||
);
|
||||
} else {
|
||||
if (res.data.result.length >= docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
if (!res.data.result.error) {
|
||||
setIsNextRecord(false);
|
||||
if (arr.length === docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
|
||||
);
|
||||
} else {
|
||||
if (res.data.result.length >= docPerPage) {
|
||||
setIsMoreDocs(true);
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
if (!res.data.result.error) {
|
||||
setIsNextRecord(false);
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
const isCancel = axios.isCancel(err);
|
||||
if (!isCancel) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
const isCancel = axios.isCancel(err);
|
||||
if (!isCancel) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const commonProps = {
|
||||
ReportName: reportName,
|
||||
@@ -243,6 +248,7 @@ const Report = () => {
|
||||
handleSearchPaste,
|
||||
isSearchResult,
|
||||
columnLabels,
|
||||
searchLoader,
|
||||
openColumnModal: () => setIsColumnModal(true)
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -381,7 +381,8 @@ function SignYourSelf() {
|
||||
dragTypeValue,
|
||||
getWidgetValue,
|
||||
owner,
|
||||
xyPosition
|
||||
xyPosition,
|
||||
true
|
||||
)
|
||||
};
|
||||
dropData.push(dropObj);
|
||||
@@ -434,7 +435,9 @@ function SignYourSelf() {
|
||||
dragTypeValue,
|
||||
getWidgetValue,
|
||||
owner,
|
||||
xyPosition
|
||||
xyPosition,
|
||||
pdfDetails?.[0],
|
||||
true
|
||||
),
|
||||
scale: containerScale
|
||||
};
|
||||
|
||||
@@ -64,10 +64,7 @@ import Alert from "../primitives/Alert";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import * as utils from "../utils";
|
||||
import CustomizeMail from "../components/pdf/CustomizeMail";
|
||||
import {
|
||||
resetWidgetState,
|
||||
setPrefillImg
|
||||
} from "../redux/reducers/widgetSlice.js";
|
||||
import { resetWidgetState, setPrefillImg } from "../redux/reducers/widgetSlice";
|
||||
import ShareButton from "../primitives/ShareButton";
|
||||
|
||||
const TemplatePlaceholder = () => {
|
||||
@@ -235,7 +232,7 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
};
|
||||
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
||||
const fetchTemplate = async () => {
|
||||
const fetchTemplate = utils.withSessionValidation(async () => {
|
||||
try {
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const params = { templateId: templateId };
|
||||
@@ -419,7 +416,7 @@ const TemplatePlaceholder = () => {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
//function for setting position after drop signature button over pdf
|
||||
const addPositionOfSignature = (item, monitor) => {
|
||||
@@ -974,6 +971,15 @@ const TemplatePlaceholder = () => {
|
||||
showAlert("danger", "please attach all role to signer");
|
||||
} else if (res?.status === "success") {
|
||||
setDocumentId(res.id);
|
||||
const ownerId = pdfDetails[0].ExtUserPtr?.UserId?.objectId;
|
||||
const firstSigner = signersdata[0];
|
||||
const isOwner = firstSigner?.UserId?.objectId === ownerId;
|
||||
if (pdfDetails[0]?.SendinOrder && isOwner) {
|
||||
setCurrUserId(firstSigner?.objectId);
|
||||
setIsSend(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsMailModal(true);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -1333,6 +1339,9 @@ const TemplatePlaceholder = () => {
|
||||
isHideLabel,
|
||||
layout
|
||||
) => {
|
||||
const isPrefill = signerPos.some(
|
||||
(x) => x?.Role === "prefill" && x.Id === uniqueId
|
||||
);
|
||||
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
|
||||
if (filterSignerPos.length > 0) {
|
||||
const getPlaceHolder = filterSignerPos[0].placeHolder;
|
||||
@@ -1400,16 +1409,21 @@ const TemplatePlaceholder = () => {
|
||||
...position.options,
|
||||
name: dropdownName,
|
||||
values: dropdownOptions,
|
||||
validation: {
|
||||
minRequiredCount: minCount,
|
||||
maxRequiredCount: maxCount
|
||||
},
|
||||
...(!isPrefill
|
||||
? {
|
||||
validation: {
|
||||
minRequiredCount: minCount,
|
||||
maxRequiredCount: maxCount
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
layout: layout,
|
||||
isReadOnly: isReadOnly || false,
|
||||
defaultValue: defaultValue,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize: textSize || 12,
|
||||
fontColor: textColor || "black"
|
||||
fontColor: textColor || "black",
|
||||
...(isPrefill ? { status } : {})
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1704,7 +1718,6 @@ const TemplatePlaceholder = () => {
|
||||
setIsCreateDocModal(true);
|
||||
setIsUiLoading(false);
|
||||
};
|
||||
|
||||
const handleUseButton = async () => {
|
||||
setIsCreateDocModal(false);
|
||||
//this function is used to open modal to show signers list
|
||||
@@ -1713,8 +1726,9 @@ const TemplatePlaceholder = () => {
|
||||
};
|
||||
|
||||
const handleRecipientSign = (docId, currUserId) => {
|
||||
const docObjId = docId || documentId;
|
||||
if (currUserId) {
|
||||
navigate(`/recipientSignPdf/${docId}/${currUserId}`);
|
||||
navigate(`/recipientSignPdf/${docObjId}/${currUserId}`);
|
||||
} else {
|
||||
navigate(`/recipientSignPdf/${docId}`);
|
||||
}
|
||||
@@ -2163,6 +2177,7 @@ const TemplatePlaceholder = () => {
|
||||
setUniqueId={setUniqueId}
|
||||
signatureTypes={signatureType}
|
||||
penColors={pdfDetails?.[0]?.PenColors}
|
||||
role={roleName}
|
||||
/>
|
||||
)}
|
||||
<RotateAlert
|
||||
@@ -2187,11 +2202,13 @@ const TemplatePlaceholder = () => {
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
title={
|
||||
mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
!pdfDetails[0]?.SendinOrder
|
||||
? mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
: t("mail-status-head")
|
||||
}
|
||||
handleClose={() => {
|
||||
setIsSend(false);
|
||||
@@ -2199,66 +2216,103 @@ const TemplatePlaceholder = () => {
|
||||
}}
|
||||
>
|
||||
<div className="h-[100%] p-[20px] text-base-content">
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{documentDetails.SendinOrder ? (
|
||||
<p>
|
||||
{currUserId
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signersdata[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-5">
|
||||
<div>
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{documentDetails.SendinOrder ? (
|
||||
<p>
|
||||
{currUserId
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signersdata[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
)}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : mailStatus === "failed" ? (
|
||||
<p>{t("mail-failed")} </p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
<div className="mb-[10px]">
|
||||
{!pdfDetails[0]?.SendinOrder &&
|
||||
(mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
))}
|
||||
{currUserId && (
|
||||
<span className="mt-1">{t("placeholder-alert-5")}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{currUserId && <p>{t("placeholder-alert-5")}</p>}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-[10px]">
|
||||
{mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
)}
|
||||
{currUserId && <p className="mt-1">{t("placeholder-alert-5")}</p>}
|
||||
</div>
|
||||
)}
|
||||
{!mailStatus && (
|
||||
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
|
||||
)}
|
||||
{mailStatus !== "quotareached" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success" ? "flex justify-center mt-1" : ""
|
||||
}
|
||||
>
|
||||
{currUserId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleRecipientSign(documentDetails?.objectId, currUserId)
|
||||
{mailStatus !== "quotareached" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success"
|
||||
? "flex justify-center mt-1"
|
||||
: "flex items-center justify-center mt-7"
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
{currUserId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleRecipientSign(
|
||||
documentDetails?.objectId,
|
||||
currUserId
|
||||
)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
>
|
||||
{t("sign-now")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{currUserId ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{currUserId ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
{mailStatus !== "success" &&
|
||||
currUserId &&
|
||||
pdfDetails[0]?.SendinOrder && (
|
||||
<div className="op-divider text-base-content mx-[0%] my-1 font-medium">
|
||||
{t("or")}
|
||||
</div>
|
||||
)}
|
||||
{mailStatus !== "success" &&
|
||||
currUserId &&
|
||||
pdfDetails[0]?.SendinOrder && (
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!mailStatus && (
|
||||
<div className="op-divider text-base-content mx-[0%] mt-3"></div>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
@@ -14,6 +14,7 @@ import DeleteUserModal from "../primitives/DeleteUserModal";
|
||||
import axios from "axios";
|
||||
import PasswordResetModal from "../primitives/PasswordResetModal";
|
||||
import { usersActions } from "../json/ReportJson";
|
||||
import { withSessionValidation } from "../utils";
|
||||
|
||||
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
||||
const UserList = () => {
|
||||
@@ -168,7 +169,7 @@ const UserList = () => {
|
||||
};
|
||||
const handleClose = () => setIsActiveModal({});
|
||||
|
||||
const handleToggleSubmit = async (user) => {
|
||||
const handleToggleSubmit = withSessionValidation(async (user) => {
|
||||
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
|
||||
if (index !== -1) {
|
||||
setIsActiveModal({});
|
||||
@@ -193,7 +194,7 @@ const UserList = () => {
|
||||
setIsActLoader({});
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleToggleBtn = (user) => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
@@ -204,7 +205,7 @@ const UserList = () => {
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), timer);
|
||||
};
|
||||
|
||||
const handleDeleteAccount = async (item) => {
|
||||
const handleDeleteAccount = withSessionValidation(async (item) => {
|
||||
setDeleting(true);
|
||||
if (item?.UserId?.objectId) {
|
||||
const url = localStorage.getItem("baseUrl")?.replace(/\/app\/?$/, "/");
|
||||
@@ -230,16 +231,16 @@ const UserList = () => {
|
||||
setDeleteUserRes(t("something-went-wrong-mssg"));
|
||||
setDeleting(false);
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleCloseModal = () => {
|
||||
setIsActModal({});
|
||||
setDeleteUserRes("");
|
||||
setDeleting(false);
|
||||
};
|
||||
|
||||
const handleActionBtn = async (act, item) => {
|
||||
const handleActionBtn = withSessionValidation(async (act, item) => {
|
||||
setIsActModal({ [`${act.action}_${item.objectId}`]: true });
|
||||
};
|
||||
});
|
||||
const handleBtnVisibility = (act, item) => {
|
||||
if (act.restrictAdmin) {
|
||||
if (item?.UserRole === "contracts_Admin") {
|
||||
@@ -264,7 +265,7 @@ const UserList = () => {
|
||||
}
|
||||
};
|
||||
|
||||
async function submitPassword(userId, password) {
|
||||
const submitPassword = withSessionValidation(async (userId, password) => {
|
||||
setIsLoader(true);
|
||||
setIsActModal({});
|
||||
try {
|
||||
@@ -277,7 +278,7 @@ const UserList = () => {
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div className="relative">
|
||||
{isLoader && (
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Navigate, useNavigate } from "react-router";
|
||||
import Parse from "parse";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import dp from "../assets/images/dp.png";
|
||||
import { sanitizeFileName } from "../utils";
|
||||
import { sanitizeFileName, withSessionValidation } from "../utils";
|
||||
import axios from "axios";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import {
|
||||
@@ -124,7 +124,7 @@ function UserProfile() {
|
||||
};
|
||||
|
||||
// `updateExtUser` is used to update user details in extended class
|
||||
const updateExtUser = async (obj) => {
|
||||
const updateExtUser = withSessionValidation(async (obj) => {
|
||||
try {
|
||||
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const ExtUserId = extData?.[0]?.objectId;
|
||||
@@ -152,10 +152,10 @@ function UserProfile() {
|
||||
const json = JSON.parse(JSON.stringify([res]));
|
||||
const extRes = JSON.stringify(json);
|
||||
localStorage.setItem("Extand_Class", extRes);
|
||||
} catch (e) {
|
||||
console.log("error in save data in contracts_Users class");
|
||||
} catch (err) {
|
||||
console.log("error in save data in contracts_Users class", err);
|
||||
}
|
||||
};
|
||||
});
|
||||
// file upload function
|
||||
const fileUpload = async (file) => {
|
||||
if (file) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatDate } from "../constant/Utils";
|
||||
import { formatDateToDdMmmYyyy } from "../constant/Utils";
|
||||
import SignerCell from "./SignerCell";
|
||||
|
||||
function isValidDateString(str) {
|
||||
@@ -14,12 +14,12 @@ const formatRow = (row) => {
|
||||
if (Array.isArray(row)) {
|
||||
return row.map((x) => x.Name).join(", ");
|
||||
} else if (typeof row === "object" && row !== null) {
|
||||
return row?.iso ? formatDate(row?.iso) : row?.Name || "-";
|
||||
return row?.iso ? formatDateToDdMmmYyyy(row?.iso) : row?.Name || "-";
|
||||
} else if (typeof row === "boolean" && row !== null) {
|
||||
return row ? row?.toString() : "false";
|
||||
} else if (isValidDateString(row) && row !== null) {
|
||||
// handle createdAt and updatedAt
|
||||
return formatDate(row) || "-";
|
||||
return formatDateToDdMmmYyyy(row) || "-";
|
||||
} else {
|
||||
return row || "-";
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export const RenderReportCell = ({
|
||||
<div className="font-semibold break-words">{rowData?.Name}</div>
|
||||
{rowData?.ExpiryDate?.iso && (
|
||||
<div className="text-gray-500">
|
||||
{t("expires")} {formatDate(rowData?.ExpiryDate?.iso)}
|
||||
{t("expires")} {formatDateToDdMmmYyyy(rowData?.ExpiryDate?.iso)}
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
@@ -133,25 +133,29 @@ export const RenderReportCell = ({
|
||||
case "Created Date":
|
||||
return (
|
||||
<td key={col} className="p-2 text-center">
|
||||
{rowData?.createdAt ? formatDate(rowData?.createdAt) : "-"}
|
||||
{rowData?.createdAt ? formatDateToDdMmmYyyy(rowData?.createdAt) : "-"}
|
||||
</td>
|
||||
);
|
||||
case "Updated Date":
|
||||
return (
|
||||
<td key={col} className="p-2 text-center">
|
||||
{rowData?.updatedAt ? formatDate(rowData?.updatedAt) : "-"}
|
||||
{rowData?.updatedAt ? formatDateToDdMmmYyyy(rowData?.updatedAt) : "-"}
|
||||
</td>
|
||||
);
|
||||
case "Expiry Date":
|
||||
return (
|
||||
<td key={col} className="p-2 text-center">
|
||||
{rowData?.ExpiryDate ? formatDate(rowData?.ExpiryDate?.iso) : "-"}
|
||||
{rowData?.ExpiryDate
|
||||
? formatDateToDdMmmYyyy(rowData?.ExpiryDate?.iso)
|
||||
: "-"}
|
||||
</td>
|
||||
);
|
||||
case "Sent Date":
|
||||
return (
|
||||
<td key={col} className="p-2 text-center">
|
||||
{rowData?.DocSentAt ? formatDate(rowData?.DocSentAt?.iso) : "-"}
|
||||
{rowData?.DocSentAt
|
||||
? formatDateToDdMmmYyyy(rowData?.DocSentAt?.iso)
|
||||
: "-"}
|
||||
</td>
|
||||
);
|
||||
case "Signers":
|
||||
|
||||
@@ -1,15 +1,47 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
const initialState = { isValidSession: true };
|
||||
const initialState = {
|
||||
userInfo: {},
|
||||
isValidSession: true,
|
||||
isLoader: false,
|
||||
isTopLoader: false,
|
||||
tenantInfo: {},
|
||||
alertInfo: { type: "success", msg: "" }
|
||||
};
|
||||
const userReducer = createSlice({
|
||||
name: "user",
|
||||
initialState: initialState,
|
||||
reducers: {
|
||||
setUserInfo: (state, action) => {
|
||||
state.userInfo = action.payload;
|
||||
},
|
||||
sessionStatus: (state, action) => {
|
||||
state.isValidSession = action.payload;
|
||||
},
|
||||
setTenantInfo: (state, action) => {
|
||||
state.tenantInfo = action.payload;
|
||||
},
|
||||
setLoader: (state, action) => {
|
||||
state.isLoader = action.payload;
|
||||
},
|
||||
setTopLoader: (state, action) => {
|
||||
state.isTopLoader = action.payload;
|
||||
},
|
||||
setAlertInfo: (state, action) => {
|
||||
state.alertInfo = {
|
||||
type: action.payload?.type,
|
||||
msg: action.payload?.msg
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const { sessionStatus } = userReducer.actions;
|
||||
export const {
|
||||
setUserInfo,
|
||||
sessionStatus,
|
||||
setTenantInfo,
|
||||
setLoader,
|
||||
setTopLoader,
|
||||
setAlertInfo
|
||||
} = userReducer.actions;
|
||||
|
||||
export default userReducer.reducer;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { serverUrl_fn } from "../../constant/appinfo";
|
||||
import { useElSize } from "../../hook/useElSize";
|
||||
import ImportContact from "./ImportContact";
|
||||
import AddContact from "../../primitives/AddContact";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
const Contactbook = (props) => {
|
||||
const titleRef = useRef(null);
|
||||
@@ -110,7 +111,7 @@ const Contactbook = (props) => {
|
||||
}
|
||||
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
|
||||
|
||||
const handleActionBtn = async (act, item) => {
|
||||
const handleActionBtn = withSessionValidation(async (act, item) => {
|
||||
if (act.action === "delete") {
|
||||
setIsDeleteModal({ [item.objectId]: true });
|
||||
} else if (act.action === "option") {
|
||||
@@ -119,7 +120,7 @@ const Contactbook = (props) => {
|
||||
setContact(item);
|
||||
setIsModal({ [`edit_${item.objectId}`]: true });
|
||||
}
|
||||
};
|
||||
});
|
||||
// Get current list
|
||||
const indexOfLastDoc = currentPage * props.docPerPage;
|
||||
const indexOfFirstDoc = indexOfLastDoc - props.docPerPage;
|
||||
@@ -163,7 +164,7 @@ const Contactbook = (props) => {
|
||||
props.setList((prevData) => [data, ...prevData]);
|
||||
};
|
||||
|
||||
const handleDelete = async (item) => {
|
||||
const handleDelete = withSessionValidation(async (item) => {
|
||||
setIsDeleteModal({});
|
||||
setActLoader({ [`${item.objectId}`]: true });
|
||||
try {
|
||||
@@ -191,7 +192,7 @@ const Contactbook = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleClose = () => {
|
||||
setIsDeleteModal({});
|
||||
};
|
||||
@@ -336,6 +337,7 @@ const Contactbook = (props) => {
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{props.List?.length > 0 &&
|
||||
!props.searchLoader &&
|
||||
currentList.map((item, index) => (
|
||||
<tr className="last:border-none border-y-[1px]" key={index}>
|
||||
{props.heading.includes("Sr.No") && (
|
||||
@@ -409,18 +411,27 @@ const Contactbook = (props) => {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{props.List?.length <= 0 && (
|
||||
{(props.searchLoader || props.List?.length <= 0) && (
|
||||
<div className="flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4">
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt={t("no-data-avaliable")}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
{props.searchLoader ? (
|
||||
<>
|
||||
<Loader />
|
||||
<div className="text-sm ">{t("loading-mssg")}</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt={t("no-data-avaliable")}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,12 +2,10 @@ import { useEffect, useState } from "react";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../../redux/reducers/userReducer";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
const EditContactForm = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
Name: "",
|
||||
@@ -31,54 +29,45 @@ const EditContactForm = (props) => {
|
||||
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
const handleSubmit = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const sessionToken = Parse.User?.current()?.getSessionToken();
|
||||
if (localStorage.getItem("TenantId") && sessionToken) {
|
||||
if (props.handleEditContact) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const params = {
|
||||
contactId: props.contact.objectId,
|
||||
name: formData.Name,
|
||||
email: formData.Email,
|
||||
phone: formData?.Phone,
|
||||
company: formData?.Company,
|
||||
jobTitle: formData?.JobTitle,
|
||||
tenantId: localStorage.getItem("TenantId")
|
||||
};
|
||||
const res = await Parse.Cloud.run("editcontact", params);
|
||||
const updateContact = {
|
||||
...res,
|
||||
Name: formData.Name,
|
||||
Email: formData.Email,
|
||||
Phone: formData?.Phone,
|
||||
Company: formData?.Company,
|
||||
JobTitle: formData?.JobTitle
|
||||
};
|
||||
props.handleEditContact(updateContact);
|
||||
} catch (err) {
|
||||
console.log("err in edit contact ", err);
|
||||
if (err.code === 137) {
|
||||
alert(t("contact-already-exists"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
props.handleClose && props.handleClose();
|
||||
}
|
||||
|
||||
if (props.handleEditContact) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const params = {
|
||||
contactId: props.contact.objectId,
|
||||
name: formData.Name,
|
||||
email: formData.Email,
|
||||
phone: formData?.Phone,
|
||||
company: formData?.Company,
|
||||
jobTitle: formData?.JobTitle,
|
||||
tenantId: localStorage.getItem("TenantId")
|
||||
};
|
||||
const res = await Parse.Cloud.run("editcontact", params);
|
||||
const updateContact = {
|
||||
...res,
|
||||
Name: formData.Name,
|
||||
Email: formData.Email,
|
||||
Phone: formData?.Phone,
|
||||
Company: formData?.Company,
|
||||
JobTitle: formData?.JobTitle
|
||||
};
|
||||
props.handleEditContact(updateContact);
|
||||
} catch (err) {
|
||||
console.log("err in edit contact ", err);
|
||||
if (err.code === 137) {
|
||||
alert(t("contact-already-exists"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
dispatch(sessionStatus(false));
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
props.handleClose && props.handleClose();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("invalid session or missing tenantId ", err);
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
};
|
||||
});
|
||||
return (
|
||||
<div className="h-full p-[20px]">
|
||||
{isLoader && (
|
||||
|
||||
@@ -3,12 +3,10 @@ import { useTranslation } from "react-i18next";
|
||||
import * as XLSX from "xlsx";
|
||||
import Parse from "parse";
|
||||
import { emailRegex } from "../../constant/const";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../../redux/reducers/userReducer";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
|
||||
const ImportContact = ({ setLoader, onImport, showAlert }) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [currentImportPage, setCurrentImportPage] = useState(1);
|
||||
const [importedData, setImportedData] = useState([]);
|
||||
const [invalidRecords, setInvalidRecords] = useState(0);
|
||||
@@ -181,52 +179,42 @@ const ImportContact = ({ setLoader, onImport, showAlert }) => {
|
||||
}
|
||||
};
|
||||
// `handleImportData` is used to create batch in contact
|
||||
const handleImportData = async (e) => {
|
||||
const handleImportData = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLoader(true);
|
||||
try {
|
||||
const sessionToken = Parse.User?.current()?.getSessionToken();
|
||||
if (localStorage.getItem("TenantId") && sessionToken) {
|
||||
try {
|
||||
const filterdata = importedData.map((x) => ({
|
||||
Name: x.Name,
|
||||
Email: x.Email,
|
||||
Phone: x.Phone,
|
||||
Company: x.Company,
|
||||
JobTitle: x.JobTitle,
|
||||
TenantId: localStorage.getItem("TenantId")
|
||||
}));
|
||||
const contacts = JSON.stringify(filterdata);
|
||||
const res = await Parse.Cloud.run("createbatchcontact", { contacts });
|
||||
if (res) {
|
||||
showAlert(
|
||||
"info",
|
||||
t("contact-imported", {
|
||||
imported: res?.success || 0,
|
||||
failed: res?.failed || 0
|
||||
})
|
||||
);
|
||||
if (res?.success > 0) {
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while creating batch contact", err);
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
onImport && onImport();
|
||||
setImportedData([]);
|
||||
setInvalidRecords(0);
|
||||
const filterdata = importedData.map((x) => ({
|
||||
Name: x.Name,
|
||||
Email: x.Email,
|
||||
Phone: x.Phone,
|
||||
Company: x.Company,
|
||||
JobTitle: x.JobTitle,
|
||||
TenantId: localStorage.getItem("TenantId")
|
||||
}));
|
||||
const contacts = JSON.stringify(filterdata);
|
||||
const res = await Parse.Cloud.run("createbatchcontact", { contacts });
|
||||
if (res) {
|
||||
showAlert(
|
||||
"info",
|
||||
t("contact-imported", {
|
||||
imported: res?.success || 0,
|
||||
failed: res?.failed || 0
|
||||
})
|
||||
);
|
||||
if (res?.success > 0) {
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
}
|
||||
} else {
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("invalid session or missing tenantId ", err);
|
||||
dispatch(sessionStatus(false));
|
||||
console.log("err while creating batch contact", err);
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
onImport && onImport();
|
||||
setImportedData([]);
|
||||
setInvalidRecords(0);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={handleImportData} className="p-[20px] h-full">
|
||||
|
||||
@@ -185,7 +185,7 @@ const DocumentsReport = (props) => {
|
||||
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const fetchTenantDetails = utils.withSessionValidation(async () => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
@@ -224,14 +224,14 @@ const DocumentsReport = (props) => {
|
||||
} else {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleURL` is used to open microapp
|
||||
const handleURL = async (item, act) => {
|
||||
navigate(`/${act.redirectUrl}?docId=${item?.objectId}`);
|
||||
};
|
||||
|
||||
const fetchTemplate = async (templateId) => {
|
||||
const fetchTemplate = utils.withSessionValidation(async (templateId) => {
|
||||
try {
|
||||
const params = {
|
||||
templateId: templateId,
|
||||
@@ -257,24 +257,26 @@ const DocumentsReport = (props) => {
|
||||
setActLoader({});
|
||||
console.log("Error to fetch template in report", e);
|
||||
}
|
||||
};
|
||||
});
|
||||
//function is called when there ther no any prefill role widget exist then create direct document and navigate
|
||||
const navigatePageToDoc = async (templateRes, placeholder, signer) => {
|
||||
setIsPrefillModal({});
|
||||
const res = await createDocument(
|
||||
[templateRes || templateDeatils],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDeatils?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
state: { title: "Use Template" }
|
||||
});
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
const navigatePageToDoc = utils.withSessionValidation(
|
||||
async (templateRes, placeholder, signer) => {
|
||||
setIsPrefillModal({});
|
||||
const res = await createDocument(
|
||||
[templateRes || templateDeatils],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDeatils?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
state: { title: "Use Template" }
|
||||
});
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
const handleUseTemplate = async (templateId, item) => {
|
||||
try {
|
||||
const templateDeatils = await fetchTemplate(templateId);
|
||||
@@ -303,7 +305,7 @@ const DocumentsReport = (props) => {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
const handleActionBtn = async (act, item) => {
|
||||
const handleActionBtn = utils.withSessionValidation(async (act, item) => {
|
||||
if (act.action === "redirect") {
|
||||
handleURL(item, act);
|
||||
} else if (act.action === "delete") {
|
||||
@@ -329,7 +331,7 @@ const DocumentsReport = (props) => {
|
||||
} else if (act.action === "extendexpiry") {
|
||||
setIsModal({ [`extendexpiry_${item.objectId}`]: true });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// Get current list
|
||||
@@ -352,7 +354,7 @@ const DocumentsReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (item) => {
|
||||
const handleDelete = utils.withSessionValidation(async (item) => {
|
||||
setIsDeleteModal({});
|
||||
setActLoader({ [`${item.objectId}`]: true });
|
||||
try {
|
||||
@@ -380,7 +382,7 @@ const DocumentsReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleClose = () => {
|
||||
setIsRevoke({});
|
||||
setIsDeleteModal({});
|
||||
@@ -429,7 +431,7 @@ const DocumentsReport = (props) => {
|
||||
setCopied({ [email]: true });
|
||||
};
|
||||
//function to handle revoke/decline docment
|
||||
const handleRevoke = async (item) => {
|
||||
const handleRevoke = utils.withSessionValidation(async (item) => {
|
||||
const senderUser = localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
@@ -477,7 +479,7 @@ const DocumentsReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
setActLoader({});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
// `handleDownload` is used to get valid doc url available in completed report
|
||||
const handleDownload = async (item) => {
|
||||
@@ -635,7 +637,7 @@ const DocumentsReport = (props) => {
|
||||
setMail((prev) => ({ ...prev, subject: res.subject, body: res.body }));
|
||||
setIsNextStep({ [user.Id]: true });
|
||||
};
|
||||
const handleResendMail = async (e, doc, user) => {
|
||||
const handleResendMail = utils.withSessionValidation(async (e, doc, user) => {
|
||||
e.preventDefault();
|
||||
setActLoader({ [user?.Id]: true });
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||
@@ -672,7 +674,7 @@ const DocumentsReport = (props) => {
|
||||
setUserDetails({});
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const fetchUserStatus = (user, doc) => {
|
||||
const email = user.email ? user.email : user.signerPtr.Email;
|
||||
const audit = doc?.AuditTrail?.find((x) => x.UserPtr.Email === email);
|
||||
@@ -707,7 +709,7 @@ const DocumentsReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateExpiry = async (e, item) => {
|
||||
const handleUpdateExpiry = utils.withSessionValidation(async (e, item) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (expiryDate) {
|
||||
@@ -758,10 +760,10 @@ const DocumentsReport = (props) => {
|
||||
} else {
|
||||
showAlert("danger", t("expiry-date-error"), 2000);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleRenameDoc` is used to update document name
|
||||
const handleRenameDoc = async (item) => {
|
||||
const handleRenameDoc = utils.withSessionValidation(async (item) => {
|
||||
setActLoader({ [item.objectId]: true });
|
||||
setIsModal({});
|
||||
const className = "contracts_Document";
|
||||
@@ -781,7 +783,7 @@ const DocumentsReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"), 2000);
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleBtnVisibility = (act, item) => {
|
||||
if (!act.restrictBtn) {
|
||||
return true;
|
||||
@@ -797,7 +799,7 @@ const DocumentsReport = (props) => {
|
||||
setError("");
|
||||
setIsModal({});
|
||||
};
|
||||
const handleSaveAsTemplate = async (doc) => {
|
||||
const handleSaveAsTemplate = utils.withSessionValidation(async (doc) => {
|
||||
try {
|
||||
const params = { docId: doc?.objectId };
|
||||
const templateRes = await Parse.Cloud.run("saveastemplate", params);
|
||||
@@ -808,7 +810,7 @@ const DocumentsReport = (props) => {
|
||||
} finally {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleCloseTemplate = () => {
|
||||
setTemplateId("");
|
||||
setIsSuccess({});
|
||||
@@ -819,37 +821,39 @@ const DocumentsReport = (props) => {
|
||||
|
||||
// `handleBulkSendTemplate` 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 axiosRes = await fetchTemplate(templateId);
|
||||
const templateRes = axiosRes.data && axiosRes.data.result;
|
||||
const isPrefillExist = templateRes?.Placeholders.some(
|
||||
(x) => x.Role === "prefill"
|
||||
);
|
||||
if (isPrefillExist) {
|
||||
setIsBulkSend({});
|
||||
setIsLoader({});
|
||||
showAlert("danger", t("prefill-bulk-error"));
|
||||
} else {
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const docSignTypes = templateRes?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
const handleBulkSendTemplate = utils.withSessionValidation(
|
||||
async (templateId, docId) => {
|
||||
setIsBulkSend({ [docId]: true });
|
||||
setIsLoader({ [docId]: true });
|
||||
try {
|
||||
const axiosRes = await fetchTemplate(templateId);
|
||||
const templateRes = axiosRes.data && axiosRes.data.result;
|
||||
const isPrefillExist = templateRes?.Placeholders.some(
|
||||
(x) => x.Role === "prefill"
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
setPlaceholders(templateRes?.Placeholders);
|
||||
setTemplateDetails(templateRes);
|
||||
setIsLoader({});
|
||||
if (isPrefillExist) {
|
||||
setIsBulkSend({});
|
||||
setIsLoader({});
|
||||
showAlert("danger", t("prefill-bulk-error"));
|
||||
} else {
|
||||
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"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in fetch template in bulk modal", err);
|
||||
setIsBulkSend({});
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const handleResendClose = () => {
|
||||
setIsResendMail({});
|
||||
@@ -857,7 +861,7 @@ const DocumentsReport = (props) => {
|
||||
setUserDetails({});
|
||||
};
|
||||
|
||||
const handleRecreateDoc = async (item) => {
|
||||
const handleRecreateDoc = utils.withSessionValidation(async (item) => {
|
||||
setActLoader({ [item.objectId]: true });
|
||||
try {
|
||||
const res = await Parse.Cloud.run("recreatedoc", {
|
||||
@@ -874,7 +878,7 @@ const DocumentsReport = (props) => {
|
||||
} finally {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const restrictBtn = (item, act) => {
|
||||
return item.IsSignyourself && act.action === "recreatedocument"
|
||||
@@ -1151,6 +1155,7 @@ const DocumentsReport = (props) => {
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{props.List?.length > 0 &&
|
||||
!props.searchLoader &&
|
||||
currentList.map((item, index) => (
|
||||
<tr
|
||||
className={`${
|
||||
@@ -1706,22 +1711,31 @@ const DocumentsReport = (props) => {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{props.List?.length <= 0 && (
|
||||
{(props.searchLoader || props.List?.length <= 0) && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
{props.searchLoader ? (
|
||||
<>
|
||||
<Loader />
|
||||
<div className="text-sm ">{t("loading-mssg")}</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt={t("no-data-avaliable")}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -36,13 +36,15 @@ import { useElSize } from "../../hook/useElSize";
|
||||
import LottieWithLoader from "../../primitives/DotLottieReact";
|
||||
import PrefillWidgetModal from "../../components/pdf/PrefillWidgetsModal";
|
||||
import * as utils from "../../utils";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RenderReportCell } from "../../primitives/RenderReportCell";
|
||||
import CustomizeMail from "../../components/pdf/CustomizeMail";
|
||||
import { resetWidgetState } from "../../redux/reducers/widgetSlice";
|
||||
|
||||
const TemplatesReport = (props) => {
|
||||
const copyUrlRef = useRef(null);
|
||||
const titleRef = useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const titleElement = useElSize(titleRef);
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const appName =
|
||||
@@ -92,8 +94,9 @@ const TemplatesReport = (props) => {
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [currUserId, setCurrUserId] = useState(false);
|
||||
const [currUserId, setCurrUserId] = useState("");
|
||||
const [documentDetails, setDocumentDetails] = useState();
|
||||
const [docId, setDocId] = useState();
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -160,6 +163,7 @@ const TemplatesReport = (props) => {
|
||||
const pageNumbers = getPaginationRange();
|
||||
// below useEffect reset currenpage to 1 if user change route
|
||||
useEffect(() => {
|
||||
dispatch(resetWidgetState([]));
|
||||
checkTourStatus();
|
||||
fetchTeamList();
|
||||
return () => setCurrentPage(1);
|
||||
@@ -206,7 +210,7 @@ const TemplatesReport = (props) => {
|
||||
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const fetchTenantDetails = utils.withSessionValidation(async () => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
@@ -230,7 +234,7 @@ const TemplatesReport = (props) => {
|
||||
} else {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleURL` is used to open microapp
|
||||
const handleURL = async (item, act) => {
|
||||
@@ -276,7 +280,7 @@ const TemplatesReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchTemplate = async (templateId) => {
|
||||
const fetchTemplate = utils.withSessionValidation(async (templateId) => {
|
||||
try {
|
||||
const params = {
|
||||
templateId: templateId,
|
||||
@@ -301,26 +305,28 @@ const TemplatesReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
//function is called when there ther no any prefill role widget exist then create direct document and navigate
|
||||
const navigatePageToDoc = async (templateRes, placeholder, signer) => {
|
||||
setIsPrefillModal({});
|
||||
const res = await createDocument(
|
||||
[templateRes || templateDetails],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDetails?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
state: { title: "Use Template" }
|
||||
});
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
const navigatePageToDoc = utils.withSessionValidation(
|
||||
async (templateRes, placeholder, signer) => {
|
||||
setIsPrefillModal({});
|
||||
const res = await createDocument(
|
||||
[templateRes || templateDetails],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDetails?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
state: { title: "Use Template" }
|
||||
});
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const handleActionBtn = async (act, item) => {
|
||||
const handleActionBtn = utils.withSessionValidation(async (act, item) => {
|
||||
setIsTour(false);
|
||||
if (act.action === "redirect") {
|
||||
handleURL(item, act);
|
||||
@@ -362,7 +368,7 @@ const TemplatesReport = (props) => {
|
||||
} else if (act.action === "extendexpiry") {
|
||||
setIsModal({ [`extendexpiry_${item.objectId}`]: true });
|
||||
}
|
||||
};
|
||||
});
|
||||
// Get current list
|
||||
const indexOfLastDoc = currentPage * props.docPerPage;
|
||||
const indexOfFirstDoc = indexOfLastDoc - props.docPerPage;
|
||||
@@ -383,7 +389,7 @@ const TemplatesReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (item) => {
|
||||
const handleDelete = utils.withSessionValidation(async (item) => {
|
||||
setIsDeleteModal({});
|
||||
setActLoader({ [`${item.objectId}`]: true });
|
||||
try {
|
||||
@@ -411,7 +417,7 @@ const TemplatesReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const handleClose = (
|
||||
) => {
|
||||
setIsDeleteModal({});
|
||||
@@ -608,7 +614,7 @@ const TemplatesReport = (props) => {
|
||||
};
|
||||
// `handleNextBtn` is used to open edit mail template screen in resend mail modal
|
||||
// as well as replace variable with original one
|
||||
const handleNextBtn = (user, doc) => {
|
||||
const handleNextBtn = utils.withSessionValidation((user, doc) => {
|
||||
const userdata = {
|
||||
Name: user?.signerPtr?.Name,
|
||||
Email: user.email ? user?.email : user.signerPtr?.Email,
|
||||
@@ -655,8 +661,8 @@ const TemplatesReport = (props) => {
|
||||
const res = replaceMailVaribles(subject, body, variables);
|
||||
setMail((prev) => ({ ...prev, subject: res.subject, body: res.body }));
|
||||
setIsNextStep({ [user.Id]: true });
|
||||
};
|
||||
const handleResendMail = async (e, doc, user) => {
|
||||
});
|
||||
const handleResendMail = utils.withSessionValidation(async (e, doc, user) => {
|
||||
e.preventDefault();
|
||||
setActLoader({ [user?.Id]: true });
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||
@@ -693,7 +699,7 @@ const TemplatesReport = (props) => {
|
||||
setUserDetails({});
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
const fetchUserStatus = (user, doc) => {
|
||||
const email = user.email ? user.email : user.signerPtr.Email;
|
||||
const audit = doc?.AuditTrail?.find((x) => x.UserPtr.Email === email);
|
||||
@@ -762,7 +768,7 @@ const TemplatesReport = (props) => {
|
||||
|
||||
|
||||
// `handleShareWith` is used to save teams in sharedWith field
|
||||
const handleShareWith = async (e, template) => {
|
||||
const handleShareWith = utils.withSessionValidation(async (e, template) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsShareWith({});
|
||||
@@ -785,10 +791,10 @@ const TemplatesReport = (props) => {
|
||||
} finally {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// `handleCreateDuplicate` is used to create duplicate from current entry using objectId
|
||||
const handleCreateDuplicate = async (item) => {
|
||||
const handleCreateDuplicate = utils.withSessionValidation(async (item) => {
|
||||
setActLoader({ [item.objectId]: true });
|
||||
setIsModal({});
|
||||
try {
|
||||
@@ -806,9 +812,9 @@ const TemplatesReport = (props) => {
|
||||
} finally {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
// `handleRenameDoc` is used to update document name
|
||||
const handleRenameDoc = async (item) => {
|
||||
const handleRenameDoc = utils.withSessionValidation(async (item) => {
|
||||
setActLoader({ [item.objectId]: true });
|
||||
setIsModal({});
|
||||
const className = "contracts_Template";
|
||||
@@ -829,7 +835,7 @@ const TemplatesReport = (props) => {
|
||||
showAlert("danger", t("something-went-wrong-mssg"), 2000);
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModal({});
|
||||
@@ -885,7 +891,7 @@ const TemplatesReport = (props) => {
|
||||
setXyPosition([]);
|
||||
};
|
||||
//`handlePrefillWidgetCreateDoc` is used to embed prefill all widgets on document, create document, and send document
|
||||
const handlePrefillWidgetCreateDoc = async () => {
|
||||
const handlePrefillWidgetCreateDoc = utils.withSessionValidation(async () => {
|
||||
setIsSubmit(true);
|
||||
const scale = 1;
|
||||
const res = await utils?.handleCheckPrefillCreateDoc(
|
||||
@@ -915,12 +921,21 @@ const TemplatesReport = (props) => {
|
||||
} else if (res?.status === "success") {
|
||||
setDocumentId(res.id);
|
||||
setActLoader({});
|
||||
setIsMailModal(true);
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
const ownerId = templateDetails.ExtUserPtr?.UserId?.objectId;
|
||||
const firstSigner = signerList[0];
|
||||
const isOwner = firstSigner?.UserId?.objectId === ownerId;
|
||||
setDocId(res.id);
|
||||
if (templateDetails?.SendinOrder && isOwner) {
|
||||
setCurrUserId(firstSigner?.objectId);
|
||||
setIsSend(true);
|
||||
} else {
|
||||
setIsMailModal(true);
|
||||
}
|
||||
if (user) {
|
||||
try {
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
@@ -951,7 +966,7 @@ const TemplatesReport = (props) => {
|
||||
}
|
||||
}
|
||||
setIsSubmit(false);
|
||||
};
|
||||
});
|
||||
//function show signer list and share link to share signUrl
|
||||
const handleShareList = () => {
|
||||
const shareLinkList = [];
|
||||
@@ -1132,6 +1147,7 @@ const TemplatesReport = (props) => {
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{props.List?.length > 0 &&
|
||||
!props.searchLoader &&
|
||||
currentList.map((item, index) => (
|
||||
<tr
|
||||
className={`${
|
||||
@@ -1544,22 +1560,31 @@ const TemplatesReport = (props) => {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{props.List?.length <= 0 && (
|
||||
{(props.searchLoader || props.List?.length <= 0) && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
{props.searchLoader ? (
|
||||
<>
|
||||
<Loader />
|
||||
<div className="text-sm ">{t("loading-mssg")}</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt={t("no-data-avaliable")}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1610,11 +1635,13 @@ const TemplatesReport = (props) => {
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
title={
|
||||
mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
!templateDetails?.SendinOrder
|
||||
? mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
: t("mail-status-head")
|
||||
}
|
||||
handleClose={() => {
|
||||
setIsSend(false);
|
||||
@@ -1622,68 +1649,102 @@ const TemplatesReport = (props) => {
|
||||
}}
|
||||
>
|
||||
<div className="h-[100%] p-[20px] text-base-content">
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{documentDetails.SendinOrder ? (
|
||||
<p>
|
||||
{currUserId
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signerList[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-5">
|
||||
<div>
|
||||
{mailStatus === "success" ? (
|
||||
<div className="text-center mb-[10px]">
|
||||
<LottieWithLoader />
|
||||
{documentDetails.SendinOrder ? (
|
||||
<p>
|
||||
{currUserId
|
||||
? t("placeholder-mail-alert-you")
|
||||
: t("placeholder-mail-alert", {
|
||||
name: signerList[0]?.Name
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
)}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : mailStatus === "failed" ? (
|
||||
<p>{t("mail-failed")} </p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-4")}</p>
|
||||
<div className="mb-[10px]">
|
||||
{!templateDetails?.SendinOrder &&
|
||||
(mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
))}
|
||||
{currUserId && (
|
||||
<span className="mt-1">{t("placeholder-alert-5")}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{currUserId && <p>{t("placeholder-alert-5")}</p>}
|
||||
</div>
|
||||
) : mailStatus === "quotareached" ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-[10px]">
|
||||
{mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
)}
|
||||
{currUserId && (
|
||||
<p className="mt-1">{t("placeholder-alert-5")}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!mailStatus && (
|
||||
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
|
||||
)}
|
||||
{mailStatus !== "quotareached" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success" ? "flex justify-center mt-1" : ""
|
||||
}
|
||||
>
|
||||
{currUserId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleRecipientSign(documentDetails?.objectId, currUserId)
|
||||
|
||||
{mailStatus !== "quotareached" && mailStatus !== "failed" && (
|
||||
<div
|
||||
className={
|
||||
mailStatus === "success"
|
||||
? "flex justify-center mt-1"
|
||||
: "flex items-center justify-center mt-7"
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
{currUserId && (
|
||||
<button
|
||||
onClick={() => handleRecipientSign(docId, currUserId)}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mr-1"
|
||||
>
|
||||
{t("sign-now")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{currUserId ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
}}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{currUserId ? t("no") : t("close")}
|
||||
</button>
|
||||
</div>
|
||||
{mailStatus !== "success" &&
|
||||
currUserId &&
|
||||
templateDetails?.SendinOrder && (
|
||||
<div className="op-divider text-base-content mx-[0%] my-1 font-medium">
|
||||
{t("or")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{mailStatus !== "success" &&
|
||||
currUserId &&
|
||||
templateDetails?.SendinOrder && (
|
||||
<>
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!mailStatus && (
|
||||
<div className="op-divider text-base-content mx-[0%] mt-3"></div>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
@@ -31,7 +31,10 @@
|
||||
border: 1px solid #007bff;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.prefillCanvas {
|
||||
width: 205px;
|
||||
height: 81px;
|
||||
}
|
||||
.signatureCanvas {
|
||||
width: 440px;
|
||||
height: 167px;
|
||||
@@ -44,15 +47,11 @@
|
||||
}
|
||||
|
||||
.mysignatureCanvas {
|
||||
/* width: 456px;
|
||||
height: 180px; */
|
||||
width: 370px;
|
||||
height: 146px;
|
||||
}
|
||||
|
||||
.mystampCanvas {
|
||||
/* width: 446px;
|
||||
height: 180px; */
|
||||
width: 370px;
|
||||
height: 146px;
|
||||
}
|
||||
@@ -60,8 +59,6 @@
|
||||
.intialSignature {
|
||||
border: 2px solid #888;
|
||||
background-color: rgb(255, 255, 255);
|
||||
/* width: 180px;
|
||||
height: 180px; */
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from "./sanitizeFileName";
|
||||
export * from "./lazyWithRetry";
|
||||
export * from "./upgradeProgress";
|
||||
export * from "./widgetUtils";
|
||||
export * from "./withSessionValidation";
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
generatePdfName,
|
||||
embedWidgetsToDoc,
|
||||
randomId,
|
||||
getBase64FromUrl
|
||||
getBase64FromUrl,
|
||||
drawWidget
|
||||
} from "../constant/Utils";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
|
||||
@@ -98,9 +99,9 @@ export const isValidPrefill = (prefillData) => {
|
||||
const emptyResponseObjects = getPlaceholder.flatMap((page) =>
|
||||
page.pos.filter(
|
||||
(item) =>
|
||||
item?.type !== "checkbox" &&
|
||||
!item?.options?.defaultValue &&
|
||||
!item?.options?.response
|
||||
!item?.options?.response &&
|
||||
item?.options?.status === "required"
|
||||
)
|
||||
);
|
||||
if (emptyResponseObjects.length > 0) {
|
||||
@@ -175,21 +176,29 @@ export const savePrefillImg = async (Placeholders) => {
|
||||
const prefillData = Placeholders?.find((x) => x.Role === "prefill");
|
||||
if (prefillData) {
|
||||
const allImageFields = prefillData?.placeHolder.flatMap((p) =>
|
||||
p.pos.filter((item) => item.type === "image")
|
||||
p.pos.filter((item) => item.type === "image" || item.type === drawWidget)
|
||||
);
|
||||
const hasImageType = allImageFields.some(
|
||||
(p) => p.type === "image" || p.type === drawWidget
|
||||
);
|
||||
const hasImageType = allImageFields.some((p) => p.type === "image");
|
||||
if (hasImageType) {
|
||||
const imgArr = [];
|
||||
for (const ph of prefillData?.placeHolder || []) {
|
||||
for (const pos of ph?.pos || []) {
|
||||
if (pos?.type === "image" && pos?.SignUrl) {
|
||||
if (
|
||||
(pos?.type === "image" || pos?.type === drawWidget) &&
|
||||
pos?.options?.response
|
||||
) {
|
||||
const addSuffix = true;
|
||||
const base64 = await getBase64FromUrl(pos?.SignUrl, addSuffix);
|
||||
const base64 = await getBase64FromUrl(
|
||||
pos?.options?.response,
|
||||
addSuffix
|
||||
);
|
||||
imgArr.push({
|
||||
id: pos?.key,
|
||||
base64: base64
|
||||
});
|
||||
} else if (pos?.type === "image") {
|
||||
} else if (pos?.type === "image" || pos?.type === drawWidget) {
|
||||
imgArr.push({
|
||||
id: pos?.key,
|
||||
base64: ""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { generateTitleFromFilename } from "../constant/Utils";
|
||||
import { generateTitleFromFilename, selectFormat } from "../constant/Utils";
|
||||
import { base64StringtoFile, uploadFile } from "./fileUtils";
|
||||
import { sanitizeFileName } from "./sanitizeFileName";
|
||||
import Parse from "parse";
|
||||
@@ -135,3 +135,107 @@ export const getInitials = (name) => {
|
||||
|
||||
return `${first[0].toUpperCase()} ${second[0].toUpperCase()}`;
|
||||
};
|
||||
|
||||
// Function to check if a string is valid Base64 (supports data URL)
|
||||
export const isValidBase64 = (str) => {
|
||||
if (!str || typeof str !== "string") return false;
|
||||
|
||||
// Remove data URL prefix if present
|
||||
const cleanedStr = str.split(",").pop()?.trim();
|
||||
if (!cleanedStr) return false;
|
||||
|
||||
// Length must be divisible by 4
|
||||
if (cleanedStr.length % 4 !== 0) return false;
|
||||
|
||||
// Base64 or Base64URL regex
|
||||
const base64Regex =
|
||||
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
||||
|
||||
if (!base64Regex.test(cleanedStr)) return false;
|
||||
|
||||
try {
|
||||
// atob throws if invalid
|
||||
atob(cleanedStr);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
export const addPreferenceOpt = (owner, type, role, isSignyourself) => {
|
||||
const widget = owner?.WidgetPreferences?.find((w) => w.type === type) || {};
|
||||
switch (type) {
|
||||
case "date": {
|
||||
const signingDate = widget?.isSigningDate ? "today" : widget?.date || "";
|
||||
const isReadOnly = widget?.isReadOnly || false;
|
||||
const isPrefill = role && role === "prefill";
|
||||
const format =
|
||||
widget?.format || selectFormat(owner?.DateFormat) || "MM/DD/YYYY";
|
||||
const prefillDate = new Date();
|
||||
const response = isPrefill
|
||||
? formatDate({ date: prefillDate, format: format })
|
||||
: signingDate || "";
|
||||
const option =
|
||||
isSignyourself || isPrefill ? {} : { isReadOnly: isReadOnly };
|
||||
return {
|
||||
response: response,
|
||||
...option,
|
||||
format: widget?.format || "MM/dd/yyyy"
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export function formatDate(dateObj) {
|
||||
const format = dateObj?.format?.toLowerCase();
|
||||
const date = new Date(dateObj.date);
|
||||
|
||||
// zero-padded day and month
|
||||
const dd = String(date.getDate()).padStart(2, "0");
|
||||
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const yyyy = date.getFullYear();
|
||||
|
||||
// month names
|
||||
const shortNames = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec"
|
||||
];
|
||||
const longNames = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
];
|
||||
const mmm = shortNames[date.getMonth()];
|
||||
const mmmm = longNames[date.getMonth()];
|
||||
|
||||
// a map of token → replacement
|
||||
const tokens = { yyyy: yyyy, mm: mm, dd: dd, mmm: mmm, mmmm: mmmm };
|
||||
|
||||
// Replace longer tokens first
|
||||
return format
|
||||
.replace(/yyyy/, tokens.yyyy)
|
||||
.replace(/mmmm/, tokens.mmmm)
|
||||
.replace(/mmm/, tokens.mmm)
|
||||
.replace(/mm/, tokens.mm)
|
||||
.replace(/dd/, tokens.dd);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
import { store } from "../redux/store";
|
||||
import Parse from "parse";
|
||||
|
||||
export function withSessionValidation(fn) {
|
||||
return async (...args) => {
|
||||
try {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const user =
|
||||
typeof Parse !== "undefined" ? Parse.User?.current?.() : null;
|
||||
const sessionToken = user?.getSessionToken?.();
|
||||
|
||||
if (!tenantId || !sessionToken) {
|
||||
store.dispatch(sessionStatus(false));
|
||||
throw new Error("invalid session token");
|
||||
}
|
||||
|
||||
return await fn(...args);
|
||||
} catch (error) {
|
||||
if (error?.message === "invalid session token") {
|
||||
console.error("invalid session or missing tenantId", error);
|
||||
store.dispatch(sessionStatus(false));
|
||||
return;
|
||||
} else {
|
||||
throw error; // important: don't silently swallow errors
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -183,35 +183,6 @@ export function generateId(length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Format date and time for the selected timezone
|
||||
export const formatTimeInTimezone = (date, timezone) => {
|
||||
const nyDate = timezone && toZonedTime(date, timezone);
|
||||
const generatedDate = timezone
|
||||
? format(nyDate, 'EEE, dd MMM yyyy HH:mm:ss zzz', { timeZone: timezone })
|
||||
: new Date(date).toUTCString();
|
||||
return generatedDate;
|
||||
};
|
||||
|
||||
// `getSecureUrl` is used to return local secure url if local files
|
||||
export const getSecureUrl = url => {
|
||||
const fileUrl = new URL(url)?.pathname?.includes('files');
|
||||
if (fileUrl) {
|
||||
try {
|
||||
const file = getSignedLocalUrl(url);
|
||||
if (file) {
|
||||
return { url: file };
|
||||
} else {
|
||||
return { url: '' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err while fileupload ', err);
|
||||
return { url: '' };
|
||||
}
|
||||
} else {
|
||||
return { url: url };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* FlattenPdf is used to remove existing widgets if present any and flatten pdf.
|
||||
* @param {string | Uint8Array | ArrayBuffer} pdfFile - pdf file.
|
||||
@@ -249,13 +220,42 @@ export const flattenPdf = async pdfFile => {
|
||||
}
|
||||
};
|
||||
|
||||
// Format date and time for the selected timezone
|
||||
export const formatTimeInTimezone = (date, timezone) => {
|
||||
const nyDate = timezone && toZonedTime(date, timezone);
|
||||
const generatedDate = timezone
|
||||
? format(nyDate, 'EEE, dd MMM yyyy HH:mm:ss zzz', { timeZone: timezone })
|
||||
: new Date(date).toUTCString();
|
||||
return generatedDate;
|
||||
};
|
||||
|
||||
// `getSecureUrl` is used to return local secure url if local files
|
||||
export const getSecureUrl = url => {
|
||||
const fileUrl = new URL(url)?.pathname?.includes('files');
|
||||
if (fileUrl) {
|
||||
try {
|
||||
const file = getSignedLocalUrl(url);
|
||||
if (file) {
|
||||
return { url: file };
|
||||
} else {
|
||||
return { url: '' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err while fileupload ', err);
|
||||
return { url: '' };
|
||||
}
|
||||
} else {
|
||||
return { url: url };
|
||||
}
|
||||
};
|
||||
|
||||
export const mailTemplate = param => {
|
||||
const themeColor = '#47a3ad';
|
||||
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 opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
|
||||
const opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
|
||||
const body =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'><div style='padding:10px'>" +
|
||||
@@ -278,7 +278,7 @@ export const mailTemplate = param => {
|
||||
AppName +
|
||||
'. For any queries regarding this email, please contact the sender ' +
|
||||
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 complaints with ${AppName}${opurl}.</p></div></div></body></html>`;
|
||||
|
||||
return { subject, body };
|
||||
};
|
||||
@@ -369,34 +369,27 @@ export const handleValidImage = async Placeholder => {
|
||||
for (const item of placeholder.placeHolder || []) {
|
||||
const updatedPos = [];
|
||||
for (const posItem of item.pos || []) {
|
||||
if (posItem?.type === 'image' && posItem?.SignUrl) {
|
||||
const validUrl = await getPresignedUrl(posItem?.SignUrl);
|
||||
if (
|
||||
(posItem?.type === 'image' || posItem?.type === 'draw') &&
|
||||
posItem?.options?.response
|
||||
) {
|
||||
const validUrl = await getPresignedUrl(posItem?.options?.response);
|
||||
updatedPos.push({
|
||||
...posItem,
|
||||
SignUrl: validUrl,
|
||||
...(item.SignUrl !== undefined && { SignUrl: validUrl }),
|
||||
options: { ...posItem.options, response: validUrl },
|
||||
});
|
||||
} else {
|
||||
updatedPos.push(posItem);
|
||||
}
|
||||
}
|
||||
updatedRole.push({
|
||||
...item,
|
||||
pos: updatedPos,
|
||||
});
|
||||
updatedRole.push({ ...item, pos: updatedPos });
|
||||
}
|
||||
|
||||
updatedPlaceholders.push({
|
||||
...placeholder,
|
||||
signerPtr,
|
||||
placeHolder: updatedRole,
|
||||
});
|
||||
updatedPlaceholders.push({ ...placeholder, signerPtr, placeHolder: updatedRole });
|
||||
} else {
|
||||
// Not prefill role, just push as-is
|
||||
updatedPlaceholders.push({
|
||||
...placeholder,
|
||||
signerPtr,
|
||||
});
|
||||
updatedPlaceholders.push({ ...placeholder, signerPtr });
|
||||
}
|
||||
}
|
||||
return updatedPlaceholders;
|
||||
|
||||
@@ -62,6 +62,7 @@ import manageSign from './parsefunction/manageSign.js';
|
||||
import getSignature from './parsefunction/getSignature.js';
|
||||
import updateEmailTemplates from './parsefunction/updateEmailTemplates.js';
|
||||
import triggerEvent from './parsefunction/triggerEvent.js';
|
||||
import setWidgetPreferences from './parsefunction/setWidgetPreferences.js';
|
||||
|
||||
// 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);
|
||||
@@ -134,3 +135,4 @@ Parse.Cloud.define('managesign', manageSign);
|
||||
Parse.Cloud.define('getdefaultsignature', getSignature);
|
||||
Parse.Cloud.define('updateemailtemplates', updateEmailTemplates);
|
||||
Parse.Cloud.define('triggerevent', triggerEvent);
|
||||
Parse.Cloud.define('setwidgetpreferences', setWidgetPreferences);
|
||||
|
||||
@@ -2,59 +2,23 @@ async function DocumentAftersave(request) {
|
||||
try {
|
||||
if (!request.original) {
|
||||
console.log('new entry is insert in contracts_Document');
|
||||
const createdAt = request.object.get('createdAt');
|
||||
const Folder = request.object.get('Type');
|
||||
const obj = request.object;
|
||||
const objId = obj?.id;
|
||||
const createdAt = obj?.get?.('createdAt');
|
||||
const folder = obj?.get?.('Type');
|
||||
const ip = request?.headers?.['x-real-ip'] || '';
|
||||
const originIp = request?.object?.get('OriginIp') || '';
|
||||
if (createdAt && Folder === undefined) {
|
||||
// console.log("IN If condition")
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
|
||||
const ExpiryDate = new Date(createdAt);
|
||||
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
documentQuery.include('ExtUserPtr.TenantId');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
if (!originIp) {
|
||||
updateQuery.set('OriginIp', ip);
|
||||
}
|
||||
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||
const ReminderDate = new Date(createdAt);
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
updateQuery.set('NextReminderDate', ReminderDate);
|
||||
}
|
||||
await updateQuery.save(null, { useMasterKey: true });
|
||||
} else if (createdAt && Folder === 'AIDoc') {
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
||||
const ExpiryDate = new Date(createdAt);
|
||||
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
documentQuery.include('ExtUserPtr.TenantId');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
if (!originIp) {
|
||||
updateQuery.set('OriginIp', ip);
|
||||
}
|
||||
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||
const ReminderDate = new Date(createdAt);
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
updateQuery.set('NextReminderDate', ReminderDate);
|
||||
}
|
||||
await updateQuery.save(null, { useMasterKey: true });
|
||||
const originIp = obj?.get?.('OriginIp') || '';
|
||||
if (createdAt) {
|
||||
await updateDocumentMeta({ objId, createdAt, folder, ip, originIp });
|
||||
}
|
||||
|
||||
const signers = request.object.get('Signers');
|
||||
const signers = obj?.get?.('Signers');
|
||||
const hasSigners = Array.isArray(signers) && signers.length > 0;
|
||||
// update acl of New Document If There are signers present in array
|
||||
if (signers && signers.length > 0) {
|
||||
await updateAclDoc(request.object.id);
|
||||
} else {
|
||||
if (request?.object?.id && request.user) {
|
||||
await updateSelfDoc(request.object.id);
|
||||
}
|
||||
if (hasSigners) {
|
||||
await updateAclDoc(objId);
|
||||
} else if (objId && request?.user) {
|
||||
await updateSelfDoc(objId);
|
||||
}
|
||||
} else {
|
||||
if (request?.user) {
|
||||
@@ -73,17 +37,49 @@ async function DocumentAftersave(request) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
async function updateDocumentMeta({ objId, createdAt, folder, ip, originIp }) {
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
documentQuery.include('ExtUserPtr.TenantId');
|
||||
|
||||
const doc = await documentQuery.get(objId, { useMasterKey: true });
|
||||
if (folder === undefined || folder === 'AIDoc') {
|
||||
// ExpiryDate
|
||||
const timeToCompleteDays =
|
||||
folder === undefined
|
||||
? doc.get('TimeToCompleteDays') || 15 // keep your default=15 only for "undefined folder"
|
||||
: doc.get('TimeToCompleteDays'); // keep original behavior for AIDoc (no forced default)
|
||||
|
||||
if (typeof timeToCompleteDays === 'number' && createdAt) {
|
||||
const expiryDate = new Date(createdAt);
|
||||
expiryDate.setDate(expiryDate.getDate() + timeToCompleteDays);
|
||||
doc.set('ExpiryDate', expiryDate);
|
||||
}
|
||||
|
||||
// OriginIp
|
||||
if (!originIp) {
|
||||
doc.set('OriginIp', ip);
|
||||
}
|
||||
|
||||
// Automatic reminders
|
||||
const autoReminder = doc.get('AutomaticReminders') || false;
|
||||
if (autoReminder && createdAt) {
|
||||
const remindOnceInEvery = doc.get('RemindOnceInEvery') || 5;
|
||||
const reminderDate = new Date(createdAt);
|
||||
reminderDate.setDate(reminderDate.getDate() + remindOnceInEvery);
|
||||
doc.set('NextReminderDate', reminderDate);
|
||||
}
|
||||
}
|
||||
|
||||
await doc.save(null, { useMasterKey: true });
|
||||
}
|
||||
|
||||
async function updateAclDoc(objId) {
|
||||
// console.log("In side updateAclDoc func")
|
||||
// console.log(objId)
|
||||
const Query = new Parse.Query('contracts_Document');
|
||||
Query.include('Signers');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
Query.include('CreatedBy');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
// console.log(JSON.stringify(res));
|
||||
const UsersPtr = res.Signers.map(item => item.UserId);
|
||||
|
||||
if (res.Signers[0].ExtUserPtr) {
|
||||
@@ -97,8 +93,6 @@ async function DocumentAftersave(request) {
|
||||
updateACL.set('Signers', ExtUserSigners);
|
||||
}
|
||||
|
||||
// console.log("UsersPtr")
|
||||
// console.log(JSON.stringify(UsersPtr))
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
@@ -116,14 +110,11 @@ async function DocumentAftersave(request) {
|
||||
}
|
||||
|
||||
async function updateSelfDoc(objId) {
|
||||
// console.log(objId)
|
||||
const Query = new Parse.Query('contracts_Document');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
// console.log(JSON.stringify(res));
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js';
|
||||
import { setDocumentCount } from '../../utils/CountUtils.js';
|
||||
|
||||
async function DocumentBeforesave(request) {
|
||||
if (!request.original) {
|
||||
@@ -33,24 +34,8 @@ async function DocumentBeforesave(request) {
|
||||
|
||||
// Check if SignedUrl field has been added (transition from undefined to defined)
|
||||
if (oldDocument && !oldDocument?.get('SignedUrl') && document?.get('SignedUrl')) {
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', oldDocument.get('ExtUserPtr').id);
|
||||
|
||||
try {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('DocumentCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_Users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('DocumentCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating document count in contracts_Users: ' + error.message);
|
||||
if (oldDocument?.get('ExtUserPtr')?.id) {
|
||||
setDocumentCount(oldDocument?.get('ExtUserPtr')?.id);
|
||||
}
|
||||
if (document?.get('Signers') && document.get('Signers').length > 0) {
|
||||
document.set('DocSentAt', new Date());
|
||||
|
||||
@@ -36,7 +36,7 @@ export default async function forwardDoc(request) {
|
||||
let mailRes;
|
||||
for (let i = 0; i < recipients.length; i++) {
|
||||
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 opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
const themeColor = '#47a3ad';
|
||||
|
||||
let params = {
|
||||
@@ -52,7 +52,7 @@ export default async function forwardDoc(request) {
|
||||
`${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>` +
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. 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 complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`,
|
||||
};
|
||||
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
|
||||
headers: {
|
||||
|
||||
@@ -2,27 +2,20 @@ export default async function TemplateAfterSave(request) {
|
||||
try {
|
||||
if (!request.original) {
|
||||
console.log('new entry is insert in contracts_Template');
|
||||
// update acl of New Document If There are signers present in array
|
||||
const signers = request.object.get('Signers');
|
||||
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||
const obj = request.object;
|
||||
const objId = obj?.id;
|
||||
const ip = request?.headers?.['x-real-ip'] || '';
|
||||
const originIp = request?.object?.get('OriginIp') || '';
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||
const ReminderDate = new Date(request?.object?.get('createdAt'));
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
request.object.set('NextReminderDate', ReminderDate);
|
||||
}
|
||||
if (!originIp) {
|
||||
request.object.set('OriginIp', ip);
|
||||
}
|
||||
await request.object.save(null, { useMasterKey: true });
|
||||
if (signers && signers.length > 0) {
|
||||
await updateAclDoc(request.object.id);
|
||||
} else {
|
||||
if (request?.object?.id && request?.user) {
|
||||
await updateSelfDoc(request.object.id);
|
||||
}
|
||||
const originIp = obj?.get?.('OriginIp') || '';
|
||||
const createdAt = obj?.get?.('createdAt');
|
||||
|
||||
// update acl of New template If There are signers present in array
|
||||
const signers = obj?.get('Signers');
|
||||
const hasSigners = Array.isArray(signers) && signers.length > 0;
|
||||
updateTemplateMeta({ objId, createdAt, ip, originIp });
|
||||
if (hasSigners) {
|
||||
await updateAclDoc(objId);
|
||||
} else if (objId && request?.user) {
|
||||
await updateSelfDoc(objId);
|
||||
}
|
||||
} else {
|
||||
if (request?.user) {
|
||||
@@ -41,17 +34,33 @@ export default async function TemplateAfterSave(request) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
async function updateTemplateMeta({ objId, createdAt, ip, originIp }) {
|
||||
const templateQuery = new Parse.Query('contracts_Template');
|
||||
templateQuery.include('ExtUserPtr.TenantId');
|
||||
|
||||
const obj = await templateQuery.get(objId, { useMasterKey: true });
|
||||
|
||||
// Automatic reminders
|
||||
const AutoReminder = obj?.get('AutomaticReminders') || false;
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = obj?.get('RemindOnceInEvery') || 5;
|
||||
const ReminderDate = new Date(createdAt);
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
obj.set('NextReminderDate', ReminderDate);
|
||||
}
|
||||
if (!originIp) {
|
||||
obj.set('OriginIp', ip);
|
||||
}
|
||||
|
||||
await obj.save(null, { useMasterKey: true });
|
||||
}
|
||||
async function updateAclDoc(objId) {
|
||||
// console.log("In side updateAclDoc func")
|
||||
// console.log(objId)
|
||||
const Query = new Parse.Query('contracts_Template');
|
||||
Query.include('Signers');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
// console.log(JSON.stringify(res));
|
||||
const UsersPtr = res.Signers.map(item => item.UserId);
|
||||
|
||||
if (res.Signers[0].ExtUserPtr) {
|
||||
@@ -65,8 +74,6 @@ export default async function TemplateAfterSave(request) {
|
||||
updateACL.set('Signers', ExtUserSigners);
|
||||
}
|
||||
|
||||
// console.log("UsersPtr")
|
||||
// console.log(JSON.stringify(UsersPtr))
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
@@ -85,15 +92,11 @@ export default async function TemplateAfterSave(request) {
|
||||
}
|
||||
|
||||
async function updateSelfDoc(objId) {
|
||||
// console.log("Inside updateSelfDoc func")
|
||||
|
||||
const Query = new Parse.Query('contracts_Template');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
// console.log(JSON.stringify(res));
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js';
|
||||
import { setTemplateCount } from '../../utils/CountUtils.js';
|
||||
|
||||
async function TemplateBeforeSave(request) {
|
||||
if (!request.original) {
|
||||
@@ -31,24 +32,8 @@ async function TemplateBeforeSave(request) {
|
||||
// below code is used to update template when user sent template or self signed
|
||||
const template = request.object;
|
||||
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', template.get('ExtUserPtr').id);
|
||||
|
||||
try {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('TemplateCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_Users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('TemplateCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating template count in contracts_Users: ' + error.message);
|
||||
if (template?.get('ExtUserPtr')?.id) {
|
||||
setTemplateCount(template?.get('ExtUserPtr')?.id);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl, mailTemplate, replaceMailVaribles, serverAppId } from '../../Utils.js';
|
||||
import { setDocumentCount } from '../../utils/CountUtils.js';
|
||||
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = serverAppId;
|
||||
async function deductcount(docsCount, extUserId) {
|
||||
try {
|
||||
const extCls = new Parse.Object('contracts_Users');
|
||||
extCls.id = extUserId;
|
||||
extCls.increment('DocumentCount', docsCount);
|
||||
const resExt = await extCls.save(null, { useMasterKey: true });
|
||||
if (extUserId) {
|
||||
setDocumentCount(extUserId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in deduct in quick send', err);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ async function sendDeclineMail(doc, publicUrl, userId, reason) {
|
||||
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 opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
const removePrefill =
|
||||
doc?.Placeholders?.length > 0 && doc?.Placeholders?.filter(x => x?.Role !== 'prefill');
|
||||
const signUser =
|
||||
@@ -36,7 +36,7 @@ async function sendDeclineMail(doc, publicUrl, userId, reason) {
|
||||
`<p>${pdfName} has been declined by ${signerName} "${signerEmail}" on ${new Date().toLocaleDateString()}.</p>` +
|
||||
`<p>Decline Reason: ${reason || 'Not specified'}</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, ` +
|
||||
`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 complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
import reportJson from './reportsJson.js';
|
||||
import reportJson, { applySearch } from './reportsJson.js';
|
||||
import axios from 'axios';
|
||||
|
||||
// Escape regex special characters. Copied from filterDocs.js
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
function buildClassesUrl({ serverUrl, clsName, paramsObj, keys, orderBy, skip, limit, include }) {
|
||||
const url = new URL(`${serverUrl}/classes/${clsName}`);
|
||||
url.searchParams.set('where', JSON.stringify(paramsObj));
|
||||
url.searchParams.set('keys', keys.join(','));
|
||||
url.searchParams.set('order', orderBy);
|
||||
url.searchParams.set('skip', String(skip));
|
||||
url.searchParams.set('limit', String(limit));
|
||||
if (include) url.searchParams.set('include', include);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export default async function getReport(request) {
|
||||
const reportId = request.params.reportId;
|
||||
const limit = request.params.limit;
|
||||
@@ -25,78 +29,76 @@ export default async function getReport(request) {
|
||||
},
|
||||
});
|
||||
const userId = userRes.data && userRes.data.objectId;
|
||||
if (userId) {
|
||||
const json = reportId && reportJson(reportId, userId);
|
||||
const clsName = json?.reportClass ? json.reportClass : 'contracts_Document';
|
||||
if (json) {
|
||||
const { params, keys } = json;
|
||||
const orderBy = '-updatedAt';
|
||||
const strKeys = keys.join();
|
||||
let paramsObj = { ...params };
|
||||
if (reportId == '6TeaPr321t') {
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', userRes.data.email);
|
||||
extUserQuery.include('TeamIds');
|
||||
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) {
|
||||
let teamArr = [];
|
||||
_extUser?.TeamIds?.forEach(x => (teamArr = [...teamArr, ...x.Ancestors]));
|
||||
paramsObj = {
|
||||
...paramsObj,
|
||||
$or: [
|
||||
{ SharedWith: { $in: teamArr } },
|
||||
{
|
||||
ExtUserPtr: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
SharedWithUsers: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
paramsObj = {
|
||||
...paramsObj,
|
||||
CreatedBy: { __type: 'Pointer', className: '_User', objectId: userId },
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!userId) {
|
||||
return { error: 'Invalid session token' };
|
||||
}
|
||||
const json = reportId && reportJson(reportId, userId);
|
||||
if (json) {
|
||||
let paramsObj = { ...(json.params || {}) };
|
||||
if (reportId == '6TeaPr321t') {
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', userRes.data.email);
|
||||
extUserQuery.include('TeamIds');
|
||||
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
|
||||
if (!extUser) {
|
||||
paramsObj = { ...paramsObj, CreatedBy: userPtr };
|
||||
}
|
||||
if (searchTerm) {
|
||||
const escaped = escapeRegExp(searchTerm);
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const extPtr = { __type: 'Pointer', className: 'contracts_Users', objectId: extUser.id };
|
||||
if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) {
|
||||
// Collect ancestors efficiently + de-dupe
|
||||
const teamSet = new Set();
|
||||
for (const team of _extUser?.TeamIds) {
|
||||
const ancestors = team.Ancestors || [];
|
||||
for (const a of ancestors) teamSet.add(a);
|
||||
}
|
||||
const teamArr = [...teamSet];
|
||||
paramsObj = {
|
||||
...paramsObj,
|
||||
Name: { $regex: `.*${escaped}.*`, $options: 'i' },
|
||||
$or: [
|
||||
{ SharedWith: { $in: teamArr } },
|
||||
{ ExtUserPtr: extPtr },
|
||||
{ SharedWithUsers: extPtr },
|
||||
],
|
||||
};
|
||||
}
|
||||
const strParams = JSON.stringify(paramsObj);
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': masterKey,
|
||||
};
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
if (res.data && res.data.results) {
|
||||
return res.data.results;
|
||||
} else {
|
||||
return [];
|
||||
paramsObj = { ...paramsObj, CreatedBy: userPtr };
|
||||
}
|
||||
} else {
|
||||
return { error: 'Report is not available!' };
|
||||
}
|
||||
paramsObj = applySearch({ reportId, baseWhere: paramsObj, searchTerm });
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': masterKey,
|
||||
};
|
||||
|
||||
const clsName = json?.reportClass ? json.reportClass : 'contracts_Document';
|
||||
const orderBy = '-updatedAt';
|
||||
const include = 'AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId';
|
||||
|
||||
const url = buildClassesUrl({
|
||||
serverUrl,
|
||||
clsName,
|
||||
paramsObj,
|
||||
keys: json.keys || [],
|
||||
orderBy,
|
||||
skip,
|
||||
limit,
|
||||
include,
|
||||
});
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
if (res.data && res.data.results) {
|
||||
return res.data.results;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
return { error: 'Report is not available!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err', err.message);
|
||||
const message = err?.response?.data?.error || err?.message || 'Something went wrong';
|
||||
console.log('getreport error:', message);
|
||||
if (err.code == 209) {
|
||||
return { error: 'Invalid session token' };
|
||||
} else {
|
||||
|
||||
@@ -122,7 +122,7 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
|
||||
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 opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
const auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0;
|
||||
const removePrefill =
|
||||
doc?.Placeholders?.length > 0 && doc?.Placeholders?.filter(x => x?.Role !== 'prefill');
|
||||
@@ -142,7 +142,7 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
|
||||
`<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>` +
|
||||
`<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, ` +
|
||||
`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 complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
@@ -169,7 +169,7 @@ async function sendCompletedMail(obj) {
|
||||
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 opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
let signersMail;
|
||||
if (doc?.Signers?.length > 0) {
|
||||
const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email);
|
||||
@@ -186,7 +186,7 @@ async function sendCompletedMail(obj) {
|
||||
`<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>` +
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. 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 complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
|
||||
if (obj?.isCustomMail) {
|
||||
const tenant = sender?.TenantId;
|
||||
|
||||
@@ -98,11 +98,6 @@ export default function reportJson(id, currentUserId) {
|
||||
IsCompleted: true,
|
||||
IsDeclined: { $ne: true },
|
||||
IsArchive: { $ne: true },
|
||||
// CreatedBy: {
|
||||
// __type: 'Pointer',
|
||||
// className: '_User',
|
||||
// objectId: currentUserId,
|
||||
// },
|
||||
$or: [
|
||||
// Condition 1: If `CreatedBy` exists, no need for `Signers` filter
|
||||
{ CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId } },
|
||||
@@ -230,3 +225,36 @@ export default function reportJson(id, currentUserId) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Escape regex special characters. Copied from filterDocs.js
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies searchTerm rules; combines existing access $or with search $or using $and.
|
||||
*/
|
||||
export function applySearch({ reportId, baseWhere, searchTerm }) {
|
||||
if (!searchTerm) return baseWhere;
|
||||
|
||||
const escaped = escapeRegExp(searchTerm);
|
||||
const nameMatch = { Name: { $regex: `.*${escaped}.*`, $options: 'i' } };
|
||||
const emailMatch = { Email: { $regex: `.*${escaped}.*`, $options: 'i' } };
|
||||
|
||||
if (reportId === 'contacts') {
|
||||
return { ...baseWhere, $or: [nameMatch, emailMatch] };
|
||||
}
|
||||
|
||||
const searchOr = [
|
||||
nameMatch,
|
||||
{ Signers: { $inQuery: { className: 'contracts_Contactbook', where: emailMatch } } },
|
||||
];
|
||||
|
||||
// If baseWhere already has an access-control $or, combine using $and
|
||||
if (baseWhere.$or) {
|
||||
const { $or: accessOr, ...rest } = baseWhere;
|
||||
return { ...rest, $and: [{ $or: accessOr }, { $or: searchOr }] };
|
||||
}
|
||||
|
||||
return { ...baseWhere, $or: searchOr };
|
||||
}
|
||||
|
||||
@@ -58,15 +58,20 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
});
|
||||
} else {
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false }); // Disable SSL validation
|
||||
const localUrl = req.params.url;
|
||||
const newlocalUrl = localUrl.replace(
|
||||
'https://localhost:3001/api',
|
||||
'http://localhost:8080'
|
||||
);
|
||||
axios
|
||||
.get(req.params.url, { responseType: 'stream', httpsAgent })
|
||||
.get(newlocalUrl, { responseType: 'stream', httpsAgent: httpsAgent })
|
||||
.then(response => {
|
||||
response.data.pipe(Pdf);
|
||||
Pdf.on('finish', () => resolve('success'));
|
||||
Pdf.on('error', () => resolve('error'));
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('error', e.message);
|
||||
console.log('error in localurl', e.message);
|
||||
resolve('error');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
export default async function setWidgetPreferences(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const dateWidgetParams = request?.params?.dateWidget;
|
||||
if (!dateWidgetParams || Object.keys(dateWidgetParams).length === 0) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameters.');
|
||||
}
|
||||
try {
|
||||
const orgQuery = new Parse.Query('contracts_Users');
|
||||
orgQuery.equalTo('UserId', request.user);
|
||||
const userObj = await orgQuery.first({ useMasterKey: true });
|
||||
if (!userObj) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Permission denied.');
|
||||
}
|
||||
|
||||
const widgetPreferencesRaw = userObj?.get('WidgetPreferences');
|
||||
const widgetPreferences = Array.isArray(widgetPreferencesRaw) ? widgetPreferencesRaw : [];
|
||||
|
||||
// Normalize booleans (simple + safe)
|
||||
const dateWidget = {
|
||||
type: 'date',
|
||||
isSigningDate: !!dateWidgetParams.isSigningDate,
|
||||
isReadOnly: !!dateWidgetParams.isReadOnly,
|
||||
date: dateWidgetParams?.isSigningDate ? '' : dateWidgetParams?.date || '',
|
||||
format: dateWidgetParams.format || 'MM/dd/yyyy',
|
||||
};
|
||||
|
||||
// Upsert: replace if exists, otherwise append
|
||||
const updatedWidgetPreferences =
|
||||
widgetPreferences?.length > 0
|
||||
? widgetPreferences.map(w => (w.type === 'date' ? dateWidget : w))
|
||||
: [...widgetPreferences, dateWidget];
|
||||
|
||||
userObj.set('WidgetPreferences', updatedWidgetPreferences);
|
||||
const saved = await userObj.save(null, { useMasterKey: true });
|
||||
if (saved) {
|
||||
const response = typeof saved.toJSON === 'function' ? saved.toJSON() : saved;
|
||||
return {
|
||||
WidgetPreferences: response.WidgetPreferences,
|
||||
updatedAt: response.updatedAt,
|
||||
createdAt: response.createdAt,
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('set widget preferences error:', err);
|
||||
throw new Parse.Error(err?.code || 400, err?.message || 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
Generated
+650
-741
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open_sign_server",
|
||||
"version": "2.34.0",
|
||||
"version": "2.35.0",
|
||||
"description": "opensign server using the parse-server module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,15 +18,15 @@
|
||||
"watch": "nodemon index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.952.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.952.0",
|
||||
"@aws-sdk/client-s3": "^3.958.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.958.0",
|
||||
"@parse/fs-files-adapter": "^3.0.0",
|
||||
"@parse/push-adapter": "^8.1.0",
|
||||
"@parse/s3-files-adapter": "^4.2.0",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@signpdf/placeholder-pdf-lib": "^3.2.6",
|
||||
"@signpdf/signer-p12": "^3.2.4",
|
||||
"@signpdf/signpdf": "^3.2.5",
|
||||
"@signpdf/placeholder-pdf-lib": "^3.3.0",
|
||||
"@signpdf/signer-p12": "^3.3.0",
|
||||
"@signpdf/signpdf": "^3.3.0",
|
||||
"aws-sdk": "^2.1693.0",
|
||||
"axios": "^1.13.2",
|
||||
"coherentpdf": "^2.5.5",
|
||||
@@ -38,20 +38,20 @@
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^169.0.0",
|
||||
"libreoffice-convert": "^1.7.0",
|
||||
"mailgun.js": "^12.4.0",
|
||||
"mailgun.js": "^12.4.1",
|
||||
"moment": "^2.30.1",
|
||||
"mongodb": "^7.0.0",
|
||||
"multer": "^2.0.2",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.3",
|
||||
"nodemailer": "^7.0.11",
|
||||
"nodemailer": "^7.0.12",
|
||||
"p-limit": "^7.2.0",
|
||||
"parse": "^8.0.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^8.6.2",
|
||||
"parse-server-api-mail-adapter": "^5.0.2",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"posthog-node": "^5.17.2",
|
||||
"posthog-node": "^5.18.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"rate-limiter-flexible": "^9.0.1",
|
||||
"sharp": "^0.34.5",
|
||||
@@ -63,7 +63,7 @@
|
||||
"@babel/eslint-parser": "^7.28.5",
|
||||
"eslint": "^9.39.2",
|
||||
"jasmine": "^5.12.0",
|
||||
"mongodb-runner": "^6.4.0",
|
||||
"mongodb-runner": "^6.5.2",
|
||||
"nodemon": "^3.1.11",
|
||||
"nyc": "^17.1.0",
|
||||
"prettier": "^3.7.4"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
export const setDocumentCount = async extUserId => {
|
||||
if (extUserId) {
|
||||
try {
|
||||
// Update count in contracts_Users class
|
||||
const extQuery = new Parse.Query('contracts_Users');
|
||||
extQuery.equalTo('objectId', extUserId);
|
||||
const contractUser = await extQuery.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('DocumentCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating document count in contracts_Users: ' + error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
export const setTemplateCount = async extUserId => {
|
||||
if (extUserId) {
|
||||
try {
|
||||
// Update count in contracts_Users class
|
||||
const extQuery = new Parse.Query('contracts_Users');
|
||||
extQuery.equalTo('objectId', extUserId);
|
||||
const contractUser = await extQuery.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('TemplateCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating template count in contracts_Users: ' + error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user