Merge pull request #2042 from OpenSignLabs/updates-19765002150

v2.32.0
This commit is contained in:
prafull-opensignlabs
2025-11-28 18:54:17 +05:30
committed by GitHub
36 changed files with 639 additions and 37890 deletions
@@ -21,9 +21,7 @@ const DisabledButtonExamples = () => {
{/* Option C: Conditional styling */}
<button
className={`op-btn op-btn-primary ${
isDisabled ? "op-btn-vscode-disabled" : ""
}`}
className={`op-btn op-btn-primary ${isDisabled ? "op-btn-vscode-disabled" : ""}`}
disabled={isDisabled}
>
Conditional Button
-17180
View File
File diff suppressed because it is too large Load Diff
+11 -10
View File
@@ -1,14 +1,14 @@
{
"name": "open_sign",
"version": "2.30.0",
"version": "2.32.0",
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.9.0",
"@imgly/background-removal": "^1.7.0",
"@lottiefiles/dotlottie-react": "^0.16.2",
"@lottiefiles/dotlottie-react": "^0.17.8",
"@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.2.1",
"@reduxjs/toolkit": "^2.10.1",
"@reduxjs/toolkit": "^2.11.0",
"axios": "^1.13.2",
"date-fns-tz": "^3.2.0",
"file-saver": "^2.0.5",
@@ -35,7 +35,7 @@
"react-dom": "^18.3.1",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-i18next": "^15.7.4",
"react-i18next": "^16.3.5",
"react-konva": "^18.2.12",
"react-pdf": "^9.2.1",
"react-quill-new": "^3.6.0",
@@ -46,6 +46,7 @@
"react-select": "^5.10.2",
"react-signature-canvas": "^1.1.0-alpha.2",
"react-timezone-select": "^3.2.8",
"react-to-print": "^3.2.0",
"react-tooltip": "^5.30.0",
"reactour": "^1.19.4",
"redux": "^5.0.1",
@@ -100,9 +101,9 @@
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.6",
"@vitejs/plugin-react": "^4.7.0",
"@vitest/ui": "^3.2.4",
"@types/react": "^19.2.7",
"@vitejs/plugin-react": "^5.1.1",
"@vitest/ui": "^4.0.13",
"autoprefixer": "^10.4.22",
"babel-loader": "^10.0.0",
"commitizen": "^4.3.1",
@@ -118,13 +119,13 @@
"prettier": "^3.6.2",
"pretty-quick": "^4.2.2",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.18",
"tailwindcss": "^3.4.17",
"vite": "^7.2.4",
"vite-plugin-svgr": "^4.5.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4"
"vitest": "^4.0.13"
},
"engines": {
"node": "18 || 20 || 22"
}
}
}
@@ -168,7 +168,9 @@
"Save as template": "Als Vorlage speichern",
"Fix & resend": "Korrigieren und erneut senden",
"Kiosk Mode": "Kiosk Modus",
"Reset password": "Passwort zurücksetzen"
"Reset password": "Passwort zurücksetzen",
"View formdata": "Formulardaten ansehen",
"Download all formdata": "Alle Formulardaten herunterladen"
},
"report-heading": {
"Sr.No": "Nr.",
@@ -203,7 +205,8 @@
"Redirect url": "Weiterleitungs-URL",
"Created Date": "Erstellungsdatum",
"Updated Date": "Aktualisierungsdatum",
"Expiry Date": "Ablaufdatum"
"Expiry Date": "Ablaufdatum",
"Sent Date": "Sendedatum"
},
"report-help": {
"Draft Documents": "Dies sind Dokumente, die Sie begonnen, aber noch nicht zum Versenden fertiggestellt haben.",
@@ -442,7 +445,7 @@
"Next to current widget": "Neben dem aktuellen Widget"
},
"options": "Optionen",
"minimun-check": "Minimale Anzahl",
"minimum-check": "Minimale Anzahl",
"maximum-check": "Maximale Anzahl",
"cell-count": "Zellzahl",
"default-value": "Standardwert",
@@ -1274,5 +1277,24 @@
"global-smtp-settings-help-p2": "Um eine bessere Zustellbarkeit aller Signaturanfragen sicherzustellen, können Sie Ihren bevorzugten E-Mail-Anbieter verbinden. Wählen Sie eine der folgenden Optionen:",
"global-connect-to-gmail-help": "Verwenden Sie ein Gmail-Konto, um Signaturanfragen direkt aus dem Posteingang Ihrer Organisation zu senden. Dies verbessert die Zustellrate, Reputation und Zuverlässigkeit.",
"global-connect-to-smtp-help": "Verwenden Sie Ihren eigenen SMTP-Server, um E-Mails über Ihre Domain zu senden. Dies bietet volle Kontrolle über Ihre E-Mail-Infrastruktur, verbessert die Zustellbarkeit und sorgt für konsistentes Branding.",
"global-smtp-settings-help-p3": "Wenn keine individuellen Benutzereinstellungen konfiguriert sind, werden automatisch diese globalen Einstellungen angewendet."
"global-smtp-settings-help-p3": "Wenn keine individuellen Benutzereinstellungen konfiguriert sind, werden automatisch diese globalen Einstellungen angewendet.",
"hide-text-with-asterisks": "Text mit Sternchen ausblenden",
"hide-text-with-asterisks-help": "Der Text im unterzeichneten Dokument wird ausgeblendet, bleibt jedoch für den Dokumentabsender (Eigentümer) über sein {{appName}}-Konto zugänglich.",
"document-id": "Dokument-ID",
"sent-date": "Sendedatum",
"status": "Status",
"signer-form-data-for": "Formulardaten für Unterzeichner: {{signerName}} <{{signerEmail}}>",
"widget-name": "Widget-Name",
"widget-responses": "Widget-Antworten",
"formdata-download-note": "Hinweis: Signaturen, Stempel, Initialen und Bilder werden nicht exportiert.",
"export-all-formdata-error": "Sie können die Formulardaten nicht exportieren, da die Vorlage doppelte Widget-Namen enthält. Bitte erstellen Sie eine neue Vorlage, um Formulardaten exportieren zu können.",
"export-formdata-error": "Sie können die Formulardaten nicht exportieren, da das Dokument doppelte Widget-Namen enthält. Bitte erstellen Sie ein neues Dokument ohne doppelte Widget-Namen, um Formulardaten exportieren zu können.",
"duplicate-widget-name-error": "Dieser Widget-Name ist bereits einem anderen Unterzeichner zugewiesen. Bitte wählen Sie einen anderen Namen.",
"duplicate-template-widget-error": "Sie können diese Vorlage nicht duplizieren, da sie doppelte Widget-Namen enthält.",
"credit-alert": "Kreditwarnung",
"document-form-data": "Dokumenten-Formulardaten",
"view-formdata-paid-title-error": "Die Funktion 'Formulardaten anzeigen' ermöglicht es Ihnen, die von Unterzeichnern ausgefüllten Widget-Daten auszudrucken und herunterzuladen.",
"view-formdata-paid-error": "Hinweis: Die Funktion 'Formulardaten anzeigen' ist nur für Dokumente verfügbar, die ab Version v2.32.0 erstellt wurden und keine doppelten Widget-Namen enthalten.",
"download-all-formdata-paid-title-error": "Die Funktion 'Alle Formulardaten herunterladen' ermöglicht es Ihnen, die von Unterzeichnern ausgefüllten Widget-Daten für alle aus der Vorlage erstellten Dokumente herunterzuladen.",
"download-all-formdata-paid-error": "Hinweis: Die Funktion 'Alle Formulardaten herunterladen' ist nur für Vorlagen verfügbar, die ab Version v2.32.0 erstellt wurden und keine doppelten Widget-Namen enthalten."
}
@@ -168,7 +168,9 @@
"Save as template": "Save as template",
"Fix & resend": "Fix & Resend",
"Kiosk Mode": "Kiosk Mode",
"Reset password": "Reset password"
"Reset password": "Reset password",
"View formdata": "View formdata",
"Download all formdata": "Download all formdata"
},
"report-heading": {
"Sr.No": "Sr.No",
@@ -203,7 +205,8 @@
"Redirect url": "Redirect url",
"Created Date": "Created Date",
"Updated Date": "Updated Date",
"Expiry Date": "Expiry Date"
"Expiry Date": "Expiry Date",
"Sent Date": "Sent Date"
},
"report-help": {
"Draft Documents": "These are documents you have started but have not finalized for sending.",
@@ -443,7 +446,7 @@
"Next to current widget": "Next to current widget"
},
"options": "Options",
"minimun-check": "Minimun check",
"minimum-check": "Minimum check",
"maximum-check": "Maximum check",
"cell-count": "Cell count",
"default-value": "Default value",
@@ -1275,5 +1278,24 @@
"global-smtp-settings-help-p2": "To ensure better inbox deliverability for all signature request emails, you can connect your preferred email provider. Choose one of the following options:",
"global-connect-to-gmail-help": "Use a Gmail account to send signature request emails directly from your organization's inbox. This improves delivery rates, reputation, and overall reliability.",
"global-connect-to-smtp-help": "Use your own SMTP server to send emails through your domain. This provides full control over your email infrastructure, enhances deliverability, and maintains consistent branding.",
"global-smtp-settings-help-p3": "If no individual user-level settings are configured, these global settings will be applied automatically."
"global-smtp-settings-help-p3": "If no individual user-level settings are configured, these global settings will be applied automatically.",
"hide-text-with-asterisks": "Hide text with asterisks",
"hide-text-with-asterisks-help": "The text on the signed document will be concealed from view but remain accessible to the document sender (owner) from their {{appName}} account.",
"document-id": "Document ID",
"sent-date": "Sent Date",
"status": "Status",
"signer-form-data-for": "Signer Form data for: {{signerName}} <{{signerEmail}}>",
"widget-name": "Widget Name",
"widget-responses": "Widget Responses",
"formdata-download-note": "Note: Signatures, stamps, initials and images wont be exported.",
"export-all-formdata-error": "You can/t export form data because the template contains duplicate widget names. Please create a fresh template to be able to export form data.",
"export-formdata-error": "You can't export form data because the document contains duplicate widget names. Please create a fresh document without duplicate widget names to be able to export form data.",
"duplicate-widget-name-error": "This widget name is already assigned to another signer. Please choose a different name.",
"duplicate-template-widget-error": "You can't duplicate this template because it contains duplicate widget names.",
"credit-alert": "Credit Alert",
"document-form-data": "Document Form Data",
"view-formdata-paid-title-error": "The View Form Data feature lets you print and download the widget data filled in by signers while signing.",
"view-formdata-paid-error": "Note: 'View Formdata' feature is only available for documents created on or after v2.32.0 that do not contain duplicate widget names.",
"download-all-formdata-paid-title-error": "The Download All Form Data feature lets you download widget data filled in by signers for all documents created from the template.",
"download-all-formdata-paid-error": "Note: 'Download All Formdata' feature is only available for templates created on or after v2.32.0 that do not contain duplicate widget names."
}
@@ -168,7 +168,9 @@
"Save as template": "Guardar como plantilla",
"Fix & resend": "Corregir y reenviar",
"Kiosk Mode": "Modo Kiosco",
"Reset password": "Restablecer contraseña"
"Reset password": "Restablecer contraseña",
"View formdata": "Ver datos del formulario",
"Download all formdata": "Descargar todos los datos del formulario"
},
"report-heading": {
"Sr.No": "Nº",
@@ -203,7 +205,8 @@
"Redirect url": "URL de redirección",
"Created Date": "Fecha de creación",
"Updated Date": "Fecha de actualización",
"Expiry Date": "Fecha de vencimiento"
"Expiry Date": "Fecha de vencimiento",
"Sent Date": "Fecha de envío"
},
"report-help": {
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
@@ -443,7 +446,7 @@
"Next to current widget": "Junto al widget actual"
},
"options": "Opciones",
"minimun-check": "Chequeo mínimo",
"minimum-check": "Chequeo mínimo",
"maximum-check": "Chequeo máximo",
"cell-count": "recuento de células",
"default-value": "Valor por defecto",
@@ -1275,5 +1278,24 @@
"global-smtp-settings-help-p2": "Para garantizar una mejor entregabilidad de todos los correos de solicitud de firma, puede conectar su proveedor de correo preferido. Elija una de las siguientes opciones:",
"global-connect-to-gmail-help": "Use una cuenta de Gmail para enviar correos de solicitud de firma directamente desde la bandeja de entrada de su organización. Esto mejora la entregabilidad, la reputación y la fiabilidad.",
"global-connect-to-smtp-help": "Use su propio servidor SMTP para enviar correos a través de su dominio. Esto proporciona control total sobre su infraestructura de correo, mejora la entregabilidad y mantiene la coherencia de marca.",
"global-smtp-settings-help-p3": "Si no se configuran ajustes a nivel de usuario, se aplicarán automáticamente estos ajustes globales."
"global-smtp-settings-help-p3": "Si no se configuran ajustes a nivel de usuario, se aplicarán automáticamente estos ajustes globales.",
"hide-text-with-asterisks": "Ocultar texto con asteriscos",
"hide-text-with-asterisks-help": "El texto en el documento firmado se ocultará, pero seguirá siendo accesible para el remitente (propietario) desde su cuenta de {{appName}}.",
"document-id": "ID del documento",
"sent-date": "Fecha de envío",
"status": "Estado",
"signer-form-data-for": "Datos del formulario para el firmante: {{signerName}} <{{signerEmail}}>",
"widget-name": "Nombre del widget",
"widget-responses": "Respuestas del widget",
"formdata-download-note": "Nota: Las firmas, sellos, iniciales e imágenes no se exportarán.",
"export-all-formdata-error": "No puede exportar los datos del formulario porque la plantilla contiene nombres de widgets duplicados. Cree una nueva plantilla para poder exportar los datos.",
"export-formdata-error": "No puede exportar los datos del formulario porque el documento contiene nombres de widgets duplicados. Cree un documento nuevo sin nombres duplicados para poder exportar los datos.",
"duplicate-widget-name-error": "Este nombre de widget ya está asignado a otro firmante. Por favor elija un nombre diferente.",
"duplicate-template-widget-error": "No puede duplicar esta plantilla porque contiene nombres de widgets duplicados.",
"credit-alert": "Alerta de crédito",
"document-form-data": "Datos del formulario del documento",
"view-formdata-paid-title-error": "La función 'Ver datos del formulario' le permite imprimir y descargar los datos de los widgets completados por los firmantes durante la firma.",
"view-formdata-paid-error": "Nota: La función 'Ver datos del formulario' solo está disponible para documentos creados en o después de la versión v2.32.0 que no contengan nombres de widgets duplicados.",
"download-all-formdata-paid-title-error": "La función 'Descargar todos los datos del formulario' le permite descargar los datos de los widgets completados por los firmantes para todos los documentos creados a partir de la plantilla.",
"download-all-formdata-paid-error": "Nota: La función 'Descargar todos los datos del formulario' solo está disponible para plantillas creadas en o después de la versión v2.32.0 que no contengan nombres de widgets duplicados."
}
@@ -176,7 +176,8 @@
"Redirect url": "URL de redirection",
"Created Date": "Date de création",
"Updated Date": "Date de mise à jour",
"Expiry Date": "Date d'expiration"
"Expiry Date": "Date d'expiration",
"Sent Date": "Date d'envoi"
},
"btnLabel": {
"sign": "Signer",
@@ -203,7 +204,9 @@
"Save as template": "Enregistrer comme modèle",
"Fix & resend": "Corriger et renvoyer",
"Kiosk Mode": "Mode Kiosque",
"Reset password": "Réinitialiser le mot de passe"
"Reset password": "Réinitialiser le mot de passe",
"View formdata": "Voir les données du formulaire",
"Download all formdata": "Télécharger toutes les données du formulaire"
},
"report-help": {
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
@@ -442,7 +445,7 @@
"Next to current widget": "À côté du widget actuel"
},
"options": "Possibilités",
"minimun-check": "Vérification minimale",
"minimum-check": "Vérification minimale",
"maximum-check": "Contrôle maximum",
"cell-count": "numération cellulaire",
"default-value": "Valeur par défaut",
@@ -1274,5 +1277,24 @@
"global-smtp-settings-help-p2": "Pour assurer une meilleure délivrabilité de toutes les demandes de signature, vous pouvez connecter votre fournisseur de messagerie préféré. Choisissez l'une des options suivantes :",
"global-connect-to-gmail-help": "Utilisez un compte Gmail pour envoyer des demandes de signature directement depuis la boîte de réception de votre organisation. Cela améliore la délivrabilité, la réputation et la fiabilité.",
"global-connect-to-smtp-help": "Utilisez votre propre serveur SMTP pour envoyer des e-mails via votre domaine. Cela offre un contrôle total sur votre infrastructure de messagerie, améliore la délivrabilité et garantit une cohérence de marque.",
"global-smtp-settings-help-p3": "Si aucun paramètre utilisateur individuel n'est configuré, ces paramètres globaux seront appliqués automatiquement."
"global-smtp-settings-help-p3": "Si aucun paramètre utilisateur individuel n'est configuré, ces paramètres globaux seront appliqués automatiquement.",
"hide-text-with-asterisks": "Masquer le texte avec des astérisques",
"hide-text-with-asterisks-help": "Le texte sur le document signé sera masqué, mais restera accessible à l'expéditeur du document (propriétaire) depuis son compte {{appName}}.",
"document-id": "ID du document",
"sent-date": "Date d'envoi",
"status": "Statut",
"signer-form-data-for": "Données du formulaire pour le signataire : {{signerName}} <{{signerEmail}}>",
"widget-name": "Nom du widget",
"widget-responses": "Réponses du widget",
"formdata-download-note": "Remarque : Les signatures, tampons, initiales et images ne seront pas exportés.",
"export-all-formdata-error": "Vous ne pouvez pas exporter les données du formulaire car le modèle contient des noms de widgets en double. Veuillez créer un nouveau modèle pour pouvoir exporter les données.",
"export-formdata-error": "Vous ne pouvez pas exporter les données du formulaire car le document contient des noms de widgets en double. Veuillez créer un nouveau document sans doublons pour pouvoir exporter les données.",
"duplicate-widget-name-error": "Ce nom de widget est déjà attribué à un autre signataire. Veuillez choisir un autre nom.",
"duplicate-template-widget-error": "Vous ne pouvez pas dupliquer ce modèle car il contient des noms de widgets en double.",
"credit-alert": "Alerte de crédit",
"document-form-data": "Données du formulaire du document",
"view-formdata-paid-title-error": "La fonction 'Afficher les données du formulaire' vous permet dimprimer et de télécharger les données des widgets remplies par les signataires lors de la signature.",
"view-formdata-paid-error": "Remarque : La fonction 'Afficher les données du formulaire' est uniquement disponible pour les documents créés à partir de la version v2.32.0 et ne contenant pas de noms de widgets en double.",
"download-all-formdata-paid-title-error": "La fonction 'Télécharger toutes les données du formulaire' vous permet de télécharger les données des widgets remplies par les signataires pour tous les documents créés à partir du modèle.",
"download-all-formdata-paid-error": "Remarque : La fonction 'Télécharger toutes les données du formulaire' est uniquement disponible pour les modèles créés à partir de la version v2.32.0 et ne contenant pas de noms de widgets en double."
}
@@ -168,7 +168,9 @@
"Save as template": "टेम्पलेट के रूप में सहेजें",
"Fix & resend": "ठीक करें और पुनः भेजें",
"Kiosk Mode": "कियोस्क मोड",
"Reset password": "पासवर्ड रीसेट करें"
"Reset password": "पासवर्ड रीसेट करें",
"View formdata": "फॉर्म डेटा देखें",
"Download all formdata": "सभी फॉर्म डेटा डाउनलोड करें"
},
"report-heading": {
"Sr.No": "क्रमांक",
@@ -203,7 +205,8 @@
"Redirect url": "रीडायरेक्ट URL",
"Created Date": "निर्माण तिथि",
"Updated Date": "अद्यतन तिथि",
"Expiry Date": "समाप्ति तिथि"
"Expiry Date": "समाप्ति तिथि",
"Sent Date": "भेजे जाने की तिथि"
},
"report-help": {
"Draft Documents": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
@@ -442,7 +445,7 @@
"Next to current widget": "वर्तमान विजेट के आगे"
},
"options": "विकल्प",
"minimun-check": "न्यूनतम जाँच",
"minimum-check": "न्यूनतम जाँच",
"maximum-check": "अधिकतम जाँच",
"cell-count": "सेल्स संख्या",
"default-value": "डिफ़ॉल्ट मान",
@@ -1274,5 +1277,24 @@
"global-smtp-settings-help-p2": "सभी हस्ताक्षर अनुरोध ईमेल की बेहतर इनबॉक्स डिलीवरी सुनिश्चित करने के लिए, आप अपना पसंदीदा ईमेल प्रदाता जोड़ सकते हैं। निम्न विकल्पों में से एक चुनें:",
"global-connect-to-gmail-help": "अपनी संस्था की इनबॉक्स से सीधे हस्ताक्षर अनुरोध ईमेल भेजने के लिए Gmail खाते का उपयोग करें। यह डिलीवरी दर, प्रतिष्ठा और विश्वसनीयता में सुधार करता है।",
"global-connect-to-smtp-help": "अपने डोमेन के माध्यम से ईमेल भेजने के लिए अपना स्वयं का SMTP सर्वर उपयोग करें। यह आपकी ईमेल संरचना पर पूर्ण नियंत्रण प्रदान करता है, डिलीवरी को बेहतर बनाता है और ब्रांड की निरंतरता बनाए रखता है।",
"global-smtp-settings-help-p3": "यदि कोई व्यक्तिगत उपयोगकर्ता-स्तरीय सेटिंग्स कॉन्फ़िगर नहीं की गई हैं, तो ये वैश्विक सेटिंग्स स्वचालित रूप से लागू होंगी।"
"global-smtp-settings-help-p3": "यदि कोई व्यक्तिगत उपयोगकर्ता-स्तरीय सेटिंग्स कॉन्फ़िगर नहीं की गई हैं, तो ये वैश्विक सेटिंग्स स्वचालित रूप से लागू होंगी।",
"hide-text-with-asterisks": "टेक्स्ट को तारांकन (*) के साथ छुपाएँ",
"hide-text-with-asterisks-help": "हस्ताक्षरित दस्तावेज़ पर टेक्स्ट दृश्य से छिपा रहेगा, लेकिन दस्तावेज़ भेजने वाले (मालिक) के लिए उनके {{appName}} खाते से सुलभ रहेगा।",
"document-id": "दस्तावेज़ ID",
"sent-date": "भेजे जाने की तिथि",
"status": "स्थिति",
"signer-form-data-for": "हस्ताक्षरकर्ता के लिए फ़ॉर्म डेटा: {{signerName}} <{{signerEmail}}>",
"widget-name": "विजेट नाम",
"widget-responses": "विजेट प्रतिक्रियाएँ",
"formdata-download-note": "नोट: हस्ताक्षर, स्टैम्प, प्रारंभिक अक्षर और छवियाँ निर्यात नहीं की जाएँगी।",
"export-all-formdata-error": "आप फ़ॉर्म डेटा निर्यात नहीं कर सकते क्योंकि टेम्पलेट में डुप्लिकेट विजेट नाम हैं। कृपया नया टेम्पलेट बनाएं ताकि आप फ़ॉर्म डेटा निर्यात कर सकें।",
"export-formdata-error": "आप फ़ॉर्म डेटा निर्यात नहीं कर सकते क्योंकि दस्तावेज़ में डुप्लिकेट विजेट नाम हैं। कृपया नया दस्तावेज़ बनाएं जिसमें डुप्लिकेट विजेट नाम न हों।",
"duplicate-widget-name-error": "यह विजेट नाम पहले से ही किसी अन्य साइनर को सौंपा गया है। कृपया कोई दूसरा नाम चुनें।",
"duplicate-template-widget-error": "आप इस टेम्पलेट को डुप्लिकेट नहीं कर सकते क्योंकि इसमें डुप्लिकेट विजेट नाम हैं।",
"credit-alert": "क्रेडिट अलर्ट",
"document-form-data": "दस्तावेज़ फ़ॉर्म डेटा",
"view-formdata-paid-title-error": "'फ़ॉर्म डेटा देखें' सुविधा आपको हस्ताक्षर करते समय साइनरों द्वारा भरे गए विजेट डेटा को प्रिंट और डाउनलोड करने की सुविधा देती है.",
"view-formdata-paid-error": "नोट: 'फ़ॉर्म डेटा देखें' सुविधा केवल उन दस्तावेज़ों के लिए उपलब्ध है जो v2.32.0 या उसके बाद बनाए गए हों और जिनमें डुप्लिकेट विजेट नाम न हों.",
"download-all-formdata-paid-title-error": "'सभी फ़ॉर्म डेटा डाउनलोड करें' सुविधा आपको टेम्पलेट से बनाए गए सभी दस्तावेज़ों के लिए साइनरों द्वारा भरे गए विजेट डेटा को डाउनलोड करने देती है.",
"download-all-formdata-paid-error": "नोट: 'सभी फ़ॉर्म डेटा डाउनलोड करें' सुविधा केवल उन टेम्पलेट्स के लिए उपलब्ध है जो v2.32.0 या उसके बाद बनाए गए हों और जिनमें डुप्लिकेट विजेट नाम न हों."
}
@@ -168,7 +168,9 @@
"Save as template": "Salva come modello",
"Fix & resend": "Correggi e reinvia",
"Kiosk Mode": "Modalità Kiosk",
"Reset password": "Reimposta password"
"Reset password": "Reimposta password",
"View formdata": "Visualizza i dati del modulo",
"Download all formdata": "Scarica tutti i dati del modulo"
},
"report-heading": {
"Sr.No": "Nr.",
@@ -203,7 +205,8 @@
"Redirect url": "URL di reindirizzamento",
"Created Date": "Data di creazione",
"Updated Date": "Data di aggiornamento",
"Expiry Date": "Data di scadenza"
"Expiry Date": "Data di scadenza",
"Sent Date": "Data di invio"
},
"report-help": {
"Draft Documents": "Questi sono documenti che hai iniziato ma non hai ancora finalizzato per l'invio.",
@@ -442,7 +445,7 @@
"Next to current widget": "Accanto al widget corrente"
},
"options": "Opzioni",
"minimun-check": "Controllo minimo",
"minimum-check": "Controllo minimo",
"maximum-check": "Controllo massimo",
"cell-count": "conteggio delle cellule",
"default-value": "Valore predefinita",
@@ -1274,5 +1277,24 @@
"global-smtp-settings-help-p2": "Per garantire una migliore consegna delle e-mail di richiesta firma, puoi collegare il tuo provider di posta preferito. Scegli una delle seguenti opzioni:",
"global-connect-to-gmail-help": "Utilizza un account Gmail per inviare e-mail di richiesta firma direttamente dalla casella di posta della tua organizzazione. Migliora la consegna, la reputazione e l'affidabilità.",
"global-connect-to-smtp-help": "Utilizza il tuo server SMTP per inviare e-mail tramite il tuo dominio. Questo offre pieno controllo sulla tua infrastruttura e-mail, migliora la consegna e mantiene la coerenza del marchio.",
"global-smtp-settings-help-p3": "Se non sono configurate impostazioni a livello utente, verranno applicate automaticamente queste impostazioni globali."
"global-smtp-settings-help-p3": "Se non sono configurate impostazioni a livello utente, verranno applicate automaticamente queste impostazioni globali.",
"hide-text-with-asterisks": "Nascondi il testo con asterischi",
"hide-text-with-asterisks-help": "Il testo nel documento firmato sarà nascosto dalla visualizzazione ma rimarrà accessibile al mittente (proprietario) tramite il suo account {{appName}}.",
"document-id": "ID documento",
"sent-date": "Data di invio",
"status": "Stato",
"signer-form-data-for": "Dati del modulo per il firmatario: {{signerName}} <{{signerEmail}}>",
"widget-name": "Nome del widget",
"widget-responses": "Risposte del widget",
"formdata-download-note": "Nota: Firme, timbri, iniziali e immagini non verranno esportati.",
"export-all-formdata-error": "Non è possibile esportare i dati del modulo perché il modello contiene nomi di widget duplicati. Creare un nuovo modello per poter esportare i dati.",
"export-formdata-error": "Non è possibile esportare i dati del modulo perché il documento contiene nomi di widget duplicati. Creare un nuovo documento senza nomi duplicati per poter esportare i dati.",
"duplicate-widget-name-error": "Questo nome di widget è già assegnato a un altro firmatario. Scegli un nome diverso.",
"duplicate-template-widget-error": "Non è possibile duplicare questo modello perché contiene nomi di widget duplicati.",
"credit-alert": "Avviso di credito",
"document-form-data": "Dati del modulo del documento",
"view-formdata-paid-title-error": "La funzione 'Visualizza dati del modulo' consente di stampare e scaricare i dati dei widget compilati dai firmatari durante la firma.",
"view-formdata-paid-error": "Nota: La funzione 'Visualizza dati del modulo' è disponibile solo per documenti creati dalla versione v2.32.0 in poi che non contengono nomi di widget duplicati.",
"download-all-formdata-paid-title-error": "La funzione 'Scarica tutti i dati del modulo' consente di scaricare i dati dei widget compilati dai firmatari per tutti i documenti creati dal modello.",
"download-all-formdata-paid-error": "Nota: La funzione 'Scarica tutti i dati del modulo' è disponibile solo per modelli creati dalla versione v2.32.0 in poi che non contengano nomi di widget duplicati."
}
Binary file not shown.
-1
View File
@@ -225,7 +225,6 @@ const AddUser = (props) => {
className="block text-xs font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
+5 -2
View File
@@ -25,13 +25,16 @@ const BulkSendUi = (props) => {
//function to check at least one signature field exist
const signatureExist = async () => {
const isPrefill = props?.Placeholders.some((x) => x?.Role === "prefill");
const isPrefill =
props?.Placeholders?.some((x) => x?.Role === "prefill") || false;
if (isPrefill) {
setIsPrefillExist(isPrefill);
}
setIsDisableBulkSend(false);
const getPlaceholder = props?.Placeholders;
const removePrefill = getPlaceholder.filter((x) => x?.Role !== "prefill");
const removePrefill = getPlaceholder?.filter(
(x) => x?.Role !== "prefill"
);
const checkIsSignatureExistt = removePrefill?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import ModalUi from "../primitives/ModalUi";
import { useTranslation } from "react-i18next";
@@ -7,7 +7,6 @@ const ColumnSelector = ({
allColumns = [],
visibleColumns = [],
columnLabels = {},
defaultColumns = [],
onApply,
onClose
}) => {
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import ModalUi from "../../primitives/ModalUi";
import { fontsizeArr, fontColorArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
@@ -7,7 +7,7 @@ export default function CellsSettingModal({
isOpen,
handleClose,
defaultData,
handleSave
handleSave,
}) {
const { t } = useTranslation();
const [name, setName] = useState("");
@@ -31,13 +31,16 @@ export default function CellsSettingModal({
name,
cellCount: parseInt(cellCount, 10),
fontSize,
fontColor
fontColor,
});
};
return (
<ModalUi isOpen={isOpen} handleClose={handleClose} title={t("widget-info")}>
<form onSubmit={onSubmit} className="p-[20px] text-base-content flex flex-col gap-3">
<ModalUi isOpen={isOpen} handleClose={handleClose} title={t("widget-info")}>
<form
onSubmit={onSubmit}
className="p-[20px] text-base-content flex flex-col gap-3"
>
<div>
<label htmlFor="name" className="text-[13px]">
{t("name")} <span className="text-[red]">*</span>
@@ -91,7 +94,10 @@ export default function CellsSettingModal({
</option>
))}
</select>
<span className="w-5 h-[19px] ml-1" style={{ background: fontColor }} />
<span
className="w-5 h-[19px] ml-1"
style={{ background: fontColor }}
/>
</div>
</div>
<div className="h-[1px] w-full bg-[#b7b3b3] my-[16px]"></div>
@@ -27,7 +27,7 @@ import moment from "moment";
import "../../styles/opensigndrive.css";
import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { setIsShowModal } from "../../redux/reducers/widgetSlice";
import { themeColor } from "../../constant/const";
import { useGuidelinesContext } from "../../context/GuidelinesContext";
@@ -566,7 +566,7 @@ function Placeholder(props) {
}
}
};
//function to calculate font size
// function to calculate font size
const calculateFont = (size, isMinHeight) => {
const containerScale = getContainerScale(
props.pdfOriginalWH,
@@ -574,7 +574,7 @@ function Placeholder(props) {
props.containerWH
);
const fontSize = (size || 12) * containerScale * props.scale;
//isMinHeight to set text box minimum height
// isMinHeight to set text box minimum height
if (isMinHeight) {
return fontSize * 1.5 + "px";
} else {
@@ -646,10 +646,7 @@ function Placeholder(props) {
};
const handleClearDate = () => {
setIsToday(false);
setSelectDate((prev) => ({
...prev,
date: ""
}));
setSelectDate((prev) => ({ ...prev, date: "" }));
};
return (
<>
@@ -667,12 +664,16 @@ function Placeholder(props) {
position: "absolute",
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
...(props.pos?.Width ? { width: props.pos?.Width - 5 } : {}),
...(props.pos?.Height ? { height: props.pos?.Height } : {}),
fontSize: fontSize,
color: fontColor,
zIndex: 99
}}
>
{props.pos?.options?.response}
{
props.pos?.options?.response
}
</span>
) : (
<Rnd
@@ -994,10 +995,7 @@ function Placeholder(props) {
type="checkbox"
className="op-checkbox op-checkbox-xs"
onClick={() => {
setSelectDate((prev) => ({
...prev,
date: ""
}));
setSelectDate((prev) => ({ ...prev, date: "" }));
setIsToday(!isToday);
}}
/>
@@ -1106,9 +1104,7 @@ function Placeholder(props) {
<button
type="button"
className="op-btn op-btn-primary"
onClick={() => {
saveDateSetting(selectDate);
}}
onClick={() => saveDateSetting(selectDate)}
>
{t("save")}
</button>
@@ -42,10 +42,10 @@ function PlaceholderType(props) {
// prefer the latest response value over any default value
const widgetData =
props.pos?.options?.response ?? props.pos?.options?.defaultValue ?? "";
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
// const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
const [widgetValue, setwidgetValue] = useState();
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
const [hint, setHint] = useState("");
// const [hint, setHint] = useState("");
const [imgUrl, setImgUrl] = useState("");
const fontSize = props.calculateFont(props.pos.options?.fontSize);
const fontColor = props.pos.options?.fontColor || "black";
@@ -72,11 +72,11 @@ function PlaceholderType(props) {
// keep displayed value in sync with the stored response
setwidgetValue(widgetData);
}
if (props.pos?.options?.hint) {
setHint(props.pos?.options.hint);
} else if (props.pos?.options?.validation?.type) {
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
}
// if (props.pos?.options?.hint) {
// setHint(props.pos?.options.hint);
// } else if (props.pos?.options?.validation?.type) {
// checkRegularExpress(props.pos?.options?.validation?.type, setHint);
// }
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos, widgetData, type]);
@@ -130,6 +130,14 @@ function PlaceholderType(props) {
loadImage();
}, [props.pos.SignUrl]);
const formatWidgetName = () => {
const widgetName = props?.pos?.options?.name;
const name = widgetName ? widgetName?.split(`-`) : ["-", "-", "-"];
const lastWord = name.length > 1 ? `-${name[name.length - 1]}` : "";
const title =
props?.pos?.type === name[0] ? `${name[0]}${lastWord}` : widgetName;
return props?.pos?.options?.hint || title;
};
switch (type) {
case "signature":
return props.pos.SignUrl ? (
@@ -150,7 +158,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{hint || widgetTypeTranslation}
{formatWidgetName()}
</div>
)}
</div>
@@ -174,7 +182,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{hint || widgetTypeTranslation}
{formatWidgetName()}
</div>
)}
</div>
@@ -221,9 +229,11 @@ function PlaceholderType(props) {
case textInputWidget:
return props.isSignYourself || iswidgetEnable ? (
<textarea
placeholder={hint || t("widgets-name.text")}
placeholder={formatWidgetName()}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
style={{
fontSize: fontSize,
@@ -237,7 +247,7 @@ function PlaceholderType(props) {
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{hint || widgetTypeTranslation}</span>
<span className="ml-0.5">{formatWidgetName()}</span>
</div>
);
case cellsWidget: {
@@ -256,12 +266,14 @@ function PlaceholderType(props) {
isEnabled={iswidgetEnable}
count={count}
height={height}
value={cells.join("")}
value={
cells.join("")
}
editable={isEditable}
resizable={props?.isAllowModify}
fontSize={fontSize}
fontColor={fontColor}
hint={hint}
hint={formatWidgetName()}
onCellCountChange={handleCellResize}
/>
);
@@ -295,7 +307,7 @@ function PlaceholderType(props) {
}}
className="font-medium text-center"
>
{hint || widgetTypeTranslation}
{formatWidgetName()}
</div>
)}
</div>
@@ -304,9 +316,11 @@ function PlaceholderType(props) {
return iswidgetEnable ? (
<textarea
readOnly
placeholder={hint || widgetTypeTranslation}
placeholder={formatWidgetName()}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
style={{
fontSize: fontSize,
@@ -319,16 +333,18 @@ function PlaceholderType(props) {
/>
) : (
<div className="flex h-full select-none-cls" style={textWidgetStyle}>
<span> {props.pos?.options?.hint || widgetTypeTranslation}</span>
<span className="ml-0.5">{formatWidgetName()}</span>
</div>
);
case "company":
return iswidgetEnable ? (
<textarea
readOnly
placeholder={hint || widgetTypeTranslation}
placeholder={formatWidgetName()}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
style={{
fontSize: fontSize,
@@ -341,16 +357,18 @@ function PlaceholderType(props) {
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{hint || widgetTypeTranslation}</span>
<span className="ml-0.5">{formatWidgetName()}</span>
</div>
);
case "job title":
return iswidgetEnable ? (
<textarea
readOnly
placeholder={hint || widgetTypeTranslation}
placeholder={formatWidgetName()}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
style={{
fontSize: fontSize,
@@ -363,7 +381,7 @@ function PlaceholderType(props) {
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{hint || widgetTypeTranslation}</span>
<span className="ml-0.5">{formatWidgetName()}</span>
</div>
);
case "date":
@@ -444,7 +462,7 @@ function PlaceholderType(props) {
}}
className="font-medium text-center"
>
{hint || widgetTypeTranslation}
{formatWidgetName()}
</div>
)}
</div>
@@ -453,9 +471,11 @@ function PlaceholderType(props) {
return iswidgetEnable ? (
<textarea
readOnly
placeholder={hint || widgetTypeTranslation}
placeholder={formatWidgetName()}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
style={{
fontSize: fontSize,
@@ -468,7 +488,7 @@ function PlaceholderType(props) {
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{hint || widgetTypeTranslation}</span>
<span className="ml-0.5">{formatWidgetName()}</span>
</div>
);
case radioButtonWidget:
@@ -513,7 +533,9 @@ function PlaceholderType(props) {
readOnly
placeholder={t("widgets-name.text")}
rows={1}
value={widgetValue}
value={
widgetValue
}
className={textWidgetCls}
style={{
fontFamily: "Arial, sans-serif",
@@ -546,7 +568,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{hint || widgetTypeTranslation}
{formatWidgetName()}
</div>
)}
</div>
@@ -1,4 +1,3 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
@@ -8,12 +8,16 @@ import {
cellsWidget,
textWidget
} from "../../constant/Utils";
import {
widgetNamesArr
} from "../../utils";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
const WidgetNameModal = (props) => {
const { t } = useTranslation();
const signTypes = props?.signatureType || signatureTypes;
const [lastSubmittedName, setLastSubmittedName] = useState("");
const [formdata, setFormdata] = useState({
name: "",
defaultValue: "",
@@ -46,7 +50,7 @@ const WidgetNameModal = (props) => {
[type]
);
const handleHint = () => {
const handleHintPlaceholder = () => {
const type = props.defaultdata?.type;
if (type === "signature") {
@@ -67,7 +71,7 @@ const WidgetNameModal = (props) => {
name: props.defaultdata?.options?.name || "",
defaultValue: props.defaultdata?.options?.defaultValue || "",
status: props.defaultdata?.options?.status || "required",
hint: props.defaultdata?.options?.hint || handleHint(),
hint: props.defaultdata?.options?.hint || "",
textvalidate:
props.defaultdata?.options?.validation?.type === "regex"
? props.defaultdata?.options?.validation?.pattern
@@ -75,12 +79,14 @@ const WidgetNameModal = (props) => {
isReadOnly: props.defaultdata?.options?.isReadOnly || false,
cellCount: props.defaultdata?.options?.cellCount || 5,
});
setLastSubmittedName(props.defaultdata?.options?.name || "");
} else {
setFormdata({
...formdata,
name: props.defaultdata?.options?.name || "",
cellCount: props.defaultdata?.options?.cellCount || 5,
});
setLastSubmittedName(props.defaultdata?.options?.name || "");
}
if (signTypes.length > 0) {
@@ -93,6 +99,19 @@ const WidgetNameModal = (props) => {
const handleSubmit = (e) => {
e.preventDefault();
if (props.handleData) {
const widgetNames = widgetNamesArr(
props.widgetsSource,
props?.activeSignerId
);
if (lastSubmittedName && lastSubmittedName !== formdata.name) {
const widgetNameExist = widgetNames?.find(
(widget) => widget === formdata.name
);
if (widgetNameExist) {
alert(t("duplicate-widget-name-error"));
return;
}
}
if (isSignOrInitials) {
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
const isDefaultSignTypeOnly =
@@ -292,7 +311,7 @@ const WidgetNameModal = (props) => {
{!props?.isSelfSign &&
!isSignOrInitials &&
props?.roleName !== "prefill" && (
<div className="mb-[0.75rem]">
<div className={showFontControls ? "mb-[0.5rem]" : "mb-[0.75rem]"}>
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
{statusArr.map((data, ind) => {
return (
@@ -324,30 +343,29 @@ const WidgetNameModal = (props) => {
{[
textInputWidget,
cellsWidget,
].includes(props.defaultdata?.type) &&
props?.roleName !== "prefill" && (
<div className="flex items-center">
<input
id="isReadOnly"
name="isReadOnly"
type="checkbox"
checked={formdata.isReadOnly}
className="op-checkbox op-checkbox-xs"
onChange={() =>
setFormdata((prev) => ({
...formdata,
isReadOnly: !prev.isReadOnly
}))
}
/>
<label
className="ml-1.5 mb-0 capitalize text-[13px]"
htmlFor="isreadonly"
>
{t("read-only")}
</label>
</div>
)}
].includes(props.defaultdata?.type) && (
<div className="flex items-center">
<input
id="isReadOnly"
name="isReadOnly"
type="checkbox"
checked={formdata.isReadOnly}
className="op-checkbox op-checkbox-xs"
onChange={() =>
setFormdata((prev) => ({
...formdata,
isReadOnly: !prev.isReadOnly
}))
}
/>
<label
className="ml-1.5 mb-0 capitalize text-[13px]"
htmlFor="isreadonly"
>
{t("read-only")}
</label>
</div>
)}
</div>
)}
{isSignOrInitials && (
@@ -387,6 +405,7 @@ const WidgetNameModal = (props) => {
maxLength={40}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="hint"
placeholder={handleHintPlaceholder()}
value={formdata.hint}
onChange={(e) => handleChange(e)}
/>
@@ -1718,7 +1718,7 @@ function WidgetsValueModal(props) {
// focusNextWidget(currWidgetsDetails?.key, xyPosition)
//function too use on click on next/finish button then update modal UI according to current widgets
const handleClickOnNext = (isFinishDoc) => {
const handleClickOnNext = async (isFinishDoc) => {
if (
["signature", "stamp", "image", "initials"].includes(
currWidgetsDetails?.type
@@ -1777,6 +1777,7 @@ function WidgetsValueModal(props) {
dispatch(setIsShowModal({ [nextWidgetDetails?.key]: true }));
props.setCurrWidgetsDetails(nextWidgetDetails);
}
};
//function is used to disable/enable save button
const handleDisable = () => {
+47 -40
View File
@@ -116,6 +116,19 @@ export const getSecureUrl = async (url) => {
}
};
// `generateId` generates a random alphanumeric ID of specified length.
export function generateId(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
/**
* Removes a trailing path segment from a URL string, if present.
*
@@ -447,14 +460,15 @@ export const addWidgetOptions = (
widgetValue
) => {
let defaultOpt;
const id = generateId(6);
if (placeholder) {
const countSameWidget = placeholder?.reduce((count, page) => {
return count + page.pos.filter((item) => item.type === type).length;
}, 0);
const count = countSameWidget + 1;
defaultOpt = { name: `${type}-${count}`, status: "required" };
defaultOpt = { name: `${type}-${id}-${count}`, status: "required" };
} else {
defaultOpt = { name: `${type}-1`, status: "required" };
defaultOpt = { name: `${type}-${id}-1`, status: "required" };
}
switch (type) {
case "signature":
@@ -464,7 +478,10 @@ export const addWidgetOptions = (
case "checkbox":
return { ...defaultOpt, isReadOnly: false, isHideLabel: false };
case textInputWidget:
return { ...defaultOpt, isReadOnly: false };
return {
...defaultOpt,
isReadOnly: false
};
case cellsWidget:
return {
...defaultOpt,
@@ -476,11 +493,20 @@ export const addWidgetOptions = (
case "initials":
return defaultOpt;
case "name":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...defaultOpt,
defaultValue: widgetValue ? widgetValue : ""
};
case "company":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...defaultOpt,
defaultValue: widgetValue ? widgetValue : ""
};
case "job title":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...defaultOpt,
defaultValue: widgetValue ? widgetValue : ""
};
case "date": {
const dateFormat = signer?.DateFormat
? selectFormat(signer?.DateFormat)
@@ -505,8 +531,7 @@ export const addWidgetOptions = (
return {
...defaultOpt,
values: [],
isReadOnly: false,
isHideLabel: false
isReadOnly: false
};
case textWidget:
return defaultOpt;
@@ -523,14 +548,15 @@ export const addWidgetSelfsignOptions = (
) => {
let defaultOpt;
//condition to handle widgets name field
const id = generateId(6);
if (placeholder) {
const countSameWidget = placeholder?.reduce((count, page) => {
return count + page.pos.filter((item) => item.type === type).length;
}, 0);
const count = countSameWidget + 1;
defaultOpt = { name: `${type}-${count}`, status: "required" };
defaultOpt = { name: `${type}-${id}-${count}`, status: "required" };
} else {
defaultOpt = { name: `${type}-1`, status: "required" };
defaultOpt = { name: `${type}-${id}-1`, status: "required" };
}
switch (type) {
case "signature":
@@ -3872,25 +3898,17 @@ export const sendEmailToSigners = async (
} else {
data = { SendMail: true };
}
try {
await axios.put(
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${
pdfDetails[0]?.objectId
}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": sessiontoken
}
}
);
} catch (err) {
console.log("axois err ", err);
}
} catch (e) {
console.log("error", e);
const docUrl = `${localStorage.getItem("baseUrl")}classes/contracts_Document`;
await axios.put(`${docUrl}/${pdfDetails[0]?.objectId}`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": sessiontoken
}
});
} catch (error) {
const err = error?.response?.data?.error || error?.message;
console.error("Error while updating doc: ", err);
}
}
return { status: "success" };
@@ -4049,17 +4067,6 @@ export const getDefaultDate = (dateStr, format) => {
//function to get default format
export const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
export function generateId(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
//function to handle widget background color
export const handleBackground = (data, isNeedSign, uniqueId) => {
if (data) {
+96 -78
View File
@@ -46,7 +46,8 @@ function Form() {
}
const Forms = (props) => {
const appName = "OpenSign™";
const appName =
"OpenSign™";
const { t } = useTranslation();
const abortController = new AbortController();
const inputFileRef = useRef(null);
@@ -69,7 +70,7 @@ const Forms = (props) => {
NotifyOnSignatures: "",
Bcc: [],
RedirectUrl: "",
AllowModifications: false
AllowModifications: false,
});
const [fileupload, setFileUpload] = useState("");
const [selectedFiles, setSelectedFiles] = useState([]);
@@ -100,7 +101,8 @@ const Forms = (props) => {
extUserData?.IsTourEnabled === false
? "false"
: "true";
const fileSize = maxFileSize;
const fileSize =
maxFileSize;
useEffect(() => {
handleReset();
return () => abortController.abort();
@@ -112,12 +114,12 @@ const Forms = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const initializeValues = async () => {
setFormData((obj) => ({
...obj,
NotifyOnSignatures: true,
SendinOrder: sendinorder,
IsTourEnabled: istourenabled
}));
setFormData((obj) => ({
...obj,
NotifyOnSignatures: true,
SendinOrder: sendinorder,
IsTourEnabled: istourenabled
}));
};
// `removeFile` is used to reset progress, percentage and remove file if exists
@@ -276,7 +278,8 @@ const Forms = (props) => {
setfileload(false);
removeFile(e);
console.log("err in docx to pdf ", err);
const error = t("docx-error");
const error =
t("docx-error");
if (err?.code === 209) {
dispatch(sessionStatus(false));
} else {
@@ -305,25 +308,25 @@ const Forms = (props) => {
const name = generatePdfName(16);
const pdfName = `${name}.pdf`;
let uploadedUrl = "";
const parseFile = new Parse.File(
pdfName,
[...pdfBytes],
"application/pdf"
);
const response = await parseFile.save({
progress: (progressValue, loaded, total) => {
if (progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
const parseFile = new Parse.File(
pdfName,
[...pdfBytes],
"application/pdf"
);
const response = await parseFile.save({
progress: (progressValue, loaded, total) => {
if (progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
if (response.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
uploadedUrl = fileRes.url;
}
}
});
if (response.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
uploadedUrl = fileRes.url;
}
}
if (uploadedUrl) {
const tenantId = localStorage.getItem("TenantId");
const userId = extUserData?.UserId?.objectId;
@@ -339,6 +342,7 @@ const Forms = (props) => {
setSelectedFiles([]);
}
} catch (error) {
console.error("error ", error);
if (error?.code === 209) {
dispatch(sessionStatus(false));
} else {
@@ -385,13 +389,16 @@ const Forms = (props) => {
object.set("Description", formData?.Description);
object.set("Note", formData?.Note);
if (props.title === "Request Signatures") {
if (
extUserData?.TenantId?.RequestBody &&
extUserData?.TenantId?.RequestSubject
) {
object.set("RequestBody", extUserData?.TenantId?.RequestBody);
object.set("RequestSubject", extUserData?.TenantId?.RequestSubject);
}
if (
extUserData?.TenantId?.RequestBody &&
extUserData?.TenantId?.RequestSubject
) {
object.set("RequestBody", extUserData?.TenantId?.RequestBody);
object.set(
"RequestSubject",
extUserData?.TenantId?.RequestSubject
);
}
}
if (props.title !== "Sign Yourself") {
const isChecked = formData.SendinOrder === "false" ? false : true;
@@ -410,11 +417,11 @@ const Forms = (props) => {
object.set("RemindOnceInEvery", remindOnceInEvery);
object.set("IsTourEnabled", isTourEnabled);
object.set("TimeToCompleteDays", TimeToCompleteDays);
object.set("AllowModifications", false);
object.set("IsEnableOTP", false);
if (formData.NotifyOnSignatures !== undefined) {
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
}
object.set("AllowModifications", false);
object.set("IsEnableOTP", false);
if (formData.NotifyOnSignatures !== undefined) {
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
}
if (formData?.RedirectUrl) {
object.set("RedirectUrl", formData.RedirectUrl);
}
@@ -451,9 +458,9 @@ const Forms = (props) => {
setBcc([]);
setFolder({ ObjectId: "", Name: "" });
const notifySign =
extUserData?.NotifyOnSignatures !== undefined
? extUserData?.NotifyOnSignatures
: true;
extUserData?.NotifyOnSignatures !== undefined
? extUserData?.NotifyOnSignatures
: true;
setFormData({
Name: "",
Description: "",
@@ -471,7 +478,7 @@ const Forms = (props) => {
IsEnableOTP: "false",
IsTourEnabled: istourenabled,
RedirectUrl: "",
AllowModifications: false
AllowModifications: false,
});
setFileUpload("");
setSelectedFiles([]);
@@ -533,9 +540,9 @@ const Forms = (props) => {
setBcc([]);
setFolder({ ObjectId: "", Name: "" });
const notifySign =
extUserData?.NotifyOnSignatures !== undefined
? extUserData?.NotifyOnSignatures
: true;
extUserData?.NotifyOnSignatures !== undefined
? extUserData?.NotifyOnSignatures
: true;
let obj = {
Name: "",
Description: "",
@@ -553,7 +560,7 @@ const Forms = (props) => {
IsTourEnabled: istourenabled,
NotifyOnSignatures: notifySign,
RedirectUrl: "",
AllowModifications: false
AllowModifications: false,
};
setFormData(obj);
removeFile();
@@ -575,27 +582,35 @@ const Forms = (props) => {
const name = generatePdfName(16);
const pdfFile = await decryptPdf(formData?.file, formData?.password);
setIsDecrypting(false);
const res = await getFileAsArrayBuffer(pdfFile);
const flatPdf = await flattenPdf(res);
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
await parseFile.save({
progress: (progressValue, loaded, total) => {
if (progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
const res = await getFileAsArrayBuffer(pdfFile);
const flatPdf = await flattenPdf(res);
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
await parseFile.save({
progress: (progressValue, loaded, total) => {
if (progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
removeFile();
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, password: "", Name: title }));
SaveFileSize(size, fileRes?.url, tenantId, userId);
return fileRes.url;
} else {
removeFile();
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
const fileRes = await getSecureUrl(parseFile.url());
if (fileRes.url) {
setFileUpload(fileRes.url);
removeFile();
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, password: "", Name: title }));
SaveFileSize(size, fileRes?.url, tenantId, userId);
return fileRes.url;
} else {
removeFile();
setFormData((prev) => ({ ...prev, password: "" }));
@@ -604,14 +619,6 @@ const Forms = (props) => {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
} else {
removeFile();
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
} catch (err) {
removeFile();
if (err?.code === 209) {
@@ -1054,7 +1061,10 @@ const Forms = (props) => {
{isAdvanceOpt && (
<div
style={{
height: props.title === "New Template" ? "100px" : "280px"
height:
props.title === "New Template"
? "100px"
: "280px"
}}
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
></div>
@@ -1163,7 +1173,11 @@ const Forms = (props) => {
</Tooltip>
</label>
<div className="flex flex-col md:flex-row md:gap-4">
<div className={`flex items-center gap-2 ml-2 mb-1`}>
<div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input
className="mr-[2px] op-radio op-radio-xs"
type="radio"
@@ -1172,7 +1186,11 @@ const Forms = (props) => {
/>
<div className="text-center">{t("yes")}</div>
</div>
<div className={`flex items-center gap-2 ml-2 mb-1`}>
<div
className={
`flex items-center gap-2 ml-2 mb-1`
}
>
<input
className="mr-[2px] op-radio op-radio-xs"
type="radio"
+1 -1
View File
@@ -463,7 +463,7 @@ function GuestLogin() {
e.preventDefault();
setIsOptionalDetails(!isOptionalDetails);
}}
className="op-link op-link-secondary max-w-fit text-xs"
className="text-base-content/60 no-underline hover:underline focus:outline-none max-w-fit text-xs"
>
{isOptionalDetails
? t("hide-optional-details")
+30 -1
View File
@@ -409,6 +409,35 @@ function PdfRequestFiles(
} else {
const obj = documentData?.[0];
setSendInOrder(obj?.SendinOrder || false);
if (
obj?.Signers?.length &&
obj?.Placeholders?.length &&
currUserId !== "undefined"
) {
const params = {
event: "viewed",
contactId: currUserId,
body: {
objectId: documentData?.[0].objectId,
}
};
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/triggerevent`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
} catch (err) {
console.log("Err ", err);
}
}
}
let signers = [];
@@ -729,7 +758,7 @@ function PdfRequestFiles(
docId,
contactId,
objectId,
widgets
widgets,
);
if (resSign && resSign.status === "success") {
dispatch(setTypedSignFont("Fasthand"));
+6 -3
View File
@@ -1617,7 +1617,7 @@ function PlaceHolderSign() {
{},
fontSize: textSize || 12,
fontColor: textColor || "black",
isReadOnly: defaultdata?.isReadOnly || false
isReadOnly: defaultdata?.isReadOnly || false,
}
};
} else if (position.type === cellsWidget) {
@@ -1637,7 +1637,7 @@ function PlaceHolderSign() {
{},
fontSize: textSize || 12,
fontColor: textColor || "black",
isReadOnly: defaultdata?.isReadOnly || false
isReadOnly: defaultdata?.isReadOnly || false,
}
};
}
@@ -1660,7 +1660,7 @@ function PlaceHolderSign() {
defaultValue: defaultdata.defaultValue,
hint: defaultdata?.hint || "",
fontSize: textSize || 12,
fontColor: textColor || "black"
fontColor: textColor || "black",
}
};
}
@@ -1743,11 +1743,14 @@ function PlaceHolderSign() {
}
};
const handleLinkUser = (id) => {
setRoleName("");
setIsAddUser({ [id]: true });
};
//`handleAddUser` function to use add new user
const handleAddUser = (data, signerObjId) => {
const id = signerObjId ? signerObjId : uniqueId;
setRoleName("");
setCurrWidgetsDetails({});
if (isAddSigner) {
handleAddNewRecipients(data);
} else {
@@ -144,7 +144,6 @@ function SignYourSelf() {
const [isUploadPdf, setIsUploadPdf] = useState(false);
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
const [widgetsTour, setWidgetsTour] = useState(false);
const [owner, setOwner] = useState({});
const index = xyPosition?.findIndex((object) => {
return object.pageNumber === pageNumber;
@@ -160,6 +160,7 @@ const TemplatePlaceholder = () => {
const [documentDetails, setDocumentDetails] = useState();
const [copied, setCopied] = useState(false);
const [isUseTemplate, setIsUseTemplate] = useState(false);
const [prevRole, setPrevRole] = useState("");
const currentUser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
@@ -1117,6 +1118,7 @@ const TemplatePlaceholder = () => {
// `handleAddSigner` is used to open Add Role Modal
const handleAddSigner = () => {
setIsModalRole(true);
setPrevRole(roleName || "");
setRoleName("");
};
@@ -1144,6 +1146,7 @@ const TemplatePlaceholder = () => {
setSignerPos((prev) => [...prev, signerPosObj]);
setIsModalRole(false);
setRoleName("");
setCurrWidgetsDetails({});
setUniqueId(Id);
setBlockColor(color[index]);
setIsSelectId(index);
@@ -1305,6 +1308,7 @@ const TemplatePlaceholder = () => {
};
const handleCloseRoleModal = () => {
setRoleName(prevRole || "");
setIsModalRole(false);
};
const handleSaveWidgetsOptions = (
@@ -1522,7 +1526,7 @@ const TemplatePlaceholder = () => {
defaultValue: defaultdata.defaultValue,
hint: defaultdata?.hint || "",
fontSize: textSize || 12,
fontColor: textColor || "black"
fontColor: textColor || "black",
}
};
}
+6 -2
View File
@@ -375,10 +375,14 @@ const UserList = () => {
</td>
{handleActiveToggleVisibility(item) ? (
<td className="px-4 py-2 font-semibold">
<label className="cursor-pointer relative block items-center mb-0">
<label
htmlFor={`isdisabled-${item.objectId}`}
className="cursor-pointer relative block items-center mb-0"
>
<input
id={`isdisabled-${item.objectId}`}
type="checkbox"
className="op-toggle transition-all op-toggle-secondary"
className="op-toggle checked:[--tglbg:#3368ff] transition-all checked:text-white"
checked={item?.IsDisabled !== true}
onChange={() => handleToggleBtn(item)}
/>
+13 -5
View File
@@ -1,4 +1,7 @@
import React, { useState, useEffect } from "react";
import React, {
useState,
useEffect,
} from "react";
import { Navigate, useNavigate } from "react-router";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -6,7 +9,10 @@ import dp from "../assets/images/dp.png";
import { sanitizeFileName } from "../utils";
import axios from "axios";
import Tooltip from "../primitives/Tooltip";
import { getSecureUrl, handleSendOTP } from "../constant/Utils";
import {
getSecureUrl,
handleSendOTP,
} from "../constant/Utils";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
@@ -127,7 +133,7 @@ function UserProfile() {
Name: obj.Name,
JobTitle: jobTitle,
Company: company,
Language: obj?.language || ""
Language: obj?.language || "",
};
await axios.put(
@@ -443,7 +449,7 @@ function UserProfile() {
<button
type="button"
onClick={(e) => {
editmode ? handleSubmit(e) : setEditMode(true);
editmode ? handleSubmit(e) : setEditMode(true);
}}
className="op-btn op-btn-primary md:w-[100px]"
>
@@ -454,7 +460,9 @@ function UserProfile() {
onClick={() =>
editmode ? handleCancel() : navigate("/changepassword")
}
className={`op-btn ${editmode ? "op-btn-ghost w-[100px]" : "op-btn-secondary"}`}
className={
`op-btn ${editmode ? "op-btn-ghost w-[100px]" : "op-btn-secondary"}`
}
>
{editmode ? t("cancel") : t("change-password")}
</button>
+2 -2
View File
@@ -139,7 +139,7 @@ const AddContact = (props) => {
)}
<div className="w-full mx-auto p-[8px]">
{!props?.isDisableTitle && (
<div className="text-[14px] font-[700]">{t("add-contact")}</div>
<div className="text-[14px] font-[700] text-base-content mb-1">{t("add-contact")}</div>
)}
{isUserExist && props?.isAddYourSelfCheckbox && (
<div className="mb-[0.75rem] flex items-center mt-1">
@@ -248,7 +248,7 @@ const AddContact = (props) => {
e.preventDefault();
setIsOptionalDetails(!isOptionalDetails);
}}
className="op-link op-link-secondary no-underline hover:underline"
className="text-base-content/60 no-underline hover:underline focus:outline-none"
>
{isOptionalDetails
? t("hide-optional-details")
@@ -238,8 +238,8 @@ const TemplatesReport = (props) => {
navigate(`/${act.redirectUrl}/${item.objectId}`);
} else {
// handle Use template
const placeholder = item?.Placeholders;
const isRoleExist = placeholder.filter((x) => x.Role !== "prefill");
const placeholder = item?.Placeholders || [];
const isRoleExist = placeholder?.filter((x) => x.Role !== "prefill");
//condition to check atleast one role is present for use template
if (isRoleExist && isRoleExist?.length > 0) {
const checkIsSignatureExist = isRoleExist?.every((placeholderObj) =>
@@ -346,9 +346,16 @@ const TemplatesReport = (props) => {
setIsShareWith({ [item.objectId]: true });
}
else if (act.action === "duplicate") {
const isPrefill = item?.Placeholders?.some((p) => p?.Role === "prefill");
setError(isPrefill ? t("duplicate-template-error") : "");
setIsModal({ [`duplicate_${item.objectId}`]: true });
const hasDuplicate = utils.hasDuplicateWidgetNames(item?.Placeholders);
if (hasDuplicate) {
setError(t("duplicate-template-widget-error"));
setIsModal({ [`duplicate_${item.objectId}`]: true });
} else {
const isPrefill =
item?.Placeholders?.some((p) => p?.Role === "prefill") ?? false;
setError(isPrefill ? t("duplicate-template-error") : "");
setIsModal({ [`duplicate_${item.objectId}`]: true });
}
} else if (act.action === "rename") {
setIsModal({ [`rename_${item.objectId}`]: true });
} else if (act.action === "edit") {
@@ -727,7 +734,7 @@ const TemplatesReport = (props) => {
// `handleBulkSend` is used to open modal as well as fetch template
// and show Ui on the basis template response handleBulkSendTemplate
const handleBulkSend = async (template) => {
const isPrefillExist = template?.Placeholders.some(
const isPrefillExist = template?.Placeholders?.some(
(x) => x.Role === "prefill"
);
if (isPrefillExist) {
@@ -756,6 +763,7 @@ const TemplatesReport = (props) => {
}
};
// `handleShareWith` is used to save teams in sharedWith field
const handleShareWith = async (e, template) => {
e.preventDefault();
+36
View File
@@ -41,6 +41,42 @@ export const hasSignatureWidget = (s) =>
(ph?.pos ?? []).some((p) => p?.type === "signature")
);
export const widgetNamesArr = (placeholders, signerId) => {
const widgetNames =
placeholders?.length > 0
? placeholders
?.filter((x) => x.Id !== signerId) // exclude current signer
?.flatMap((item) => item.placeHolder || []) // go inside placeHolder[]
?.flatMap((ph) => ph.pos || []) // go inside pos[]
?.map((pos) => pos.options?.name) // pick the name
.filter(Boolean)
: [];
return widgetNames;
};
// `hasDuplicateWidgetNames` checks for duplicate widget names across different placeholder objects
export const hasDuplicateWidgetNames = (placeholders = []) => {
const seen = new Set(); // names we've already seen in previous objs
for (const obj of placeholders || []) {
const widgetNames =
obj?.placeHolder
?.flatMap((ph) => ph.pos || [])
?.map((pos) => pos.options?.name)
.filter(Boolean) || [];
const uniqueNames = [...new Set(widgetNames)]; // ignore duplicates inside same obj
const isDuplicate = uniqueNames.some((name) => seen.has(name));
if (isDuplicate) return true; // early exit on first cross-obj duplicate
uniqueNames.forEach((name) => seen.add(name));
}
return false;
};
/**
*
* @param {Array<object>} pages
+2
View File
@@ -61,6 +61,7 @@ import saveSignature from './parsefunction/saveSignature.js';
import manageSign from './parsefunction/manageSign.js';
import getSignature from './parsefunction/getSignature.js';
import updateEmailTemplates from './parsefunction/updateEmailTemplates.js';
import triggerEvent from './parsefunction/triggerEvent.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);
@@ -132,3 +133,4 @@ Parse.Cloud.define('savesignature', saveSignature);
Parse.Cloud.define('managesign', manageSign);
Parse.Cloud.define('getdefaultsignature', getSignature);
Parse.Cloud.define('updateemailtemplates', updateEmailTemplates);
Parse.Cloud.define('triggerevent', triggerEvent);
@@ -61,6 +61,7 @@ export default async function saveAsTemplate(request) {
status: 'required',
response: '',
...(p?.options?.defaultValue ? { defaultValue: '' } : {}),
...(p?.options?.isReadOnly ? { isReadOnly: false } : {}),
},
})),
}));
@@ -89,7 +90,14 @@ export default async function saveAsTemplate(request) {
pos: (page.pos || []).map(widget => {
// if there is a defaultValue in options, zero it out
if (widget.options && widget.options.defaultValue !== undefined) {
return { ...widget, options: { ...widget.options, defaultValue: '' } }; // reset only the value
return {
...widget,
options: {
...widget.options,
defaultValue: '',
...(widget?.options?.isReadOnly ? { isReadOnly: false } : {}),
},
}; // reset only the value
}
// otherwise, return the widget unchanged
return widget;
@@ -0,0 +1,64 @@
import axios from 'axios';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function triggerEvent(request) {
const event = request.params.event;
const body = request.params.body;
const docId = body.objectId;
const contactId = request.params.contactId;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = serverAppId;
try {
const docQuery = new Parse.Query('contracts_Document');
docQuery.select(['Name', 'IsEnableOTP', 'SignedUrl', 'AuditTrail']);
const docRes = await docQuery.get(docId, { useMasterKey: true });
const _docRes = docRes && docRes?.toJSON();
const isEnableOTP = docRes?.get('IsEnableOTP') || false;
let userId;
if (isEnableOTP) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
userId = userRes.data && userRes.data.objectId;
if (!userId) {
return { message: 'User not found!' };
}
}
if (event === 'viewed' && contactId) {
const auditTrail = Array.isArray(_docRes.AuditTrail) ? _docRes.AuditTrail : [];
const isUserExist = auditTrail.some(x => x?.UserPtr?.objectId === contactId && x?.ViewedOn);
if (!isUserExist) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactId,
};
const date = new Date().toISOString();
const newEntry = {
UserPtr: contactPtr,
SignedUrl: _docRes?.SignedUrl || '',
Activity: 'Viewed',
ipAddress: request.headers['x-real-ip'],
ViewedOn: date,
};
// update Audit trail entry
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docRes.id;
updateDoc.set('AuditTrail', [...auditTrail, newEntry]);
await updateDoc.save(null, { useMasterKey: true });
}
}
return { message: 'event called!' };
} catch (err) {
console.log(`Err in triggerEvent: ${err}`);
return { message: 'Something went wrong!' };
}
}
-20438
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -1,6 +1,6 @@
{
"name": "open_sign_server",
"version": "2.30.0",
"version": "2.32.0",
"description": "opensign server using the parse-server module",
"repository": {
"type": "git",
@@ -18,9 +18,10 @@
"watch": "nodemon index.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.936.0",
"@aws-sdk/s3-request-presigner": "^3.936.0",
"@aws-sdk/client-s3": "^3.940.0",
"@aws-sdk/s3-request-presigner": "^3.940.0",
"@parse/fs-files-adapter": "^3.0.0",
"@parse/push-adapter": "^7.0.0",
"@parse/s3-files-adapter": "^4.2.0",
"@pdf-lib/fontkit": "^1.1.1",
"@signpdf/placeholder-pdf-lib": "^3.2.6",
@@ -37,21 +38,21 @@
"generate-api-key": "^1.0.2",
"googleapis": "^166.0.0",
"libreoffice-convert": "^1.7.0",
"mailgun.js": "^12.1.1",
"mailgun.js": "^12.2.0",
"mongodb": "^5.9.2",
"multer": "^2.0.2",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^7.0.10",
"nodemailer": "^7.0.11",
"p-limit": "^7.2.0",
"parse": "^7.0.2",
"parse-dbtool": "^1.2.0",
"parse-server": "^8.4.0",
"parse-server-api-mail-adapter": "^4.1.0",
"parse-server-api-mail-adapter": "^5.0.2",
"pdf-lib": "^1.17.1",
"posthog-node": "^5.13.2",
"posthog-node": "^5.14.0",
"qrcode": "^1.5.4",
"rate-limiter-flexible": "^8.2.1",
"rate-limiter-flexible": "^8.3.0",
"sharp": "^0.34.5",
"speakeasy": "^2.0.0",
"ws": "^8.18.3"
@@ -69,7 +70,8 @@
"overrides": {
"ws": "$ws",
"parse": "$parse",
"form-data": "$form-data"
"form-data": "$form-data",
"@parse/push-adapter": "$@parse/push-adapter"
},
"engines": {
"node": "18 || 20 || 22"