mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-05 01:07:39 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca1b87309f | ||
|
|
0b4548130d | ||
|
|
e8b3c7fed8 | ||
|
|
2ce43ec2c8 | ||
|
|
d5af1e8a17 | ||
|
|
fab7998e22 | ||
|
|
4a4db6851e | ||
|
|
870973e7f3 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "open_sign",
|
"name": "open_sign",
|
||||||
"version": "2.26.0",
|
"version": "2.30.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^0.9.0",
|
"@formkit/auto-animate": "^0.9.0",
|
||||||
@@ -36,13 +36,13 @@
|
|||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-gtm-module": "^2.0.11",
|
"react-gtm-module": "^2.0.11",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-i18next": "^15.7.3",
|
"react-i18next": "^15.7.4",
|
||||||
"react-konva": "^18.2.12",
|
"react-konva": "^18.2.12",
|
||||||
"react-pdf": "^9.2.1",
|
"react-pdf": "^9.2.1",
|
||||||
"react-quill-new": "^3.6.0",
|
"react-quill-new": "^3.6.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-rnd": "^10.5.2",
|
"react-rnd": "^10.5.2",
|
||||||
"react-router": "^7.9.1",
|
"react-router": "^7.9.3",
|
||||||
"react-scrollbars-custom": "^4.1.1",
|
"react-scrollbars-custom": "^4.1.1",
|
||||||
"react-select": "^5.10.2",
|
"react-select": "^5.10.2",
|
||||||
"react-signature-canvas": "^1.1.0-alpha.2",
|
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"daisyui": "^4.12.24",
|
"daisyui": "^4.12.24",
|
||||||
"eslint": "^9.34.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"jsdom": "^27.0.0",
|
"jsdom": "^27.0.0",
|
||||||
|
|||||||
@@ -165,7 +165,8 @@
|
|||||||
"daily-mail-quota": "Tägliches E-Mail-Kontingent",
|
"daily-mail-quota": "Tägliches E-Mail-Kontingent",
|
||||||
"Save as template": "Als Vorlage speichern",
|
"Save as template": "Als Vorlage speichern",
|
||||||
"Fix & resend": "Korrigieren und erneut senden",
|
"Fix & resend": "Korrigieren und erneut senden",
|
||||||
"Kiosk Mode": "Kiosk Modus"
|
"Kiosk Mode": "Kiosk Modus",
|
||||||
|
"Reset password": "Passwort zurücksetzen"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nr.",
|
"Sr.No": "Nr.",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "Dropdown",
|
"dropdown": "Dropdown",
|
||||||
"radio button": "Radiobutton",
|
"radio button": "Radiobutton",
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
"email": "E-Mail"
|
"email": "E-Mail",
|
||||||
|
"number": "Zahl"
|
||||||
},
|
},
|
||||||
"fields": "Felder",
|
"fields": "Felder",
|
||||||
"recipients": "Empfänger",
|
"recipients": "Empfänger",
|
||||||
@@ -442,6 +444,19 @@
|
|||||||
"maximum-check": "Maximale Anzahl",
|
"maximum-check": "Maximale Anzahl",
|
||||||
"cell-count": "Zellzahl",
|
"cell-count": "Zellzahl",
|
||||||
"default-value": "Standardwert",
|
"default-value": "Standardwert",
|
||||||
|
"formula": "Formel",
|
||||||
|
"formula-placeholder": "Erstellen Sie eine Formel mit Zahlen-Widgets",
|
||||||
|
"formula-no-widgets": "Für diesen Unterzeichner sind noch keine weiteren Zahlen-Widgets vorhanden.",
|
||||||
|
"supported-operators": "Unterstützte Operatoren:",
|
||||||
|
"formula-helper": "Klicken Sie auf einen Zahlen-Widget-Chip, um ihn einzufügen (z. B. {{number-1}} + {{number-2}}).",
|
||||||
|
"formula-error-letter": "Widget-Namen müssen in doppelte geschweifte Klammern gesetzt werden (z. B. {{number-1}}).",
|
||||||
|
"formula-error-characters": "Außerhalb der Klammern sind nur Zahlen sowie +, -, *, /, (, ) erlaubt.",
|
||||||
|
"formula-error-braces": "Jede öffnende {{ muss eine passende }} haben.",
|
||||||
|
"formula-error-operators": "Verwenden Sie bei Operatoren oder Klammern mindestens zwei Werte.",
|
||||||
|
"formula-error-leading-operator": "Formeln dürfen nicht mit Operatoren beginnen.",
|
||||||
|
"decimal-places": "Dezimalstellen",
|
||||||
|
"number-of-decimal-places": "Anzahl der Dezimalstellen",
|
||||||
|
"decimal-places-helper": "Auf 0 setzen für ganze Zahlen",
|
||||||
"select": "Auswählen",
|
"select": "Auswählen",
|
||||||
"read-only": "Ist schreibgeschützt",
|
"read-only": "Ist schreibgeschützt",
|
||||||
"hide-labels": "Labels ausblenden",
|
"hide-labels": "Labels ausblenden",
|
||||||
@@ -1230,5 +1245,16 @@
|
|||||||
"save-as-template-msg": "Speichern Sie dieses Dokument als wiederverwendbare Vorlage, die Sie für zukünftige Dokumente erneut nutzen können.",
|
"save-as-template-msg": "Speichern Sie dieses Dokument als wiederverwendbare Vorlage, die Sie für zukünftige Dokumente erneut nutzen können.",
|
||||||
"public-credit-alert": "Das Signieren über diesen Link verbraucht Ihre kostenlosen E-Mail-Credits. Als kostenloser Nutzer erhalten Sie 15 E-Mail-Credits pro Monat, um zu verhindern, dass Spammer unsere Systeme missbrauchen. Um höhere Limits und ununterbrochenen Zugriff zu genießen, abonnieren Sie die <1>OpenSign™-Bezahlpläne.</1>",
|
"public-credit-alert": "Das Signieren über diesen Link verbraucht Ihre kostenlosen E-Mail-Credits. Als kostenloser Nutzer erhalten Sie 15 E-Mail-Credits pro Monat, um zu verhindern, dass Spammer unsere Systeme missbrauchen. Um höhere Limits und ununterbrochenen Zugriff zu genießen, abonnieren Sie die <1>OpenSign™-Bezahlpläne.</1>",
|
||||||
"know-more-about": "Mehr erfahren über",
|
"know-more-about": "Mehr erfahren über",
|
||||||
"free-unlimited-signatures": "Kostenlose unbegrenzte Signaturen"
|
"free-unlimited-signatures": "Kostenlose unbegrenzte Signaturen",
|
||||||
|
"email-quota-updated": "Tägliches E-Mail-Kontingent erfolgreich aktualisiert.",
|
||||||
|
"password-has-been-reset": "Passwort wurde zurückgesetzt.",
|
||||||
|
"Unauthorized to reset your own password.": "Nicht berechtigt, Ihr eigenes Passwort zurückzusetzen.",
|
||||||
|
"User not found or not allowed.": "Benutzer nicht gefunden oder nicht erlaubt.",
|
||||||
|
"User not found.": "Benutzer nicht gefunden.",
|
||||||
|
"Admin user tenant not found.": "Admin-Benutzer-Tenant nicht gefunden.",
|
||||||
|
"User is not authenticated.": "Benutzer ist nicht authentifiziert.",
|
||||||
|
"Please provide required parameters.": "Bitte geben Sie die erforderlichen Parameter an.",
|
||||||
|
"reset-password": "Benutzerpasswort zurücksetzen",
|
||||||
|
"enter-strong-password": "Geben Sie ein sicheres Passwort ein oder generieren Sie eines automatisch.",
|
||||||
|
"autogenerate": "Automatisch generieren"
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,8 @@
|
|||||||
"daily-mail-quota": "Daily Email Quota",
|
"daily-mail-quota": "Daily Email Quota",
|
||||||
"Save as template": "Save as template",
|
"Save as template": "Save as template",
|
||||||
"Fix & resend": "Fix & Resend",
|
"Fix & resend": "Fix & Resend",
|
||||||
"Kiosk Mode": "Kiosk Mode"
|
"Kiosk Mode": "Kiosk Mode",
|
||||||
|
"Reset password": "Reset password"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Sr.No",
|
"Sr.No": "Sr.No",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "dropdown",
|
"dropdown": "dropdown",
|
||||||
"radio button": "radio button",
|
"radio button": "radio button",
|
||||||
"image": "image",
|
"image": "image",
|
||||||
"email": "email"
|
"email": "email",
|
||||||
|
"number": "number"
|
||||||
},
|
},
|
||||||
"fields": "Fields",
|
"fields": "Fields",
|
||||||
"recipients": "Recipients",
|
"recipients": "Recipients",
|
||||||
@@ -443,6 +445,19 @@
|
|||||||
"maximum-check": "Maximum check",
|
"maximum-check": "Maximum check",
|
||||||
"cell-count": "Cell count",
|
"cell-count": "Cell count",
|
||||||
"default-value": "Default value",
|
"default-value": "Default value",
|
||||||
|
"formula": "Formula",
|
||||||
|
"formula-placeholder": "Build a formula using number widgets",
|
||||||
|
"formula-no-widgets": "No other number widgets are assigned to this signer yet.",
|
||||||
|
"supported-operators": "Supported operators:",
|
||||||
|
"formula-helper": "Click a number widget chip to insert it (e.g. {{number-1}} + {{number-2}}).",
|
||||||
|
"formula-error-letter": "Wrap widget names in double curly braces (e.g. {{number-1}}).",
|
||||||
|
"formula-error-characters": "Only numbers and +, -, *, /, (, ) are allowed outside of braces.",
|
||||||
|
"formula-error-braces": "Each opening {{ must have a matching }}.",
|
||||||
|
"formula-error-operators": "Add at least two values when using operators or parentheses.",
|
||||||
|
"formula-error-leading-operator": "Formulas cannot start with operators.",
|
||||||
|
"decimal-places": "Decimal Places",
|
||||||
|
"number-of-decimal-places": "Number of decimal places",
|
||||||
|
"decimal-places-helper": "Set to 0 for whole numbers",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
"read-only": "read only",
|
"read-only": "read only",
|
||||||
"hide-labels": "Hide labels",
|
"hide-labels": "Hide labels",
|
||||||
@@ -1231,5 +1246,16 @@
|
|||||||
"save-as-template-msg": "Save this document as a reusable template that you can use again for future documents.",
|
"save-as-template-msg": "Save this document as a reusable template that you can use again for future documents.",
|
||||||
"public-credit-alert": "Signing through this link consumes your free email credits. As a Free user, you are allotted 15 email credits per month to prevent spammers from abusing our systems. To enjoy higher limits and uninterrupted access, Subscribe to <1>OpenSign™ Paid plans.</1>",
|
"public-credit-alert": "Signing through this link consumes your free email credits. As a Free user, you are allotted 15 email credits per month to prevent spammers from abusing our systems. To enjoy higher limits and uninterrupted access, Subscribe to <1>OpenSign™ Paid plans.</1>",
|
||||||
"know-more-about": "Know more about",
|
"know-more-about": "Know more about",
|
||||||
"free-unlimited-signatures": "Free Unlimited Signatures"
|
"free-unlimited-signatures": "Free Unlimited Signatures",
|
||||||
|
"email-quota-updated": "Daily email quota updated successfully.",
|
||||||
|
"password-has-been-reset": "Password has been reset.",
|
||||||
|
"Unauthorized to reset your own password.": "Unauthorized to reset your own password.",
|
||||||
|
"User not found or not allowed.": "User not found or not allowed.",
|
||||||
|
"User not found.": "User not found.",
|
||||||
|
"Admin user tenant not found.": "Admin user tenant not found.",
|
||||||
|
"User is not authenticated.": "User is not authenticated.",
|
||||||
|
"Please provide required parameters.": "Please provide required parameters.",
|
||||||
|
"reset-password": "Reset User Password",
|
||||||
|
"enter-strong-password": "Enter a strong password or auto-generate one.",
|
||||||
|
"autogenerate": "Autogenerate"
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,8 @@
|
|||||||
"daily-mail-quota": "Cuota diaria de correos electrónicos",
|
"daily-mail-quota": "Cuota diaria de correos electrónicos",
|
||||||
"Save as template": "Guardar como plantilla",
|
"Save as template": "Guardar como plantilla",
|
||||||
"Fix & resend": "Corregir y reenviar",
|
"Fix & resend": "Corregir y reenviar",
|
||||||
"Kiosk Mode": "Modo Kiosco"
|
"Kiosk Mode": "Modo Kiosco",
|
||||||
|
"Reset password": "Restablecer contraseña"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nº",
|
"Sr.No": "Nº",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "desplegable",
|
"dropdown": "desplegable",
|
||||||
"radio button": "botón de radio",
|
"radio button": "botón de radio",
|
||||||
"image": "imagen",
|
"image": "imagen",
|
||||||
"email": "correo"
|
"email": "correo",
|
||||||
|
"number": "número"
|
||||||
},
|
},
|
||||||
"fields": "Campos",
|
"fields": "Campos",
|
||||||
"recipients": "Destinatarios",
|
"recipients": "Destinatarios",
|
||||||
@@ -443,6 +445,19 @@
|
|||||||
"maximum-check": "Chequeo máximo",
|
"maximum-check": "Chequeo máximo",
|
||||||
"cell-count": "recuento de células",
|
"cell-count": "recuento de células",
|
||||||
"default-value": "Valor por defecto",
|
"default-value": "Valor por defecto",
|
||||||
|
"formula": "Fórmula",
|
||||||
|
"formula-placeholder": "Crea una fórmula utilizando widgets numéricos",
|
||||||
|
"formula-no-widgets": "Todavía no hay otros widgets numéricos asignados a este firmante.",
|
||||||
|
"supported-operators": "Operadores compatibles:",
|
||||||
|
"formula-helper": "Haz clic en una ficha de widget numérico para insertarla (p. ej. {{number-1}} + {{number-2}}).",
|
||||||
|
"formula-error-letter": "Envuelve los nombres de los widgets entre dobles llaves (p. ej. {{number-1}}).",
|
||||||
|
"formula-error-characters": "Fuera de las llaves solo se permiten números y +, -, *, /, (, ).",
|
||||||
|
"formula-error-braces": "Cada {{ de apertura debe tener una }} correspondiente.",
|
||||||
|
"formula-error-operators": "Cuando uses operadores o paréntesis, agrega al menos dos valores.",
|
||||||
|
"formula-error-leading-operator": "Las fórmulas no pueden comenzar con operadores.",
|
||||||
|
"decimal-places": "Decimales",
|
||||||
|
"number-of-decimal-places": "Número de decimales",
|
||||||
|
"decimal-places-helper": "Establecer en 0 para números enteros",
|
||||||
"select": "Seleccionar",
|
"select": "Seleccionar",
|
||||||
"read-only": "Es de solo lectura",
|
"read-only": "Es de solo lectura",
|
||||||
"hide-labels": "Esconder etiquetas",
|
"hide-labels": "Esconder etiquetas",
|
||||||
@@ -1231,5 +1246,16 @@
|
|||||||
"save-as-template-msg": "Guarde este documento como una plantilla reutilizable que podrá usar nuevamente para futuros documentos.",
|
"save-as-template-msg": "Guarde este documento como una plantilla reutilizable que podrá usar nuevamente para futuros documentos.",
|
||||||
"public-credit-alert": "Firmar a través de este enlace consumirá sus créditos de correo electrónico gratuitos. Como usuario gratuito, se le asignan 15 créditos de correo electrónico por mes para evitar que los spammers abusen de nuestros sistemas. Para disfrutar de límites más altos y acceso ininterrumpido, suscríbase a los planes de pago de <1>OpenSign™.</1>",
|
"public-credit-alert": "Firmar a través de este enlace consumirá sus créditos de correo electrónico gratuitos. Como usuario gratuito, se le asignan 15 créditos de correo electrónico por mes para evitar que los spammers abusen de nuestros sistemas. Para disfrutar de límites más altos y acceso ininterrumpido, suscríbase a los planes de pago de <1>OpenSign™.</1>",
|
||||||
"know-more-about": "Saber más sobre",
|
"know-more-about": "Saber más sobre",
|
||||||
"free-unlimited-signatures": "¿Firmas ilimitadas gratis"
|
"free-unlimited-signatures": "¿Firmas ilimitadas gratis",
|
||||||
|
"email-quota-updated": "La cuota diaria de correos se actualizó correctamente.",
|
||||||
|
"password-has-been-reset": "La contraseña ha sido restablecida.",
|
||||||
|
"Unauthorized to reset your own password.": "No está autorizado para restablecer su propia contraseña.",
|
||||||
|
"User not found or not allowed.": "Usuario no encontrado o no autorizado.",
|
||||||
|
"User not found.": "Usuario no encontrado.",
|
||||||
|
"Admin user tenant not found.": "Inquilino de usuario administrador no encontrado.",
|
||||||
|
"User is not authenticated.": "El usuario no está autenticado.",
|
||||||
|
"Please provide required parameters.": "Por favor, proporcione los parámetros requeridos.",
|
||||||
|
"reset-password": "Restablecer contraseña de usuario",
|
||||||
|
"enter-strong-password": "Ingrese una contraseña segura o genere una automáticamente.",
|
||||||
|
"autogenerate": "Generar automáticamente"
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,8 @@
|
|||||||
"daily-mail-quota": "Quota d'e-mails quotidien",
|
"daily-mail-quota": "Quota d'e-mails quotidien",
|
||||||
"Save as template": "Enregistrer comme modèle",
|
"Save as template": "Enregistrer comme modèle",
|
||||||
"Fix & resend": "Corriger et renvoyer",
|
"Fix & resend": "Corriger et renvoyer",
|
||||||
"Kiosk Mode": "Mode Kiosque"
|
"Kiosk Mode": "Mode Kiosque",
|
||||||
|
"Reset password": "Réinitialiser le mot de passe"
|
||||||
},
|
},
|
||||||
"report-help": {
|
"report-help": {
|
||||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "dérouler",
|
"dropdown": "dérouler",
|
||||||
"radio button": "bouton radio",
|
"radio button": "bouton radio",
|
||||||
"image": "image",
|
"image": "image",
|
||||||
"email": "e-mail"
|
"email": "e-mail",
|
||||||
|
"number": "nombre"
|
||||||
},
|
},
|
||||||
"fields": "Des champs",
|
"fields": "Des champs",
|
||||||
"recipients": "Destinataires",
|
"recipients": "Destinataires",
|
||||||
@@ -426,7 +428,7 @@
|
|||||||
"initial-alert": "Mon initiale est introuvable",
|
"initial-alert": "Mon initiale est introuvable",
|
||||||
"copy-title": "Copier le widget vers",
|
"copy-title": "Copier le widget vers",
|
||||||
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
||||||
"reset-password": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
|
"reset-password-alert-1": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
|
||||||
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
|
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
|
||||||
"reset-password-alert-3": "Réinitialisez votre mot de passe",
|
"reset-password-alert-3": "Réinitialisez votre mot de passe",
|
||||||
"faild-animation": "Échec du chargement de l'animation",
|
"faild-animation": "Échec du chargement de l'animation",
|
||||||
@@ -442,6 +444,19 @@
|
|||||||
"maximum-check": "Contrôle maximum",
|
"maximum-check": "Contrôle maximum",
|
||||||
"cell-count": "numération cellulaire",
|
"cell-count": "numération cellulaire",
|
||||||
"default-value": "Valeur par défaut",
|
"default-value": "Valeur par défaut",
|
||||||
|
"formula": "Formule",
|
||||||
|
"formula-placeholder": "Créez une formule à l'aide des widgets numériques",
|
||||||
|
"formula-no-widgets": "Aucun autre widget numérique n'est encore attribué à ce signataire.",
|
||||||
|
"supported-operators": "Opérateurs pris en charge :",
|
||||||
|
"formula-helper": "Cliquez sur une puce de widget numérique pour l'insérer (ex. {{number-1}} + {{number-2}}).",
|
||||||
|
"formula-error-letter": "Encadrez les noms de widget avec des doubles accolades (ex. {{number-1}}).",
|
||||||
|
"formula-error-characters": "En dehors des accolades, seuls les chiffres et +, -, *, /, (, ) sont autorisés.",
|
||||||
|
"formula-error-braces": "Chaque {{ ouvrante doit avoir une }} correspondante.",
|
||||||
|
"formula-error-operators": "Lorsque vous utilisez des opérateurs ou des parenthèses, ajoutez au moins deux valeurs.",
|
||||||
|
"formula-error-leading-operator": "Les formules ne peuvent pas commencer par des opérateurs.",
|
||||||
|
"decimal-places": "Décimales",
|
||||||
|
"number-of-decimal-places": "Nombre de décimales",
|
||||||
|
"decimal-places-helper": "Définir sur 0 pour les nombres entiers",
|
||||||
"select": "Sélectionner",
|
"select": "Sélectionner",
|
||||||
"read-only": "Est en lecture seule",
|
"read-only": "Est en lecture seule",
|
||||||
"hide-labels": "Masquer les étiquettes",
|
"hide-labels": "Masquer les étiquettes",
|
||||||
@@ -1230,5 +1245,16 @@
|
|||||||
"save-as-template-msg": "Enregistrez ce document comme modèle réutilisable que vous pourrez utiliser à nouveau pour de futurs documents.",
|
"save-as-template-msg": "Enregistrez ce document comme modèle réutilisable que vous pourrez utiliser à nouveau pour de futurs documents.",
|
||||||
"public-credit-alert": "Signer via ce lien consomme vos crédits e-mail gratuits. En tant qu'utilisateur gratuit, vous disposez de 15 crédits e-mail par mois afin d’empêcher les spammeurs d’abuser de nos systèmes. Pour profiter de limites plus élevées et d'un accès ininterrompu, abonnez-vous aux forfaits payants de <1>OpenSign™.</1>",
|
"public-credit-alert": "Signer via ce lien consomme vos crédits e-mail gratuits. En tant qu'utilisateur gratuit, vous disposez de 15 crédits e-mail par mois afin d’empêcher les spammeurs d’abuser de nos systèmes. Pour profiter de limites plus élevées et d'un accès ininterrompu, abonnez-vous aux forfaits payants de <1>OpenSign™.</1>",
|
||||||
"know-more-about": "En savoir plus sur",
|
"know-more-about": "En savoir plus sur",
|
||||||
"free-unlimited-signatures": "Signatures illimitées gratuites"
|
"free-unlimited-signatures": "Signatures illimitées gratuites",
|
||||||
|
"email-quota-updated": "Quota quotidien d'e-mails mis à jour avec succès.",
|
||||||
|
"password-has-been-reset": "Le mot de passe a été réinitialisé.",
|
||||||
|
"Unauthorized to reset your own password.": "Non autorisé à réinitialiser votre propre mot de passe.",
|
||||||
|
"User not found or not allowed.": "Utilisateur introuvable ou non autorisé.",
|
||||||
|
"User not found.": "Utilisateur introuvable.",
|
||||||
|
"Admin user tenant not found.": "Locataire administrateur introuvable.",
|
||||||
|
"User is not authenticated.": "L'utilisateur n'est pas authentifié.",
|
||||||
|
"Please provide required parameters.": "Veuillez fournir les paramètres requis.",
|
||||||
|
"reset-password": "Réinitialiser le mot de passe utilisateur",
|
||||||
|
"enter-strong-password": "Entrez un mot de passe sécurisé ou générez-en un automatiquement.",
|
||||||
|
"autogenerate": "Générer automatiquement"
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,8 @@
|
|||||||
"daily-mail-quota": "दैनिक ईमेल कोटा",
|
"daily-mail-quota": "दैनिक ईमेल कोटा",
|
||||||
"Save as template": "टेम्पलेट के रूप में सहेजें",
|
"Save as template": "टेम्पलेट के रूप में सहेजें",
|
||||||
"Fix & resend": "ठीक करें और पुनः भेजें",
|
"Fix & resend": "ठीक करें और पुनः भेजें",
|
||||||
"Kiosk Mode": "कियोस्क मोड"
|
"Kiosk Mode": "कियोस्क मोड",
|
||||||
|
"Reset password": "पासवर्ड रीसेट करें"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "क्रमांक",
|
"Sr.No": "क्रमांक",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "ड्रॉपडाउन",
|
"dropdown": "ड्रॉपडाउन",
|
||||||
"radio button": "रेडियो बटन",
|
"radio button": "रेडियो बटन",
|
||||||
"image": "छवि",
|
"image": "छवि",
|
||||||
"email": "ईमेल"
|
"email": "ईमेल",
|
||||||
|
"number": "संख्या"
|
||||||
},
|
},
|
||||||
"fields": "क्षेत्र",
|
"fields": "क्षेत्र",
|
||||||
"recipients": "प्राप्तकर्ता",
|
"recipients": "प्राप्तकर्ता",
|
||||||
@@ -442,6 +444,19 @@
|
|||||||
"maximum-check": "अधिकतम जाँच",
|
"maximum-check": "अधिकतम जाँच",
|
||||||
"cell-count": "सेल्स संख्या",
|
"cell-count": "सेल्स संख्या",
|
||||||
"default-value": "डिफ़ॉल्ट मान",
|
"default-value": "डिफ़ॉल्ट मान",
|
||||||
|
"formula": "सूत्र",
|
||||||
|
"formula-placeholder": "संख्या विजेट्स का उपयोग करके एक सूत्र बनाएं",
|
||||||
|
"formula-no-widgets": "इस हस्ताक्षरकर्ता को अभी तक कोई अन्य संख्या विजेट असाइन नहीं किया गया है।",
|
||||||
|
"supported-operators": "समर्थित ऑपरेटर:",
|
||||||
|
"formula-helper": "सूची में से संख्या विजेट चिप पर क्लिक करके उसे जोड़ें (उदा. {{number-1}} + {{number-2}})।",
|
||||||
|
"formula-error-letter": "विजेट नामों को दोहरी घुंघराली ब्रैकेट में रखें (उदा. {{number-1}})।",
|
||||||
|
"formula-error-characters": "ब्रैकेट के बाहर केवल संख्याएँ और +, -, *, /, (, ) की अनुमति है।",
|
||||||
|
"formula-error-braces": "हर खुलने वाली {{ के लिए एक मिलान करने वाली }} होना चाहिए।",
|
||||||
|
"formula-error-operators": "ऑपरेटर या कोष्ठक का उपयोग करते समय कम से कम दो मान जोड़ें।",
|
||||||
|
"formula-error-leading-operator": "सूत्र ऑपरेटर से शुरू नहीं हो सकते।",
|
||||||
|
"decimal-places": "दशमलव स्थान",
|
||||||
|
"number-of-decimal-places": "दशमलव स्थानों की संख्या",
|
||||||
|
"decimal-places-helper": "पूर्णांक के लिए 0 पर सेट करें",
|
||||||
"select": "चुनें",
|
"select": "चुनें",
|
||||||
"read-only": "सिर्फ पढ़ने के लिए है",
|
"read-only": "सिर्फ पढ़ने के लिए है",
|
||||||
"hide-labels": "लेबल छिपाएँ",
|
"hide-labels": "लेबल छिपाएँ",
|
||||||
@@ -1230,5 +1245,16 @@
|
|||||||
"save-as-template-msg": "इस दस्तावेज़ को एक पुन: प्रयोज्य टेम्पलेट के रूप में सहेजें जिसे आप भविष्य के दस्तावेज़ों के लिए फिर से उपयोग कर सकते हैं।",
|
"save-as-template-msg": "इस दस्तावेज़ को एक पुन: प्रयोज्य टेम्पलेट के रूप में सहेजें जिसे आप भविष्य के दस्तावेज़ों के लिए फिर से उपयोग कर सकते हैं।",
|
||||||
"public-credit-alert": "इस लिंक के माध्यम से हस्ताक्षर करने पर आपके निःशुल्क ईमेल क्रेडिट का उपभोग होगा। एक निःशुल्क उपयोगकर्ता के रूप में, आपको स्पैमर्स को हमारी प्रणाली का दुरुपयोग करने से रोकने के लिए प्रति माह 15 ईमेल क्रेडिट आवंटित किए जाते हैं। उच्च सीमा और निर्बाध पहुँच का आनंद लेने के लिए, <1>OpenSign™ पेड प्लान</1> की सदस्यता लें।",
|
"public-credit-alert": "इस लिंक के माध्यम से हस्ताक्षर करने पर आपके निःशुल्क ईमेल क्रेडिट का उपभोग होगा। एक निःशुल्क उपयोगकर्ता के रूप में, आपको स्पैमर्स को हमारी प्रणाली का दुरुपयोग करने से रोकने के लिए प्रति माह 15 ईमेल क्रेडिट आवंटित किए जाते हैं। उच्च सीमा और निर्बाध पहुँच का आनंद लेने के लिए, <1>OpenSign™ पेड प्लान</1> की सदस्यता लें।",
|
||||||
"know-more-about": "इसके बारे में और जानें",
|
"know-more-about": "इसके बारे में और जानें",
|
||||||
"free-unlimited-signatures": "मुफ़्त असीमित हस्ताक्षर"
|
"free-unlimited-signatures": "मुफ़्त असीमित हस्ताक्षर",
|
||||||
|
"email-quota-updated": "दैनिक ईमेल कोटा सफलतापूर्वक अपडेट किया गया।",
|
||||||
|
"password-has-been-reset": "पासवर्ड रीसेट कर दिया गया है।",
|
||||||
|
"Unauthorized to reset your own password.": "अपना पासवर्ड रीसेट करने के लिए अधिकृत नहीं है।",
|
||||||
|
"User not found or not allowed.": "उपयोगकर्ता नहीं मिला या अनुमति नहीं है।",
|
||||||
|
"User not found.": "उपयोगकर्ता नहीं मिला।",
|
||||||
|
"Admin user tenant not found.": "एडमिन उपयोगकर्ता टेनेंट नहीं मिला।",
|
||||||
|
"User is not authenticated.": "उपयोगकर्ता प्रमाणीकृत नहीं है।",
|
||||||
|
"Please provide required parameters.": "कृपया आवश्यक पैरामीटर प्रदान करें।",
|
||||||
|
"reset-password": "उपयोगकर्ता पासवर्ड रीसेट करें",
|
||||||
|
"enter-strong-password": "एक मज़बूत पासवर्ड दर्ज करें या स्वचालित रूप से उत्पन्न करें।",
|
||||||
|
"autogenerate": "स्वतः उत्पन्न करें"
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,8 @@
|
|||||||
"daily-mail-quota": "Quota e-mail giornaliera",
|
"daily-mail-quota": "Quota e-mail giornaliera",
|
||||||
"Save as template": "Salva come modello",
|
"Save as template": "Salva come modello",
|
||||||
"Fix & resend": "Correggi e reinvia",
|
"Fix & resend": "Correggi e reinvia",
|
||||||
"Kiosk Mode": "Modalità Kiosk"
|
"Kiosk Mode": "Modalità Kiosk",
|
||||||
|
"Reset password": "Reimposta password"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nr.",
|
"Sr.No": "Nr.",
|
||||||
@@ -385,7 +386,8 @@
|
|||||||
"dropdown": "menu a tendina",
|
"dropdown": "menu a tendina",
|
||||||
"radio button": "pulsante di opzione",
|
"radio button": "pulsante di opzione",
|
||||||
"image": "immagine",
|
"image": "immagine",
|
||||||
"email": "email"
|
"email": "email",
|
||||||
|
"number": "numero"
|
||||||
},
|
},
|
||||||
"fields": "Campi",
|
"fields": "Campi",
|
||||||
"recipients": "Destinatari",
|
"recipients": "Destinatari",
|
||||||
@@ -442,6 +444,19 @@
|
|||||||
"maximum-check": "Controllo massimo",
|
"maximum-check": "Controllo massimo",
|
||||||
"cell-count": "conteggio delle cellule",
|
"cell-count": "conteggio delle cellule",
|
||||||
"default-value": "Valore predefinita",
|
"default-value": "Valore predefinita",
|
||||||
|
"formula": "Formula",
|
||||||
|
"formula-placeholder": "Crea una formula utilizzando i widget numerici",
|
||||||
|
"formula-no-widgets": "Non ci sono ancora altri widget numerici assegnati a questo firmatario.",
|
||||||
|
"supported-operators": "Operatori supportati:",
|
||||||
|
"formula-helper": "Fai clic su una chip di widget numerico per inserirla (es. {{number-1}} + {{number-2}}).",
|
||||||
|
"formula-error-letter": "Racchiudi i nomi dei widget tra doppie parentesi graffe (es. {{number-1}}).",
|
||||||
|
"formula-error-characters": "Al di fuori delle parentesi sono consentiti solo numeri e +, -, *, /, (, ).",
|
||||||
|
"formula-error-braces": "Ogni {{ di apertura deve avere una }} corrispondente.",
|
||||||
|
"formula-error-operators": "Quando usi operatori o parentesi, aggiungi almeno due valori.",
|
||||||
|
"formula-error-leading-operator": "Le formule non possono iniziare con operatori.",
|
||||||
|
"decimal-places": "Cifre decimali",
|
||||||
|
"number-of-decimal-places": "Numero di cifre decimali",
|
||||||
|
"decimal-places-helper": "Imposta su 0 per numeri interi",
|
||||||
"select": "Seleziona",
|
"select": "Seleziona",
|
||||||
"read-only": "È di sola lettura",
|
"read-only": "È di sola lettura",
|
||||||
"hide-labels": "Nascondi etichette",
|
"hide-labels": "Nascondi etichette",
|
||||||
@@ -1230,5 +1245,16 @@
|
|||||||
"save-as-template-msg": "Salva questo documento come modello riutilizzabile che potrai usare di nuovo per documenti futuri.",
|
"save-as-template-msg": "Salva questo documento come modello riutilizzabile che potrai usare di nuovo per documenti futuri.",
|
||||||
"public-credit-alert": "Firmare tramite questo link consumerà i tuoi crediti e-mail gratuiti. Come utente gratuito, ti vengono assegnati 15 crediti e-mail al mese per impedire agli spammer di abusare dei nostri sistemi. Per usufruire di limiti più elevati e accesso ininterrotto, abbonati ai piani a pagamento di <1>OpenSign™.</1>",
|
"public-credit-alert": "Firmare tramite questo link consumerà i tuoi crediti e-mail gratuiti. Come utente gratuito, ti vengono assegnati 15 crediti e-mail al mese per impedire agli spammer di abusare dei nostri sistemi. Per usufruire di limiti più elevati e accesso ininterrotto, abbonati ai piani a pagamento di <1>OpenSign™.</1>",
|
||||||
"know-more-about": "Scopri di più su",
|
"know-more-about": "Scopri di più su",
|
||||||
"free-unlimited-signatures": "Firme illimitate gratuite"
|
"free-unlimited-signatures": "Firme illimitate gratuite",
|
||||||
|
"email-quota-updated": "Quota e-mail giornaliera aggiornata con successo.",
|
||||||
|
"password-has-been-reset": "La password è stata reimpostata.",
|
||||||
|
"Unauthorized to reset your own password.": "Non sei autorizzato a reimpostare la tua password.",
|
||||||
|
"User not found or not allowed.": "Utente non trovato o non autorizzato.",
|
||||||
|
"User not found.": "Utente non trovato.",
|
||||||
|
"Admin user tenant not found.": "Tenant utente amministratore non trovato.",
|
||||||
|
"User is not authenticated.": "Utente non autenticato.",
|
||||||
|
"Please provide required parameters.": "Fornisci i parametri richiesti.",
|
||||||
|
"reset-password": "Reimposta password utente",
|
||||||
|
"enter-strong-password": "Inserisci una password sicura o generane una automaticamente.",
|
||||||
|
"autogenerate": "Genera automaticamente"
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,6 @@ import Loader from "./primitives/Loader";
|
|||||||
import UserList from "./pages/UserList";
|
import UserList from "./pages/UserList";
|
||||||
import { serverUrl_fn } from "./constant/appinfo";
|
import { serverUrl_fn } from "./constant/appinfo";
|
||||||
import DocSuccessPage from "./pages/DocSuccessPage";
|
import DocSuccessPage from "./pages/DocSuccessPage";
|
||||||
import ValidateSession from "./primitives/ValidateSession";
|
|
||||||
import DragProvider from "./components/DragProivder";
|
import DragProvider from "./components/DragProivder";
|
||||||
import Title from "./components/Title";
|
import Title from "./components/Title";
|
||||||
const DebugPdf = lazyWithRetry(() => import("./pages/DebugPdf"));
|
const DebugPdf = lazyWithRetry(() => import("./pages/DebugPdf"));
|
||||||
@@ -88,13 +87,7 @@ function App() {
|
|||||||
path="/forgetpassword"
|
path="/forgetpassword"
|
||||||
element={<Lazy Page={ForgetPassword} />}
|
element={<Lazy Page={ForgetPassword} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route element={<HomeLayout />}>
|
||||||
element={
|
|
||||||
<ValidateSession>
|
|
||||||
<HomeLayout />
|
|
||||||
</ValidateSession>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Route path="/users" element={<UserList />} />
|
<Route path="/users" element={<UserList />} />
|
||||||
<Route
|
<Route
|
||||||
path="/changepassword"
|
path="/changepassword"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { toggleSidebar } from "../redux/reducers/sidebarReducer.js";
|
import { toggleSidebar } from "../redux/reducers/sidebarReducer.js";
|
||||||
|
import { sessionStatus } from "../redux/reducers/userReducer.js";
|
||||||
|
|
||||||
const Header = ({ isConsole, setIsLoggingOut }) => {
|
const Header = ({ isConsole, setIsLoggingOut }) => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
@@ -67,6 +68,8 @@ const Header = ({ isConsole, setIsLoggingOut }) => {
|
|||||||
await Parse.User.logOut();
|
await Parse.User.logOut();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Err while logging out", err);
|
console.log("Err while logging out", err);
|
||||||
|
} finally {
|
||||||
|
dispatch(sessionStatus(true));
|
||||||
}
|
}
|
||||||
let appdata = localStorage.getItem("userSettings");
|
let appdata = localStorage.getItem("userSettings");
|
||||||
let applogo = localStorage.getItem("appLogo");
|
let applogo = localStorage.getItem("appLogo");
|
||||||
|
|||||||
@@ -71,11 +71,9 @@ function DriveBody(props) {
|
|||||||
});
|
});
|
||||||
props.setPdfData(updatedData);
|
props.setPdfData(updatedData);
|
||||||
props.sortingData(null, null, updatedData);
|
props.sortingData(null, null, updatedData);
|
||||||
await axios
|
try {
|
||||||
.put(
|
await axios.put(
|
||||||
`${localStorage.getItem(
|
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${docId}`,
|
||||||
"baseUrl"
|
|
||||||
)}classes/contracts_Document/${docId}`,
|
|
||||||
updateName,
|
updateName,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -84,18 +82,14 @@ function DriveBody(props) {
|
|||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
.then(() => {
|
} catch (err) {
|
||||||
// const res = result.data;
|
console.log("Err ", err);
|
||||||
// console.log("res", res);
|
props.setIsAlert({
|
||||||
})
|
isShow: true,
|
||||||
.catch((err) => {
|
alertMessage: t("something-went-wrong-mssg")
|
||||||
console.log("Err ", err);
|
|
||||||
props.setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: t("something-went-wrong-mssg")
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,7 +239,6 @@ function DriveBody(props) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
.then((Listdata) => {
|
.then((Listdata) => {
|
||||||
// console.log("Listdata ", Listdata);
|
|
||||||
const res = Listdata.data;
|
const res = Listdata.data;
|
||||||
if (res) {
|
if (res) {
|
||||||
const updatedData = props.pdfData.filter(
|
const updatedData = props.pdfData.filter(
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ function EmailComponent({
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
|
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
|
||||||
const sendmail = await Parse.Cloud.run("forwarddoc", params);
|
const sendmail = await Parse.Cloud.run("forwarddoc", params);
|
||||||
console.log("sendmail ", sendmail);
|
|
||||||
if (sendmail?.status === "success") {
|
if (sendmail?.status === "success") {
|
||||||
setSuccessEmail(true);
|
setSuccessEmail(true);
|
||||||
setIsEmail(false);
|
setIsEmail(false);
|
||||||
|
|||||||
@@ -267,7 +267,8 @@ function Placeholder(props) {
|
|||||||
(props.isSignYourself || props.isSelfSign)
|
(props.isSignYourself || props.isSelfSign)
|
||||||
) {
|
) {
|
||||||
props.handleTextSettingModal(true);
|
props.handleTextSettingModal(true);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
props?.handleNameModal && props?.handleNameModal(true);
|
props?.handleNameModal && props?.handleNameModal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,13 +306,7 @@ function Placeholder(props) {
|
|||||||
) {
|
) {
|
||||||
props.setUniqueId(props?.data?.Id);
|
props.setUniqueId(props?.data?.Id);
|
||||||
}
|
}
|
||||||
|
if (props?.data?.Role !== "prefill") {
|
||||||
//checking widget's type and open widget copy modal for required widgets
|
|
||||||
if (
|
|
||||||
["signature", textInputWidget, textWidget, "stamp", "initials"].includes(
|
|
||||||
props.pos.type
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
props.setIsPageCopy(true);
|
props.setIsPageCopy(true);
|
||||||
props.setCurrWidgetsDetails(props.pos);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
} else {
|
} else {
|
||||||
@@ -326,7 +321,7 @@ function Placeholder(props) {
|
|||||||
props.data && props.data?.Id
|
props.data && props.data?.Id
|
||||||
);
|
);
|
||||||
//condiiton is used to store copied prefill image base64 url in redux for display image
|
//condiiton is used to store copied prefill image base64 url in redux for display image
|
||||||
if (props?.data?.Role === "prefill") {
|
if (props?.pos?.type === "image") {
|
||||||
const getPrefillImg = prefillImg?.find((x) => x.id === props.pos.key);
|
const getPrefillImg = prefillImg?.find((x) => x.id === props.pos.key);
|
||||||
dispatch(
|
dispatch(
|
||||||
setPrefillImg({
|
setPrefillImg({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, forwardRef } from "react";
|
import { useEffect, useMemo, useState, forwardRef } from "react";
|
||||||
import {
|
import {
|
||||||
getMonth,
|
getMonth,
|
||||||
getYear,
|
getYear,
|
||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
years,
|
years,
|
||||||
selectCheckbox,
|
selectCheckbox,
|
||||||
checkRegularExpress,
|
checkRegularExpress,
|
||||||
isBase64
|
isBase64,
|
||||||
|
getSignerPages
|
||||||
} from "../../constant/Utils";
|
} from "../../constant/Utils";
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
@@ -19,6 +20,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import CellsWidget from "./CellsWidget";
|
import CellsWidget from "./CellsWidget";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import Loader from "../../primitives/Loader";
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
const textWidgetCls =
|
const textWidgetCls =
|
||||||
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap";
|
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap";
|
||||||
const widgetCls =
|
const widgetCls =
|
||||||
@@ -65,18 +67,21 @@ function PlaceholderType(props) {
|
|||||||
props?.pos?.options?.defaultValue ||
|
props?.pos?.options?.defaultValue ||
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// keep displayed value in sync with the stored response
|
// keep displayed value in sync with the stored response
|
||||||
setwidgetValue(widgetData);
|
setwidgetValue(widgetData);
|
||||||
}
|
}
|
||||||
if (props.pos?.options?.hint) {
|
if (props.pos?.options?.hint) {
|
||||||
setHint(props.pos?.options.hint);
|
setHint(props.pos?.options.hint);
|
||||||
} else if (props.pos?.options?.validation?.type) {
|
} else if (props.pos?.options?.validation?.type) {
|
||||||
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.pos]);
|
}, [props.pos, widgetData, type]);
|
||||||
|
|
||||||
|
|
||||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import {
|
|||||||
} from "../../redux/reducers/widgetSlice";
|
} from "../../redux/reducers/widgetSlice";
|
||||||
import * as utils from "../../utils";
|
import * as utils from "../../utils";
|
||||||
|
|
||||||
|
const widgetTitle = "font-medium";
|
||||||
|
|
||||||
const ShowTextWidget = ({ position, handleWidgetDetails }) => {
|
const ShowTextWidget = ({ position, handleWidgetDetails }) => {
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [inputValue, setInputValue] = useState(position.options.response || "");
|
const [inputValue, setInputValue] = useState(position.options.response || "");
|
||||||
@@ -46,13 +48,77 @@ const ShowTextWidget = ({ position, handleWidgetDetails }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ImageComponent = (props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||||
|
const imageRefs = useRef({});
|
||||||
|
|
||||||
|
let imgUrl = "";
|
||||||
|
const isBase64Url = isBase64(props?.position?.SignUrl);
|
||||||
|
if (isBase64Url) {
|
||||||
|
imgUrl = props?.position?.SignUrl;
|
||||||
|
} else {
|
||||||
|
const getPrefillImg = prefillImg?.find(
|
||||||
|
(x) => x.id === props?.position?.key
|
||||||
|
);
|
||||||
|
imgUrl = getPrefillImg?.base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className={widgetTitle}>{props?.position.options?.name}</span>
|
||||||
|
{imgUrl ? (
|
||||||
|
<>
|
||||||
|
<div className="cursor-pointer op-card border-[1px] border-gray-400 flex flex-col w-full h-full justify-center items-center ">
|
||||||
|
<img
|
||||||
|
alt="print img"
|
||||||
|
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamicallys
|
||||||
|
src={imgUrl}
|
||||||
|
draggable="false"
|
||||||
|
className="object-contain h-full w-full aspect-[5/2]"
|
||||||
|
onLoad={() => props?.handleImageLoaded?.(props?.position.key)}
|
||||||
|
onError={() => props?.handleImageLoaded?.(props?.position.key)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
onClick={() => props?.handleClearImage(props?.position)}
|
||||||
|
className="flex justify-start text-blue-500 underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{t("clear")}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="cursor-pointer op-card border-[1px] op-border-hover flex flex-col overflow-hidden w-full h-full aspect-[5/2] justify-center items-center"
|
||||||
|
onClick={() => imageRefs.current[props?.position.key]?.click()}
|
||||||
|
>
|
||||||
|
{props?.imageLoaders[props?.position?.key] && (
|
||||||
|
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-white/30 z-50">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={(e) => props?.onImageChange?.(e, props?.position)}
|
||||||
|
className="filetype"
|
||||||
|
accept="image/png,image/jpeg"
|
||||||
|
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamically
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
<i className="fa-light text-base-content fa-cloud-upload-alt text-[25px]"></i>
|
||||||
|
<div className="text-[10px] text-base-content">{t("upload")}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
function PrefillWidgetModal(props) {
|
function PrefillWidgetModal(props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
// Track already loaded image keys so they don't increment multiple times
|
// Track already loaded image keys so they don't increment multiple times
|
||||||
const loadedSet = useRef(new Set());
|
const loadedSet = useRef(new Set());
|
||||||
const initializedRef = useRef(false); // prevent rerun on state updates
|
const initializedRef = useRef(false); // prevent rerun on state updates
|
||||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
|
||||||
const [image, setImage] = useState(null);
|
const [image, setImage] = useState(null);
|
||||||
const [imageLoaders, setImageLoaders] = useState({});
|
const [imageLoaders, setImageLoaders] = useState({});
|
||||||
const [currentWidget, setCurrentWidget] = useState("");
|
const [currentWidget, setCurrentWidget] = useState("");
|
||||||
@@ -61,7 +127,6 @@ function PrefillWidgetModal(props) {
|
|||||||
const [loadedImages, setLoadedImages] = useState(0);
|
const [loadedImages, setLoadedImages] = useState(0);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const years = range(1950, getYear(new Date()) + 16, 1);
|
const years = range(1950, getYear(new Date()) + 16, 1);
|
||||||
const widgetTitle = "font-medium";
|
|
||||||
const isAnyLoaderActive = Object.values(imageLoaders).some(
|
const isAnyLoaderActive = Object.values(imageLoaders).some(
|
||||||
(val) => val === true
|
(val) => val === true
|
||||||
);
|
);
|
||||||
@@ -93,6 +158,7 @@ function PrefillWidgetModal(props) {
|
|||||||
|
|
||||||
return flatArray || [];
|
return flatArray || [];
|
||||||
}, [props.prefillData]);
|
}, [props.prefillData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(resetWidgetState([]));
|
dispatch(resetWidgetState([]));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -163,67 +229,7 @@ function PrefillWidgetModal(props) {
|
|||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
};
|
};
|
||||||
const ImageComponent = (props) => {
|
|
||||||
const imageRefs = useRef([]);
|
|
||||||
let imgUrl = "";
|
|
||||||
const isBase64Url = isBase64(props?.position?.SignUrl);
|
|
||||||
if (isBase64Url) {
|
|
||||||
imgUrl = props?.position?.SignUrl;
|
|
||||||
} else {
|
|
||||||
const getPrefillImg = prefillImg?.find(
|
|
||||||
(x) => x.id === props?.position?.key
|
|
||||||
);
|
|
||||||
imgUrl = getPrefillImg?.base64;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span className={widgetTitle}>{props?.position.options?.name}</span>
|
|
||||||
{imgUrl ? (
|
|
||||||
<>
|
|
||||||
<div className="cursor-pointer op-card border-[1px] border-gray-400 flex flex-col w-full h-full justify-center items-center ">
|
|
||||||
<img
|
|
||||||
alt="print img"
|
|
||||||
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamicallys
|
|
||||||
src={imgUrl}
|
|
||||||
draggable="false"
|
|
||||||
className="object-contain h-full w-full aspect-[5/2]"
|
|
||||||
onLoad={() => handleImageLoaded?.(props?.position.key)}
|
|
||||||
onError={() => handleImageLoaded?.(props?.position.key)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
onClick={() => handleClearImage(props?.position)}
|
|
||||||
className="flex justify-start text-blue-500 underline cursor-pointer"
|
|
||||||
>
|
|
||||||
{t("clear")}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className="cursor-pointer op-card border-[1px] op-border-hover flex flex-col overflow-hidden w-full h-full aspect-[5/2] justify-center items-center"
|
|
||||||
onClick={() => imageRefs.current[props?.position.key]?.click()}
|
|
||||||
>
|
|
||||||
{imageLoaders[props?.position?.key] && (
|
|
||||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-white/30 z-50">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
onChange={(e) => onImageChange(e, props?.position)}
|
|
||||||
className="filetype"
|
|
||||||
accept="image/png,image/jpeg"
|
|
||||||
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamically
|
|
||||||
hidden
|
|
||||||
/>
|
|
||||||
<i className="fa-light text-base-content fa-cloud-upload-alt text-[25px]"></i>
|
|
||||||
<div className="text-[10px] text-base-content">{t("upload")}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||||
<div
|
<div
|
||||||
style={{ fontFamily: "Arial, sans-serif" }}
|
style={{ fontFamily: "Arial, sans-serif" }}
|
||||||
@@ -273,7 +279,6 @@ function PrefillWidgetModal(props) {
|
|||||||
image.src,
|
image.src,
|
||||||
image.imgType
|
image.imgType
|
||||||
);
|
);
|
||||||
setImageLoaders({});
|
|
||||||
if (imageUrl) {
|
if (imageUrl) {
|
||||||
return imageUrl;
|
return imageUrl;
|
||||||
}
|
}
|
||||||
@@ -414,6 +419,7 @@ function PrefillWidgetModal(props) {
|
|||||||
// (tracks how many images have finished loading)
|
// (tracks how many images have finished loading)
|
||||||
setLoadedImages((prev) => prev + 1);
|
setLoadedImages((prev) => prev + 1);
|
||||||
}
|
}
|
||||||
|
setImageLoaders({});
|
||||||
};
|
};
|
||||||
const handleWidgetType = (position) => {
|
const handleWidgetType = (position) => {
|
||||||
switch (position?.type) {
|
switch (position?.type) {
|
||||||
@@ -432,9 +438,9 @@ function PrefillWidgetModal(props) {
|
|||||||
className="mt-[2px] op-checkbox op-checkbox-xs"
|
className="mt-[2px] op-checkbox op-checkbox-xs"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selectCheckbox(ind, position)}
|
checked={selectCheckbox(ind, position)}
|
||||||
onChange={(e) => {
|
onChange={(e) =>
|
||||||
handleCheckboxValue(e.target.checked, ind, position);
|
handleCheckboxValue(e.target.checked, ind, position)
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`checkbox-${position.key + ind}`}
|
htmlFor={`checkbox-${position.key + ind}`}
|
||||||
@@ -467,9 +473,7 @@ function PrefillWidgetModal(props) {
|
|||||||
value={
|
value={
|
||||||
position?.options?.response || position?.options?.defaultValue
|
position?.options?.response || position?.options?.defaultValue
|
||||||
}
|
}
|
||||||
onChange={(e) => {
|
onChange={(e) => handleWidgetDetails(position, e.target.value)}
|
||||||
handleWidgetDetails(position, e.target.value);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Default/Title option */}
|
{/* Default/Title option */}
|
||||||
<option value="" disabled hidden>
|
<option value="" disabled hidden>
|
||||||
@@ -519,9 +523,7 @@ function PrefillWidgetModal(props) {
|
|||||||
)}
|
)}
|
||||||
closeOnScroll={true}
|
closeOnScroll={true}
|
||||||
selected={handleDate(position)}
|
selected={handleDate(position)}
|
||||||
onChange={(date) => {
|
onChange={(date) => handleOnDateChange(date, position)}
|
||||||
handleOnDateChange(date, position);
|
|
||||||
}}
|
|
||||||
customInput={<ExampleCustomInput />}
|
customInput={<ExampleCustomInput />}
|
||||||
dateFormat={position?.options?.validation?.format || "MM/dd/yyyy"}
|
dateFormat={position?.options?.validation?.format || "MM/dd/yyyy"}
|
||||||
/>
|
/>
|
||||||
@@ -531,7 +533,10 @@ function PrefillWidgetModal(props) {
|
|||||||
return (
|
return (
|
||||||
<ImageComponent
|
<ImageComponent
|
||||||
position={position}
|
position={position}
|
||||||
docId={props?.docId}
|
imageLoaders={imageLoaders}
|
||||||
|
onImageChange={onImageChange}
|
||||||
|
handleImageLoaded={handleImageLoaded}
|
||||||
|
handleClearImage={handleClearImage}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case radioButtonWidget:
|
case radioButtonWidget:
|
||||||
@@ -549,9 +554,7 @@ function PrefillWidgetModal(props) {
|
|||||||
className="mt-[2px] op-radio op-radio-xs"
|
className="mt-[2px] op-radio op-radio-xs"
|
||||||
type="radio"
|
type="radio"
|
||||||
checked={handleRadioCheck(data, position)}
|
checked={handleRadioCheck(data, position)}
|
||||||
onChange={() => {
|
onChange={() => handleWidgetDetails(position, data)}
|
||||||
handleWidgetDetails(position, data);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`radio-${position.key + ind}`}
|
htmlFor={`radio-${position.key + ind}`}
|
||||||
@@ -697,9 +700,9 @@ function PrefillWidgetModal(props) {
|
|||||||
multiValueLabel: () => "mb-[2px]",
|
multiValueLabel: () => "mb-[2px]",
|
||||||
menu: () =>
|
menu: () =>
|
||||||
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content absolute z-9999",
|
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content absolute z-9999",
|
||||||
menuList: () => "shadow-md rounded-lg ",
|
menuList: () => "shadow-md rounded-lg",
|
||||||
option: () =>
|
option: () =>
|
||||||
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2 ",
|
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
|
||||||
noOptionsMessage: () =>
|
noOptionsMessage: () =>
|
||||||
"p-2 bg-base-200 rounded-lg m-1 p-2"
|
"p-2 bg-base-200 rounded-lg m-1 p-2"
|
||||||
}}
|
}}
|
||||||
@@ -710,7 +713,7 @@ function PrefillWidgetModal(props) {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => handleCreateNew(e, field.value)}
|
onClick={(e) => handleCreateNew(e, field.value)}
|
||||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm "
|
className="op-btn op-btn-accent op-btn-outline op-btn-sm"
|
||||||
>
|
>
|
||||||
<i className="fa-light fa-plus"></i>
|
<i className="fa-light fa-plus"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ function RenderPdf(props) {
|
|||||||
setIsReqSignTourDisabled={props.setIsReqSignTourDisabled}
|
setIsReqSignTourDisabled={props.setIsReqSignTourDisabled}
|
||||||
calculateFontsize={calculateFontsize}
|
calculateFontsize={calculateFontsize}
|
||||||
currWidgetsDetails={props?.currWidgetsDetails}
|
currWidgetsDetails={props?.currWidgetsDetails}
|
||||||
|
handleNameModal={props?.handleNameModal}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
@@ -484,6 +485,8 @@ function RenderPdf(props) {
|
|||||||
setIsReqSignTourDisabled={
|
setIsReqSignTourDisabled={
|
||||||
props.setIsReqSignTourDisabled
|
props.setIsReqSignTourDisabled
|
||||||
}
|
}
|
||||||
|
unSignedWidgetId={props.unSignedWidgetId}
|
||||||
|
handleNameModal={props.handleNameModal}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -91,14 +91,14 @@ function WidgetComponent(props) {
|
|||||||
name,
|
name,
|
||||||
jobTitle,
|
jobTitle,
|
||||||
company,
|
company,
|
||||||
|
email,
|
||||||
date,
|
date,
|
||||||
text,
|
text,
|
||||||
cells,
|
cells,
|
||||||
checkbox,
|
checkbox,
|
||||||
dropdown,
|
dropdown,
|
||||||
radioButton,
|
radioButton,
|
||||||
image,
|
image
|
||||||
email
|
|
||||||
];
|
];
|
||||||
const getWidgetArray = widgets;
|
const getWidgetArray = widgets;
|
||||||
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
||||||
@@ -109,7 +109,7 @@ function WidgetComponent(props) {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
//allow only (signature,stamp,initials,text,name, job title, company,email) widget when isAllowModification true and user have session token
|
// allow only (signature, stamp, initials, text, name, job title, company, email, cells) widget when isAllowModification true and user have session token
|
||||||
const modifiedWidgets = widget.filter(
|
const modifiedWidgets = widget.filter(
|
||||||
(data) =>
|
(data) =>
|
||||||
![
|
![
|
||||||
@@ -121,7 +121,7 @@ function WidgetComponent(props) {
|
|||||||
"checkbox"
|
"checkbox"
|
||||||
].includes(data.type)
|
].includes(data.type)
|
||||||
);
|
);
|
||||||
//allow only (signature,stamp,initials,text) widget when isAllowModification true and user does not have session token
|
// allow only (signature, stamp, initials, text, cells) widget when isAllowModification true and user does not have session token
|
||||||
const unlogedInUserWidgets = widget.filter(
|
const unlogedInUserWidgets = widget.filter(
|
||||||
(data) =>
|
(data) =>
|
||||||
![
|
![
|
||||||
@@ -263,7 +263,12 @@ function WidgetComponent(props) {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-[15px] flex flex-col pt-4" data-tut="addWidgets">
|
<div
|
||||||
|
className="p-[12px] grid lg:grid-cols-2 gap-x-2 lg:gap-y-1.5 pt-3"
|
||||||
|
data-tut="addWidgets"
|
||||||
|
role="list"
|
||||||
|
aria-label="Add widgets"
|
||||||
|
>
|
||||||
<WidgetList
|
<WidgetList
|
||||||
updateWidgets={handleWidgetType}
|
updateWidgets={handleWidgetType}
|
||||||
handleDivClick={props.handleDivClick}
|
handleDivClick={props.handleDivClick}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import "../../styles/AddUser.css";
|
import "../../styles/AddUser.css";
|
||||||
import RegexParser from "regex-parser";
|
import RegexParser from "regex-parser";
|
||||||
@@ -21,11 +21,30 @@ const WidgetNameModal = (props) => {
|
|||||||
hint: "",
|
hint: "",
|
||||||
textvalidate: "",
|
textvalidate: "",
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
cellCount: 5
|
cellCount: 5,
|
||||||
});
|
});
|
||||||
const [isValid, setIsValid] = useState(true);
|
const [isValid, setIsValid] = useState(true);
|
||||||
const statusArr = ["Required", "Optional"];
|
const statusArr = ["Required", "Optional"];
|
||||||
const [signatureType, setSignatureType] = useState([]);
|
const [signatureType, setSignatureType] = useState([]);
|
||||||
|
const type = props?.defaultdata?.type;
|
||||||
|
const isCellWidget = useMemo(() => type === cellsWidget, [type]);
|
||||||
|
const isSignOrInitials = useMemo(
|
||||||
|
() => ["signature", "initials"].includes(type),
|
||||||
|
[type]
|
||||||
|
);
|
||||||
|
const showFontControls = useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
textInputWidget,
|
||||||
|
textWidget,
|
||||||
|
cellsWidget,
|
||||||
|
"name",
|
||||||
|
"company",
|
||||||
|
"job title",
|
||||||
|
"email"
|
||||||
|
].includes(props.defaultdata?.type),
|
||||||
|
[type]
|
||||||
|
);
|
||||||
|
|
||||||
const handleHint = () => {
|
const handleHint = () => {
|
||||||
const type = props.defaultdata?.type;
|
const type = props.defaultdata?.type;
|
||||||
@@ -54,13 +73,13 @@ const WidgetNameModal = (props) => {
|
|||||||
? props.defaultdata?.options?.validation?.pattern
|
? props.defaultdata?.options?.validation?.pattern
|
||||||
: props.defaultdata?.options?.validation?.type || "",
|
: props.defaultdata?.options?.validation?.type || "",
|
||||||
isReadOnly: props.defaultdata?.options?.isReadOnly || false,
|
isReadOnly: props.defaultdata?.options?.isReadOnly || false,
|
||||||
cellCount: props.defaultdata?.options?.cellCount || 5
|
cellCount: props.defaultdata?.options?.cellCount || 5,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setFormdata({
|
setFormdata({
|
||||||
...formdata,
|
...formdata,
|
||||||
name: props.defaultdata?.options?.name || "",
|
name: props.defaultdata?.options?.name || "",
|
||||||
cellCount: props.defaultdata?.options?.cellCount || 5
|
cellCount: props.defaultdata?.options?.cellCount || 5,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,12 +87,13 @@ const WidgetNameModal = (props) => {
|
|||||||
const defaultSignatureType = signTypes || [];
|
const defaultSignatureType = signTypes || [];
|
||||||
setSignatureType(defaultSignatureType);
|
setSignatureType(defaultSignatureType);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.defaultdata]);
|
}, [props.defaultdata]);
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (props.handleData) {
|
if (props.handleData) {
|
||||||
if (["signature", "initials"].includes(props.defaultdata?.type)) {
|
if (isSignOrInitials) {
|
||||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||||
const isDefaultSignTypeOnly =
|
const isDefaultSignTypeOnly =
|
||||||
enabledSignTypes?.length === 1 &&
|
enabledSignTypes?.length === 1 &&
|
||||||
@@ -87,9 +107,11 @@ const WidgetNameModal = (props) => {
|
|||||||
props.handleData(data, props.defaultdata?.type);
|
props.handleData(data, props.defaultdata?.type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const isTextInput = [textInputWidget, cellsWidget].includes(
|
|
||||||
props.defaultdata?.type
|
const isTextInput = [
|
||||||
);
|
textInputWidget,
|
||||||
|
cellsWidget,
|
||||||
|
].includes(props.defaultdata?.type);
|
||||||
const { isReadOnly, defaultValue, status } = formdata;
|
const { isReadOnly, defaultValue, status } = formdata;
|
||||||
// If it’s a text‐input widget, enforce that read-only fields have
|
// If it’s a text‐input widget, enforce that read-only fields have
|
||||||
// either a defaultValue or an "optional" status.
|
// either a defaultValue or an "optional" status.
|
||||||
@@ -110,7 +132,7 @@ const WidgetNameModal = (props) => {
|
|||||||
status: "required",
|
status: "required",
|
||||||
hint: "",
|
hint: "",
|
||||||
textvalidate: "",
|
textvalidate: "",
|
||||||
cellCount: 5
|
cellCount: 5,
|
||||||
});
|
});
|
||||||
setSignatureType(signTypes);
|
setSignatureType(signTypes);
|
||||||
}
|
}
|
||||||
@@ -148,10 +170,11 @@ const WidgetNameModal = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
setIsValid(true);
|
setIsValid(true);
|
||||||
}
|
}
|
||||||
const val =
|
|
||||||
props.defaultdata?.type === cellsWidget
|
const val = isCellWidget
|
||||||
? e.target.value.slice(0, formdata.cellCount)
|
? e.target.value.slice(0, formdata.cellCount)
|
||||||
: e.target.value;
|
: e.target.value;
|
||||||
|
|
||||||
setFormdata({ ...formdata, [e.target.name]: val });
|
setFormdata({ ...formdata, [e.target.name]: val });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -160,7 +183,7 @@ const WidgetNameModal = (props) => {
|
|||||||
case "email":
|
case "email":
|
||||||
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||||
case "number":
|
case "number":
|
||||||
return "/^\\d+$/";
|
return "^\\d+(?:\\.\\d+)?$"; // "/^\\d+$/";
|
||||||
case "text":
|
case "text":
|
||||||
//allow space in text regex
|
//allow space in text regex
|
||||||
return "/^[a-zA-Z ]+$/";
|
return "/^[a-zA-Z ]+$/";
|
||||||
@@ -184,23 +207,19 @@ const WidgetNameModal = (props) => {
|
|||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={props.isOpen}
|
isOpen={props.isOpen}
|
||||||
handleClose={props.handleClose && props.handleClose}
|
handleClose={props.handleClose && props.handleClose}
|
||||||
title={
|
title={isSignOrInitials ? t("signature-setting") : t("widget-info")}
|
||||||
["signature", "initials"].includes(props.defaultdata?.type)
|
|
||||||
? t("signature-setting")
|
|
||||||
: t("widget-info")
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className={`${
|
className={`${
|
||||||
[textInputWidget, cellsWidget].includes(props.defaultdata?.type)
|
[textInputWidget, cellsWidget].includes(props.defaultdata?.type)
|
||||||
? "pt-0"
|
? "pt-0"
|
||||||
: ["signature", "initials"].includes(props.defaultdata?.type)
|
: isSignOrInitials
|
||||||
? "pt-2"
|
? "pt-2"
|
||||||
: ""
|
: ""
|
||||||
} p-[20px] text-base-content`}
|
} p-[20px] text-base-content`}
|
||||||
>
|
>
|
||||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
{!isSignOrInitials && (
|
||||||
<div className="mb-[0.75rem] text-[13px]">
|
<div className="mb-[0.75rem] text-[13px]">
|
||||||
<label htmlFor="name">
|
<label htmlFor="name">
|
||||||
{t("name")}
|
{t("name")}
|
||||||
@@ -217,7 +236,7 @@ const WidgetNameModal = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props.defaultdata?.type === cellsWidget && (
|
{isCellWidget && (
|
||||||
<div className="mb-[0.75rem] text-[13px]">
|
<div className="mb-[0.75rem] text-[13px]">
|
||||||
<label htmlFor="cellCount">{t("cell-count")}</label>
|
<label htmlFor="cellCount">{t("cell-count")}</label>
|
||||||
<input
|
<input
|
||||||
@@ -231,7 +250,10 @@ const WidgetNameModal = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{[textInputWidget, cellsWidget].includes(props.defaultdata?.type) &&
|
{[
|
||||||
|
textInputWidget,
|
||||||
|
cellsWidget,
|
||||||
|
].includes(props.defaultdata?.type) &&
|
||||||
props?.roleName !== "prefill" && (
|
props?.roleName !== "prefill" && (
|
||||||
<>
|
<>
|
||||||
<div className="mb-[0.75rem]">
|
<div className="mb-[0.75rem]">
|
||||||
@@ -244,11 +266,7 @@ const WidgetNameModal = (props) => {
|
|||||||
value={formdata.defaultValue}
|
value={formdata.defaultValue}
|
||||||
onChange={(e) => handledefaultChange(e)}
|
onChange={(e) => handledefaultChange(e)}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
maxLength={
|
maxLength={isCellWidget ? formdata.cellCount : undefined}
|
||||||
props.defaultdata?.type === cellsWidget
|
|
||||||
? formdata.cellCount
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
if (isValid === false) {
|
if (isValid === false) {
|
||||||
setFormdata({ ...formdata, defaultValue: "" });
|
setFormdata({ ...formdata, defaultValue: "" });
|
||||||
@@ -262,7 +280,7 @@ const WidgetNameModal = (props) => {
|
|||||||
style={{ fontSize: 12 }}
|
style={{ fontSize: 12 }}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className="fa-light fa-exclamation-circle text-[15px]"
|
className="fa-light fa-exclamation-circle text-[15px] mr-1"
|
||||||
style={{ color: "#fab005" }}
|
style={{ color: "#fab005" }}
|
||||||
></i>
|
></i>
|
||||||
{t("invalid-default-value")}
|
{t("invalid-default-value")}
|
||||||
@@ -271,7 +289,8 @@ const WidgetNameModal = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!["signature", "initials"].includes(props.defaultdata?.type) &&
|
{!props?.isSelfSign &&
|
||||||
|
!isSignOrInitials &&
|
||||||
props?.roleName !== "prefill" && (
|
props?.roleName !== "prefill" && (
|
||||||
<div className="mb-[0.75rem]">
|
<div className="mb-[0.75rem]">
|
||||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||||
@@ -302,9 +321,10 @@ const WidgetNameModal = (props) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{[textInputWidget, cellsWidget].includes(
|
{[
|
||||||
props.defaultdata?.type
|
textInputWidget,
|
||||||
) &&
|
cellsWidget,
|
||||||
|
].includes(props.defaultdata?.type) &&
|
||||||
props?.roleName !== "prefill" && (
|
props?.roleName !== "prefill" && (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
@@ -330,7 +350,7 @@ const WidgetNameModal = (props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{["signature", "initials"].includes(props.defaultdata?.type) && (
|
{isSignOrInitials && (
|
||||||
<div className="mb-[0.75rem]">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
||||||
{t("allowed-signature-types")}
|
{t("allowed-signature-types")}
|
||||||
@@ -358,7 +378,7 @@ const WidgetNameModal = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props?.roleName !== "prefill" && (
|
{!props?.isSelfSign && props?.roleName !== "prefill" && (
|
||||||
<div className="mb-[0.75rem]">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="hint" className="text-[13px]">
|
<label htmlFor="hint" className="text-[13px]">
|
||||||
{t("hint")}
|
{t("hint")}
|
||||||
@@ -372,15 +392,7 @@ const WidgetNameModal = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{[
|
{showFontControls && (
|
||||||
textInputWidget,
|
|
||||||
textWidget,
|
|
||||||
cellsWidget,
|
|
||||||
"name",
|
|
||||||
"company",
|
|
||||||
"job title",
|
|
||||||
"email"
|
|
||||||
].includes(props.defaultdata?.type) && (
|
|
||||||
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
|
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
|
||||||
<div className="flex items-center gap-2 ">
|
<div className="flex items-center gap-2 ">
|
||||||
<span className="whitespace-nowrap">{t("font-size")}: </span>
|
<span className="whitespace-nowrap">{t("font-size")}: </span>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { forwardRef, useEffect, useRef, useState } from "react";
|
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { removeBackground } from "@imgly/background-removal";
|
import { removeBackground } from "@imgly/background-removal";
|
||||||
@@ -23,7 +23,8 @@ import {
|
|||||||
convertBase64ToFile,
|
convertBase64ToFile,
|
||||||
generatePdfName,
|
generatePdfName,
|
||||||
getDefaultDate,
|
getDefaultDate,
|
||||||
getDefaultFormat
|
getDefaultFormat,
|
||||||
|
getSignerPages
|
||||||
} from "../../constant/Utils";
|
} from "../../constant/Utils";
|
||||||
import CellsWidget from "./CellsWidget";
|
import CellsWidget from "./CellsWidget";
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
@@ -45,7 +46,9 @@ import { useDispatch, useSelector } from "react-redux";
|
|||||||
import Loader from "../../primitives/Loader";
|
import Loader from "../../primitives/Loader";
|
||||||
import { emailRegex } from "../../constant/const";
|
import { emailRegex } from "../../constant/const";
|
||||||
import RegexParser from "regex-parser";
|
import RegexParser from "regex-parser";
|
||||||
import { saveToMySign } from "../../utils/widgetUtils";
|
import {
|
||||||
|
saveToMySign
|
||||||
|
} from "../../utils/widgetUtils";
|
||||||
|
|
||||||
const fontOptions = [
|
const fontOptions = [
|
||||||
{ value: "Fasthand" },
|
{ value: "Fasthand" },
|
||||||
@@ -111,12 +114,28 @@ function WidgetsValueModal(props) {
|
|||||||
props?.kiosk_signer || JSON.parse(localStorage.getItem("kiosk_signer"));
|
props?.kiosk_signer || JSON.parse(localStorage.getItem("kiosk_signer"));
|
||||||
const jsonSender = senderUser && JSON.parse(senderUser);
|
const jsonSender = senderUser && JSON.parse(senderUser);
|
||||||
const currentUserName = jsonSender && jsonSender?.name;
|
const currentUserName = jsonSender && jsonSender?.name;
|
||||||
|
const type = currWidgetsDetails?.type;
|
||||||
const widgetTypeTranslation = t(`widgets-name.${currWidgetsDetails?.type}`);
|
const widgetTypeTranslation = t(`widgets-name.${currWidgetsDetails?.type}`);
|
||||||
const [widgetValue, setWidgetValue] = useState(
|
|
||||||
currWidgetsDetails.type !== "checkbox" &&
|
const [widgetValue, setWidgetValue] = useState(() => {
|
||||||
(currWidgetsDetails?.options?.response ||
|
if (currWidgetsDetails.type === "checkbox") {
|
||||||
currWidgetsDetails?.options?.defaultValue)
|
return undefined;
|
||||||
);
|
}
|
||||||
|
return (
|
||||||
|
currWidgetsDetails?.options?.response ||
|
||||||
|
currWidgetsDetails?.options?.defaultValue
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const blocked = [
|
||||||
|
"checkbox",
|
||||||
|
"dropdown",
|
||||||
|
"date",
|
||||||
|
"radio",
|
||||||
|
];
|
||||||
|
|
||||||
|
const showClearbtn =
|
||||||
|
!blocked.includes(type);
|
||||||
|
|
||||||
const [cellsValue, setCellsValue] = useState(() => {
|
const [cellsValue, setCellsValue] = useState(() => {
|
||||||
const count = currWidgetsDetails?.options?.cellCount || 5;
|
const count = currWidgetsDetails?.options?.cellCount || 5;
|
||||||
const val =
|
const val =
|
||||||
@@ -192,7 +211,8 @@ function WidgetsValueModal(props) {
|
|||||||
} else {
|
} else {
|
||||||
setHint(currWidgetsDetails?.type);
|
setHint(currWidgetsDetails?.type);
|
||||||
}
|
}
|
||||||
} else if (isSignOrInitials) {
|
}
|
||||||
|
else if (isSignOrInitials) {
|
||||||
setIsTab(currWidgetsDetails?.signatureType);
|
setIsTab(currWidgetsDetails?.signatureType);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -877,9 +897,10 @@ function WidgetsValueModal(props) {
|
|||||||
const handleOnchangeTextBox = (e) => {
|
const handleOnchangeTextBox = (e) => {
|
||||||
// hide any prior validation error while typing
|
// hide any prior validation error while typing
|
||||||
setIsShowValidation(false);
|
setIsShowValidation(false);
|
||||||
setWidgetValue(e.target.value);
|
let value = e.target.value;
|
||||||
|
setWidgetValue(value);
|
||||||
onChangeInput(
|
onChangeInput(
|
||||||
e.target.value,
|
value,
|
||||||
currWidgetsDetails,
|
currWidgetsDetails,
|
||||||
xyPosition,
|
xyPosition,
|
||||||
props.index,
|
props.index,
|
||||||
@@ -1718,13 +1739,14 @@ function WidgetsValueModal(props) {
|
|||||||
} else if (!isSave) {
|
} else if (!isSave) {
|
||||||
const widgetsPosition = xyPosition?.find((data) => data.Id === uniqueId);
|
const widgetsPosition = xyPosition?.find((data) => data.Id === uniqueId);
|
||||||
let nextWidgetDetails = null;
|
let nextWidgetDetails = null;
|
||||||
const editableWidgets = widgetsPosition?.placeHolder.flatMap((page) =>
|
const editableWidgets = (widgetsPosition?.placeHolder ?? []).flatMap(
|
||||||
page.pos
|
({ pos = [], pageNumber }) =>
|
||||||
.filter((widget) => !widget.options?.isReadOnly)
|
pos
|
||||||
.map((widget) => ({
|
.filter(
|
||||||
widget,
|
({ options }) =>
|
||||||
pageNumber: page.pageNumber
|
!options?.isReadOnly
|
||||||
}))
|
)
|
||||||
|
.map((widget) => ({ widget, pageNumber }))
|
||||||
);
|
);
|
||||||
//get current index of widget
|
//get current index of widget
|
||||||
const currentIndex = editableWidgets.findIndex(
|
const currentIndex = editableWidgets.findIndex(
|
||||||
@@ -1926,13 +1948,11 @@ function WidgetsValueModal(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center text-center">
|
<div className="flex justify-between items-center text-center">
|
||||||
{!["checkbox", "dropdown", "date", "radio"].includes(
|
{showClearbtn ? (
|
||||||
currWidgetsDetails?.type
|
|
||||||
) ? (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="op-btn op-btn-ghost op-btn-sm text-base-content mr-1"
|
className="op-btn op-btn-ghost op-btn-sm text-base-content mr-1"
|
||||||
onClick={() => handleClear()}
|
onClick={handleClear}
|
||||||
>
|
>
|
||||||
{t("clear")}
|
{t("clear")}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,15 +3,20 @@ import { isMobile } from "../../constant/Utils";
|
|||||||
// `getWidgetType` is used to load ui of widget in side list
|
// `getWidgetType` is used to load ui of widget in side list
|
||||||
const getWidgetType = (item, widgetName) => {
|
const getWidgetType = (item, widgetName) => {
|
||||||
return (
|
return (
|
||||||
<div className="op-btn w-fit md:w-[100%] op-btn-primary op-btn-outline op-btn-sm focus:outline-none outline outline-[1.5px] ml-[6px] md:ml-0 p-0 overflow-hidden">
|
<div
|
||||||
|
title={widgetName}
|
||||||
|
className="op-btn w-fit md:w-[100%] op-btn-primary op-btn-outline op-btn-sm focus:outline-none ml-[6px] outline outline-[1px] md:ml-0 p-0 overflow-hidden"
|
||||||
|
>
|
||||||
<div className="w-full h-full flex md:justify-between items-center">
|
<div className="w-full h-full flex md:justify-between items-center">
|
||||||
<div className="flex justify-start items-center text-[13px] ml-1">
|
<div className="flex justify-start items-center ml-1 overflow-hidden">
|
||||||
{!isMobile && <i className="fa-light fa-grip-vertical ml-[3px]"></i>}
|
{!isMobile && (
|
||||||
<span className="md:inline-block text-center text-[15px] ml-[5px] font-semibold pr-1 md:pr-0">
|
<i className="fa-light fa-grip-vertical ml-[3px] text-[13px]"></i>
|
||||||
|
)}
|
||||||
|
<span className="md:inline-block text-center text-[11px] mx-[3px] truncate font-medium capitalize">
|
||||||
{widgetName}
|
{widgetName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[20px] op-btn op-btn-primary rounded-none w-[40px] h-full flex justify-center items-center">
|
<div className="text-[13px] op-btn op-btn-primary rounded-none w-[20px] h-full flex justify-center items-center">
|
||||||
<i className={item.icon}></i>
|
<i className={item.icon}></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import fontkit from "@pdf-lib/fontkit";
|
|||||||
import { themeColor } from "./const";
|
import { themeColor } from "./const";
|
||||||
import { format, toZonedTime } from "date-fns-tz";
|
import { format, toZonedTime } from "date-fns-tz";
|
||||||
import i18n from "../i18n";
|
import i18n from "../i18n";
|
||||||
import { buildDownloadFilename } from "../utils";
|
import { applyNumberFormulasToPages, buildDownloadFilename } from "../utils";
|
||||||
|
|
||||||
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||||
export const fontColorArr = ["red", "black", "blue", "yellow"];
|
export const fontColorArr = ["red", "black", "blue", "yellow"];
|
||||||
@@ -327,6 +327,7 @@ export const widgets = [
|
|||||||
{ type: "name", icon: "fa-light fa-user", iconSize: "21px" },
|
{ type: "name", icon: "fa-light fa-user", iconSize: "21px" },
|
||||||
{ type: "job title", icon: "fa-light fa-address-card", iconSize: "17px" },
|
{ type: "job title", icon: "fa-light fa-address-card", iconSize: "17px" },
|
||||||
{ type: "company", icon: "fa-light fa-building", iconSize: "25px" },
|
{ type: "company", icon: "fa-light fa-building", iconSize: "25px" },
|
||||||
|
{ type: "email", icon: "fa-light fa-envelope", iconSize: "20px" },
|
||||||
{ type: "date", icon: "fa-light fa-calendar-days", iconSize: "20px" },
|
{ type: "date", icon: "fa-light fa-calendar-days", iconSize: "20px" },
|
||||||
{ type: textWidget, icon: "fa-light fa-text-width", iconSize: "20px" },
|
{ type: textWidget, icon: "fa-light fa-text-width", iconSize: "20px" },
|
||||||
{ type: cellsWidget, icon: "fa-light fa-table-cells", iconSize: "20px" },
|
{ type: cellsWidget, icon: "fa-light fa-table-cells", iconSize: "20px" },
|
||||||
@@ -337,8 +338,7 @@ export const widgets = [
|
|||||||
iconSize: "19px"
|
iconSize: "19px"
|
||||||
},
|
},
|
||||||
{ type: radioButtonWidget, icon: "fa-light fa-circle-dot", iconSize: "20px" },
|
{ 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: "email", icon: "fa-light fa-envelope", iconSize: "20px" }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getDate = (dateformat) => {
|
export const getDate = (dateformat) => {
|
||||||
@@ -410,6 +410,35 @@ export const changeDateToMomentFormat = (format) => {
|
|||||||
return "L";
|
return "L";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getSignerPages = (xyPosition = [], currentKey, signerId) => {
|
||||||
|
if (!Array.isArray(xyPosition)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const hasSigners = xyPosition.some((item) =>
|
||||||
|
Array.isArray(item?.placeHolder)
|
||||||
|
);
|
||||||
|
if (hasSigners) {
|
||||||
|
const candidates =
|
||||||
|
signerId !== undefined && signerId !== null && signerId !== ""
|
||||||
|
? xyPosition.filter(
|
||||||
|
(item) => item.Id === signerId || item.signerObjId === signerId
|
||||||
|
)
|
||||||
|
: xyPosition;
|
||||||
|
for (const signer of candidates) {
|
||||||
|
const pages = signer?.placeHolder || [];
|
||||||
|
const ownsWidget = pages.some((page) =>
|
||||||
|
(page?.pos || []).some((widget) => widget?.key === currentKey)
|
||||||
|
);
|
||||||
|
if (ownsWidget) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return xyPosition;
|
||||||
|
};
|
||||||
|
|
||||||
export const addWidgetOptions = (
|
export const addWidgetOptions = (
|
||||||
type,
|
type,
|
||||||
signer,
|
signer,
|
||||||
@@ -423,15 +452,9 @@ export const addWidgetOptions = (
|
|||||||
return count + page.pos.filter((item) => item.type === type).length;
|
return count + page.pos.filter((item) => item.type === type).length;
|
||||||
}, 0);
|
}, 0);
|
||||||
const count = countSameWidget + 1;
|
const count = countSameWidget + 1;
|
||||||
defaultOpt = {
|
defaultOpt = { name: `${type}-${count}`, status: "required" };
|
||||||
name: `${type}-${count}`,
|
|
||||||
status: "required"
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
defaultOpt = {
|
defaultOpt = { name: `${type}-1`, status: "required" };
|
||||||
name: `${type}-1`,
|
|
||||||
status: "required"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "signature":
|
case "signature":
|
||||||
@@ -439,11 +462,7 @@ export const addWidgetOptions = (
|
|||||||
case "stamp":
|
case "stamp":
|
||||||
return defaultOpt;
|
return defaultOpt;
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
return {
|
return { ...defaultOpt, isReadOnly: false, isHideLabel: false };
|
||||||
...defaultOpt,
|
|
||||||
isReadOnly: false,
|
|
||||||
isHideLabel: false
|
|
||||||
};
|
|
||||||
case textInputWidget:
|
case textInputWidget:
|
||||||
return { ...defaultOpt, isReadOnly: false };
|
return { ...defaultOpt, isReadOnly: false };
|
||||||
case cellsWidget:
|
case cellsWidget:
|
||||||
@@ -457,20 +476,11 @@ export const addWidgetOptions = (
|
|||||||
case "initials":
|
case "initials":
|
||||||
return defaultOpt;
|
return defaultOpt;
|
||||||
case "name":
|
case "name":
|
||||||
return {
|
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
|
||||||
...defaultOpt,
|
|
||||||
defaultValue: widgetValue ? widgetValue : ""
|
|
||||||
};
|
|
||||||
case "company":
|
case "company":
|
||||||
return {
|
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
|
||||||
...defaultOpt,
|
|
||||||
defaultValue: widgetValue ? widgetValue : ""
|
|
||||||
};
|
|
||||||
case "job title":
|
case "job title":
|
||||||
return {
|
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
|
||||||
...defaultOpt,
|
|
||||||
defaultValue: widgetValue ? widgetValue : ""
|
|
||||||
};
|
|
||||||
case "date": {
|
case "date": {
|
||||||
const dateFormat = signer?.DateFormat
|
const dateFormat = signer?.DateFormat
|
||||||
? selectFormat(signer?.DateFormat)
|
? selectFormat(signer?.DateFormat)
|
||||||
@@ -518,15 +528,9 @@ export const addWidgetSelfsignOptions = (
|
|||||||
return count + page.pos.filter((item) => item.type === type).length;
|
return count + page.pos.filter((item) => item.type === type).length;
|
||||||
}, 0);
|
}, 0);
|
||||||
const count = countSameWidget + 1;
|
const count = countSameWidget + 1;
|
||||||
defaultOpt = {
|
defaultOpt = { name: `${type}-${count}`, status: "required" };
|
||||||
name: `${type}-${count}`,
|
|
||||||
status: "required"
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
defaultOpt = {
|
defaultOpt = { name: `${type}-1`, status: "required" };
|
||||||
name: `${type}-1`,
|
|
||||||
status: "required"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "signature":
|
case "signature":
|
||||||
@@ -1077,9 +1081,11 @@ export const onChangeInput = (
|
|||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const recalculatedPages = applyNumberFormulasToPages(newUpdateSignPos);
|
||||||
|
|
||||||
const newUpdateSigner = xyPosition.map((obj) => {
|
const newUpdateSigner = xyPosition.map((obj) => {
|
||||||
if (obj.Id === userId) {
|
if (obj.Id === userId) {
|
||||||
return { ...obj, placeHolder: newUpdateSignPos };
|
return { ...obj, placeHolder: recalculatedPages };
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
@@ -1125,7 +1131,7 @@ export const onChangeInput = (
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
setXyPosition(updatePlaceholder);
|
setXyPosition(applyNumberFormulasToPages(updatePlaceholder));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//function to increase height of text area on press enter
|
//function to increase height of text area on press enter
|
||||||
@@ -1220,13 +1226,13 @@ export const calculateInitialWidthHeight = (widgetData) => {
|
|||||||
export const widgetDataValue = (type, value) => {
|
export const widgetDataValue = (type, value) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "name":
|
case "name":
|
||||||
return value?.Name;
|
return value?.Name || value?.name;
|
||||||
case "company":
|
case "company":
|
||||||
return value?.Company;
|
return value?.Company || value?.company;
|
||||||
case "job title":
|
case "job title":
|
||||||
return value?.JobTitle;
|
return value?.JobTitle || value?.jobTitle;
|
||||||
case "email":
|
case "email":
|
||||||
return value?.Email;
|
return value?.Email || value?.email;
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -1850,6 +1856,16 @@ export const embedWidgetsToDoc = async (
|
|||||||
console.log("error in image", e);
|
console.log("error in image", e);
|
||||||
}
|
}
|
||||||
for (let [id, position] of widgetsPositionArr.entries()) {
|
for (let [id, position] of widgetsPositionArr.entries()) {
|
||||||
|
const isTextTypeWidget = [
|
||||||
|
textWidget,
|
||||||
|
textInputWidget,
|
||||||
|
cellsWidget,
|
||||||
|
"name",
|
||||||
|
"company",
|
||||||
|
"job title",
|
||||||
|
"date",
|
||||||
|
"email"
|
||||||
|
].includes(position.type);
|
||||||
if (hasError) break; // Stop the inner loop if an error occurred
|
if (hasError) break; // Stop the inner loop if an error occurred
|
||||||
try {
|
try {
|
||||||
let img;
|
let img;
|
||||||
@@ -1898,33 +1914,13 @@ export const embedWidgetsToDoc = async (
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const WidgetsTypeTextExist = [
|
const yPosition = isTextTypeWidget ? resizePos + 6 : resizePos;
|
||||||
textWidget,
|
|
||||||
textInputWidget,
|
|
||||||
cellsWidget,
|
|
||||||
"name",
|
|
||||||
"company",
|
|
||||||
"job title",
|
|
||||||
"date",
|
|
||||||
"email"
|
|
||||||
].includes(position.type);
|
|
||||||
const yPosition = WidgetsTypeTextExist ? resizePos + 6 : resizePos;
|
|
||||||
return yPosition;
|
return yPosition;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const color = position?.options?.fontColor;
|
const color = position?.options?.fontColor;
|
||||||
const updateColorInRgb = getWidgetsFontColor(color);
|
const updateColorInRgb = getWidgetsFontColor(color);
|
||||||
const fontSize = parseInt(position?.options?.fontSize || 12);
|
const fontSize = parseInt(position?.options?.fontSize || 12);
|
||||||
const isTextTypeWidget = [
|
|
||||||
textWidget,
|
|
||||||
textInputWidget,
|
|
||||||
cellsWidget,
|
|
||||||
"name",
|
|
||||||
"company",
|
|
||||||
"job title",
|
|
||||||
"date",
|
|
||||||
"email"
|
|
||||||
].includes(position.type);
|
|
||||||
if (position.type === "checkbox") {
|
if (position.type === "checkbox") {
|
||||||
// Determine layout mode: 'vertical' (default) or 'horizontal'
|
// Determine layout mode: 'vertical' (default) or 'horizontal'
|
||||||
// const isHorizontal = position.layout === "horizontal";
|
// const isHorizontal = position.layout === "horizontal";
|
||||||
@@ -2022,7 +2018,7 @@ export const embedWidgetsToDoc = async (
|
|||||||
textContent = position.options?.response;
|
textContent = position.options?.response;
|
||||||
}
|
}
|
||||||
} else if (position?.options?.defaultValue) {
|
} else if (position?.options?.defaultValue) {
|
||||||
textContent = position?.options?.defaultValue;
|
textContent = position?.options?.defaultValue?.toString();
|
||||||
}
|
}
|
||||||
if (position.type === cellsWidget) {
|
if (position.type === cellsWidget) {
|
||||||
const cellCount =
|
const cellCount =
|
||||||
@@ -2049,15 +2045,16 @@ export const embedWidgetsToDoc = async (
|
|||||||
if (ch) page.drawText(ch, textPosition);
|
if (ch) page.drawText(ch, textPosition);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const fixedWidth = widgetWidth; // Set your fixed width
|
const fixedWidth = widgetWidth; // Set your fixed
|
||||||
const isNewOnEnterLineExist = textContent.includes("\n");
|
textContent = textContent?.toString();
|
||||||
|
const isNewOnEnterLineExist = textContent?.includes("\n");
|
||||||
|
|
||||||
// Function to break text into lines based on the fixed width
|
// Function to break text into lines based on the fixed width
|
||||||
const NewbreakTextIntoLines = (textContent, width) => {
|
const NewbreakTextIntoLines = (textContent, width) => {
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let currentLine = "";
|
let currentLine = "";
|
||||||
|
|
||||||
for (const word of textContent.split(" ")) {
|
for (const word of textContent?.split(" ")) {
|
||||||
//get text line width
|
//get text line width
|
||||||
const lineWidth = font.widthOfTextAtSize(
|
const lineWidth = font.widthOfTextAtSize(
|
||||||
`${currentLine} ${word}`,
|
`${currentLine} ${word}`,
|
||||||
@@ -2242,7 +2239,6 @@ export const embedWidgetsToDoc = async (
|
|||||||
width: widgetWidth,
|
width: widgetWidth,
|
||||||
height: widgetHeight
|
height: widgetHeight
|
||||||
};
|
};
|
||||||
|
|
||||||
const imageOptions = getWidgetPosition(page, signature, 1, getSize);
|
const imageOptions = getWidgetPosition(page, signature, 1, getSize);
|
||||||
page.drawImage(img, imageOptions);
|
page.drawImage(img, imageOptions);
|
||||||
}
|
}
|
||||||
@@ -2370,56 +2366,34 @@ export const contractDocument = async (documentId, include) => {
|
|||||||
|
|
||||||
//function for add default signature or image for all requested location
|
//function for add default signature or image for all requested location
|
||||||
export const addDefaultSignatureImg = (xyPosition, defaultSignImg, type) => {
|
export const addDefaultSignatureImg = (xyPosition, defaultSignImg, type) => {
|
||||||
let imgWH = { width: "", height: "" };
|
|
||||||
const img = new Image();
|
|
||||||
img.src = defaultSignImg;
|
|
||||||
if (img.complete) {
|
|
||||||
imgWH = { width: img.width, height: img.height };
|
|
||||||
}
|
|
||||||
|
|
||||||
let xyDefaultPos = [];
|
let xyDefaultPos = [];
|
||||||
for (let i = 0; i < xyPosition.length; i++) {
|
for (let i = 0; i < xyPosition.length; i++) {
|
||||||
// let getIMGWH;
|
|
||||||
const getXYdata = xyPosition[i].pos;
|
const getXYdata = xyPosition[i].pos;
|
||||||
const getPageNo = xyPosition[i].pageNumber;
|
const getPageNo = xyPosition[i].pageNumber;
|
||||||
const getPosData = getXYdata;
|
const getPosData = getXYdata;
|
||||||
|
|
||||||
const addSign = getPosData.map((position) => {
|
const addSign = getPosData.map((position) => {
|
||||||
// getIMGWH = calculateImgAspectRatio(imgWH, position);
|
|
||||||
if (position.type) {
|
if (position.type) {
|
||||||
if (position?.type === type) {
|
if (position?.type === type) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
SignUrl: defaultSignImg,
|
SignUrl: defaultSignImg,
|
||||||
// Width: getIMGWH.newWidth,
|
|
||||||
// Height: getIMGWH.newHeight,
|
|
||||||
ImageType: "default",
|
ImageType: "default",
|
||||||
options: {
|
options: { ...position.options, response: defaultSignImg }
|
||||||
...position.options,
|
|
||||||
response: defaultSignImg
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (position && !position.isStamp) {
|
} else if (position && !position.isStamp) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
SignUrl: defaultSignImg,
|
SignUrl: defaultSignImg,
|
||||||
// Width: getIMGWH.newWidth,
|
|
||||||
// Height: getIMGWH.newHeight,
|
|
||||||
ImageType: "default",
|
ImageType: "default",
|
||||||
options: {
|
options: { ...position.options, response: defaultSignImg }
|
||||||
...position.options,
|
|
||||||
response: defaultSignImg
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return position;
|
return position;
|
||||||
});
|
});
|
||||||
|
|
||||||
const newXypos = {
|
const newXypos = { pageNumber: getPageNo, pos: addSign };
|
||||||
pageNumber: getPageNo,
|
|
||||||
pos: addSign
|
|
||||||
};
|
|
||||||
xyDefaultPos.push(newXypos);
|
xyDefaultPos.push(newXypos);
|
||||||
}
|
}
|
||||||
return xyDefaultPos;
|
return xyDefaultPos;
|
||||||
@@ -3006,34 +2980,34 @@ export const saveLanguageInLocal = (i18n) => {
|
|||||||
const detectedLanguage = i18n.language || "en";
|
const detectedLanguage = i18n.language || "en";
|
||||||
localStorage.setItem("i18nextLng", detectedLanguage);
|
localStorage.setItem("i18nextLng", detectedLanguage);
|
||||||
};
|
};
|
||||||
//function to get default signatur eof current user from `contracts_Signature` class
|
|
||||||
|
// function to get default signature of current user from `contracts_Signature` class
|
||||||
export const getDefaultSignature = async (objectId) => {
|
export const getDefaultSignature = async (objectId) => {
|
||||||
try {
|
try {
|
||||||
const query = new Parse.Query("contracts_Signature");
|
if (objectId) {
|
||||||
query.equalTo("UserId", {
|
const result = await Parse.Cloud.run("getdefaultsignature", {
|
||||||
__type: "Pointer",
|
userId: objectId
|
||||||
className: "_User",
|
});
|
||||||
objectId: objectId
|
if (result) {
|
||||||
});
|
const res = JSON.parse(JSON.stringify(result));
|
||||||
|
const defaultSignature = res?.ImageURL
|
||||||
|
? await getBase64FromUrl(res?.ImageURL, true)
|
||||||
|
: "";
|
||||||
|
const defaultInitial = res?.Initials
|
||||||
|
? await getBase64FromUrl(res?.Initials, true)
|
||||||
|
: "";
|
||||||
|
|
||||||
const result = await query.first();
|
return {
|
||||||
if (result) {
|
status: "success",
|
||||||
const res = JSON.parse(JSON.stringify(result));
|
res: {
|
||||||
const defaultSignature = res?.ImageURL
|
id: result?.id,
|
||||||
? await getBase64FromUrl(res?.ImageURL, true)
|
defaultSignature: defaultSignature,
|
||||||
: "";
|
defaultInitial: defaultInitial
|
||||||
const defaultInitial = res?.Initials
|
}
|
||||||
? await getBase64FromUrl(res?.Initials, true)
|
};
|
||||||
: "";
|
}
|
||||||
|
} else {
|
||||||
return {
|
return { status: "error" };
|
||||||
status: "success",
|
|
||||||
res: {
|
|
||||||
id: result?.id,
|
|
||||||
defaultSignature: defaultSignature,
|
|
||||||
defaultInitial: defaultInitial
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -3359,10 +3333,7 @@ export const handleHeighlightWidget = (
|
|||||||
...data,
|
...data,
|
||||||
pos: data.pos.map((position) => {
|
pos: data.pos.map((position) => {
|
||||||
if (position.key === key) {
|
if (position.key === key) {
|
||||||
return {
|
return { ...position, zIndex: highestZIndex + 1 };
|
||||||
...position,
|
|
||||||
zIndex: highestZIndex + 1
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return position;
|
return position;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ const parseAppId = process.env.REACT_APP_APPID
|
|||||||
? process.env.REACT_APP_APPID
|
? process.env.REACT_APP_APPID
|
||||||
: "opensign";
|
: "opensign";
|
||||||
const serverUrl = serverUrl_fn();
|
const serverUrl = serverUrl_fn();
|
||||||
|
const commonheader = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId
|
||||||
|
};
|
||||||
export const SaveFileSize = async (size, imageUrl, tenantId, userId) => {
|
export const SaveFileSize = async (size, imageUrl, tenantId, userId) => {
|
||||||
//checking server url and save file's size
|
//checking server url and save file's size
|
||||||
const tenantPtr = {
|
const tenantPtr = {
|
||||||
@@ -29,7 +33,6 @@ export const SaveFileSize = async (size, imageUrl, tenantId, userId) => {
|
|||||||
);
|
);
|
||||||
const response = res.data.results;
|
const response = res.data.results;
|
||||||
let data;
|
let data;
|
||||||
// console.log("response", response);
|
|
||||||
if (response && response.length > 0) {
|
if (response && response.length > 0) {
|
||||||
data = {
|
data = {
|
||||||
usedStorage: response[0].usedStorage
|
usedStorage: response[0].usedStorage
|
||||||
@@ -39,20 +42,12 @@ export const SaveFileSize = async (size, imageUrl, tenantId, userId) => {
|
|||||||
await axios.put(
|
await axios.put(
|
||||||
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
|
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
|
||||||
data,
|
data,
|
||||||
{
|
{ headers: commonheader }
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
||||||
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
|
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
|
||||||
headers: {
|
headers: commonheader
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -69,14 +64,9 @@ const saveDataFile = async (size, imageUrl, tenantPtr, UserId) => {
|
|||||||
TenantPtr: tenantPtr,
|
TenantPtr: tenantPtr,
|
||||||
...(UserId ? { UserId: UserId } : {})
|
...(UserId ? { UserId: UserId } : {})
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log("data save",file, data)
|
|
||||||
try {
|
try {
|
||||||
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
|
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
|
||||||
headers: {
|
headers: commonheader
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in save usage ", err);
|
console.log("err in save usage ", err);
|
||||||
|
|||||||
@@ -4,21 +4,22 @@ import Footer from "../components/Footer";
|
|||||||
import Sidebar from "../components/sidebar/Sidebar";
|
import Sidebar from "../components/sidebar/Sidebar";
|
||||||
import Tour from "../primitives/Tour";
|
import Tour from "../primitives/Tour";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import { useNavigate, Outlet } from "react-router";
|
||||||
import { useNavigate, useLocation, Outlet } from "react-router";
|
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||||
|
import SessionExpiredModal from "../primitives/SessionExpiredModal";
|
||||||
|
|
||||||
const HomeLayout = () => {
|
const HomeLayout = () => {
|
||||||
const appName =
|
const appName =
|
||||||
"OpenSign™";
|
"OpenSign™";
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const dispatch = useDispatch();
|
||||||
const arr = useSelector((state) => state.TourSteps);
|
const arr = useSelector((state) => state.TourSteps);
|
||||||
const [isUserValid, setIsUserValid] = useState(true);
|
const isValidSession = useSelector((state) => state.user.isValidSession);
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||||
const [isTour, setIsTour] = useState(false);
|
const [isTour, setIsTour] = useState(false);
|
||||||
@@ -35,28 +36,31 @@ const HomeLayout = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!tenantId) {
|
if (localStorage.getItem("accesstoken")) {
|
||||||
setIsUserValid(false);
|
if (!tenantId) {
|
||||||
} else {
|
dispatch(sessionStatus(false));
|
||||||
(async () => {
|
} else {
|
||||||
try {
|
(async () => {
|
||||||
// Use the session token to validate the user
|
try {
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
// Use the session token to validate the user
|
||||||
const user = await userQuery.get(Parse?.User?.current()?.id, {
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
const user = await userQuery.get(Parse?.User?.current()?.id, {
|
||||||
});
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
if (user) {
|
});
|
||||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
if (user) {
|
||||||
setIsUserValid(true);
|
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||||
setIsLoader(false);
|
dispatch(sessionStatus(true));
|
||||||
} else {
|
setIsLoader(false);
|
||||||
setIsUserValid(false);
|
} else {
|
||||||
|
dispatch(sessionStatus(true));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("catch");
|
||||||
|
// Session token is invalid or there was an error
|
||||||
|
dispatch(sessionStatus(false));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
// Session token is invalid or there was an error
|
}
|
||||||
setIsUserValid(false);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -169,77 +173,56 @@ const HomeLayout = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLoginBtn = async () => {
|
return isValidSession && localStorage.getItem("accesstoken") ? (
|
||||||
try {
|
|
||||||
await Parse?.User?.logOut();
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err ", err);
|
|
||||||
} finally {
|
|
||||||
localStorage.removeItem("accesstoken");
|
|
||||||
navigate("/", { replace: true, state: { from: location } });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-screen overflow-hidden">
|
<div className="flex flex-col h-screen overflow-hidden">
|
||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
<header className="z-[501]">
|
<header className="z-[501]">
|
||||||
{!isLoader && <Header setIsLoggingOut={setIsLoggingOut} />}
|
{!isLoader && <Header setIsLoggingOut={setIsLoggingOut} />}
|
||||||
</header>
|
</header>
|
||||||
{isUserValid ? (
|
{isLoader ? (
|
||||||
|
<div className="flex h-[100vh] justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<>
|
<>
|
||||||
{isLoader ? (
|
{isLoggingOut && (
|
||||||
<div className="flex h-[100vh] justify-center items-center">
|
<div className="inset-0 bg-black/30 z-[1000] fixed flex justify-center items-center">
|
||||||
<Loader />
|
<Loader />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{isLoggingOut && (
|
|
||||||
<div className="inset-0 bg-black/30 z-[1000] fixed flex justify-center items-center">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* BODY */}
|
|
||||||
<div className="flex flex-1 overflow-hidden">
|
|
||||||
{/* SIDEBAR with width animation */}
|
|
||||||
<Sidebar />
|
|
||||||
{/* MAIN (includes both content + footer in one scrollable column) */}
|
|
||||||
<main
|
|
||||||
id="renderList"
|
|
||||||
className="flex-1 overflow-auto transition-all duration-300 ease-in-out"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col min-h-full">
|
|
||||||
{/* your page content */}
|
|
||||||
<div className="p-3">{<Outlet />}</div>
|
|
||||||
{/* sticky-but-scrollable footer */}
|
|
||||||
<div className="mt-auto z-30">
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
<Tour
|
|
||||||
onRequestClose={closeTour}
|
|
||||||
steps={tourConfigs}
|
|
||||||
isOpen={isTour}
|
|
||||||
disableKeyboardNavigation={["esc"]}
|
|
||||||
// disableInteraction={true}
|
|
||||||
scrollOffset={-100}
|
|
||||||
showCloseButton={isCloseBtn}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</>
|
{/* BODY */}
|
||||||
) : (
|
<div className="flex flex-1 overflow-hidden">
|
||||||
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
{/* SIDEBAR with width animation */}
|
||||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
<Sidebar />
|
||||||
<p className="text-xl font-medium">{t("session-expired")}</p>
|
{/* MAIN (includes both content + footer in one scrollable column) */}
|
||||||
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
|
<main
|
||||||
{t("login")}
|
id="renderList"
|
||||||
</button>
|
className="flex-1 overflow-auto transition-all duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col min-h-full">
|
||||||
|
{/* your page content */}
|
||||||
|
<div className="p-3">{<Outlet />}</div>
|
||||||
|
{/* sticky-but-scrollable footer */}
|
||||||
|
<div className="mt-auto z-30">
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
<Tour
|
||||||
|
onRequestClose={closeTour}
|
||||||
|
steps={tourConfigs}
|
||||||
|
isOpen={isTour}
|
||||||
|
disableKeyboardNavigation={["esc"]}
|
||||||
|
// disableInteraction={true}
|
||||||
|
scrollOffset={-100}
|
||||||
|
showCloseButton={isCloseBtn}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<SessionExpiredModal />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import ModalUi from "../primitives/ModalUi";
|
|||||||
import { Tooltip } from "react-tooltip";
|
import { Tooltip } from "react-tooltip";
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||||
|
|
||||||
// `Form` render all type of Form on this basis of their provided in path
|
// `Form` render all type of Form on this basis of their provided in path
|
||||||
function Form() {
|
function Form() {
|
||||||
@@ -50,6 +52,7 @@ const Forms = (props) => {
|
|||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
const inputFileRef = useRef(null);
|
const inputFileRef = useRef(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const [signers, setSigners] = useState([]);
|
const [signers, setSigners] = useState([]);
|
||||||
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -277,7 +280,11 @@ const Forms = (props) => {
|
|||||||
console.log("err in docx to pdf ", err);
|
console.log("err in docx to pdf ", err);
|
||||||
const error =
|
const error =
|
||||||
t("docx-error");
|
t("docx-error");
|
||||||
alert(error);
|
if (err?.code === 209) {
|
||||||
|
dispatch(sessionStatus(false));
|
||||||
|
} else {
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,7 +342,11 @@ const Forms = (props) => {
|
|||||||
setSelectedFiles([]);
|
setSelectedFiles([]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(error.message);
|
if (error?.code === 209) {
|
||||||
|
dispatch(sessionStatus(false));
|
||||||
|
} else {
|
||||||
|
alert(error.message);
|
||||||
|
}
|
||||||
setSelectedFiles([]);
|
setSelectedFiles([]);
|
||||||
removeFile(e);
|
removeFile(e);
|
||||||
}
|
}
|
||||||
@@ -472,7 +483,9 @@ const Forms = (props) => {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
console.log("err ", err);
|
||||||
if (err.message === "only 15 reminder allowed") {
|
if (err?.code === 209) {
|
||||||
|
dispatch(sessionStatus(false));
|
||||||
|
} else if (err.message === "only 15 reminder allowed") {
|
||||||
setIsAlert({
|
setIsAlert({
|
||||||
type: "danger",
|
type: "danger",
|
||||||
message: t("only-15-reminder-allowed")
|
message: t("only-15-reminder-allowed")
|
||||||
@@ -604,7 +617,9 @@ const Forms = (props) => {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
removeFile();
|
removeFile();
|
||||||
if (err?.response?.status === 401) {
|
if (err?.code === 209) {
|
||||||
|
dispatch(sessionStatus(false));
|
||||||
|
} else if (err?.response?.status === 401) {
|
||||||
setIsPassword(true);
|
setIsPassword(true);
|
||||||
setIsCorrectPass(false);
|
setIsCorrectPass(false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -46,10 +46,9 @@ const ManageSign = () => {
|
|||||||
objectId: User.id
|
objectId: User.id
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const signCls = "contracts_Signature";
|
const signRes = await Parse.Cloud.run("getdefaultsignature", {
|
||||||
const signQuery = new Parse.Query(signCls);
|
userId: User.id
|
||||||
signQuery.equalTo("UserId", userId);
|
});
|
||||||
const signRes = await signQuery.first();
|
|
||||||
if (signRes) {
|
if (signRes) {
|
||||||
const res = signRes.toJSON();
|
const res = signRes.toJSON();
|
||||||
setId(res.objectId);
|
setId(res.objectId);
|
||||||
@@ -226,44 +225,23 @@ const ManageSign = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const saveEntry = async (obj) => {
|
const saveEntry = async (obj) => {
|
||||||
const signCls = "contracts_Signature";
|
try {
|
||||||
const User = Parse?.User?.current()?.id;
|
const User = Parse?.User?.current()?.id;
|
||||||
const userId = { __type: "Pointer", className: "_User", objectId: User };
|
const res = await Parse.Cloud.run("managesign", {
|
||||||
if (id) {
|
signature: obj.url,
|
||||||
try {
|
userId: User,
|
||||||
const updateSign = new Parse.Object(signCls);
|
initials: obj.initialsUrl,
|
||||||
updateSign.id = id;
|
id: id,
|
||||||
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : "");
|
title: obj.name
|
||||||
updateSign.set("ImageURL", obj.url ? obj.url : "");
|
});
|
||||||
updateSign.set("SignatureName", obj.name);
|
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||||
updateSign.set("UserId", userId);
|
return res;
|
||||||
const res = await updateSign.save();
|
} catch (err) {
|
||||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
console.log(err);
|
||||||
return res;
|
setIsAlert({ type: "danger", message: `${err.message}` });
|
||||||
} catch (err) {
|
} finally {
|
||||||
console.log(err);
|
setIsLoader(false);
|
||||||
setIsAlert({ type: "danger", message: `${err.message}` });
|
setTimeout(() => setIsAlert({}), 2000);
|
||||||
} finally {
|
|
||||||
setIsLoader(false);
|
|
||||||
setTimeout(() => setIsAlert({}), 2000);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const updateSign = new Parse.Object(signCls);
|
|
||||||
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : "");
|
|
||||||
updateSign.set("ImageURL", obj.url);
|
|
||||||
updateSign.set("SignatureName", obj.name);
|
|
||||||
updateSign.set("UserId", userId);
|
|
||||||
const res = await updateSign.save();
|
|
||||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
|
||||||
return res;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
setIsAlert({ type: "success", message: `${err.message}` });
|
|
||||||
} finally {
|
|
||||||
setIsLoader(false);
|
|
||||||
setTimeout(() => setIsAlert({}), 2000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import {
|
|||||||
widgetDataValue,
|
widgetDataValue,
|
||||||
getOriginalWH,
|
getOriginalWH,
|
||||||
handleCheckResponse,
|
handleCheckResponse,
|
||||||
convertJpegToPng
|
convertJpegToPng,
|
||||||
} from "../constant/Utils";
|
} from "../constant/Utils";
|
||||||
import Header from "../components/pdf/PdfHeader";
|
import Header from "../components/pdf/PdfHeader";
|
||||||
import RenderPdf from "../components/pdf/RenderPdf";
|
import RenderPdf from "../components/pdf/RenderPdf";
|
||||||
|
|||||||
@@ -722,9 +722,7 @@ function PlaceHolderSign() {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const addSignPos = getPageNumer?.[0]?.pos?.map((url) => {
|
||||||
const getPosData = getXYdata;
|
|
||||||
const addSignPos = getPosData.map((url) => {
|
|
||||||
if (url.key === keyValue) {
|
if (url.key === keyValue) {
|
||||||
return {
|
return {
|
||||||
...url,
|
...url,
|
||||||
@@ -1455,9 +1453,9 @@ function PlaceHolderSign() {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const textSize = fontSize || currWidgetsDetails?.options?.fontSize;
|
||||||
const getPosData = getXYdata;
|
const textColor = fontColor || currWidgetsDetails?.options?.fontColor;
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (currWidgetsDetails?.type === radioButtonWidget) {
|
if (currWidgetsDetails?.type === radioButtonWidget) {
|
||||||
if (addOption) {
|
if (addOption) {
|
||||||
@@ -1486,12 +1484,8 @@ function PlaceHolderSign() {
|
|||||||
isReadOnly: isReadOnly || false,
|
isReadOnly: isReadOnly || false,
|
||||||
isHideLabel: isHideLabel || false,
|
isHideLabel: isHideLabel || false,
|
||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1525,12 +1519,8 @@ function PlaceHolderSign() {
|
|||||||
layout: layout,
|
layout: layout,
|
||||||
isReadOnly: isReadOnly || false,
|
isReadOnly: isReadOnly || false,
|
||||||
isHideLabel: isHideLabel || false,
|
isHideLabel: isHideLabel || false,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1543,12 +1533,8 @@ function PlaceHolderSign() {
|
|||||||
status: status,
|
status: status,
|
||||||
values: dropdownOptions,
|
values: dropdownOptions,
|
||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black",
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black",
|
|
||||||
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
|
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1563,9 +1549,11 @@ function PlaceHolderSign() {
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
|
const recalculatedPlaceholders =
|
||||||
|
utils.applyNumberFormulasToPages(newUpdateSignPos);
|
||||||
const newUpdateSigner = signerPos.map((obj) => {
|
const newUpdateSigner = signerPos.map((obj) => {
|
||||||
if (obj.Id === uniqueId) {
|
if (obj.Id === uniqueId) {
|
||||||
return { ...obj, placeHolder: newUpdateSignPos };
|
return { ...obj, placeHolder: recalculatedPlaceholders };
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
@@ -1597,9 +1585,9 @@ function PlaceHolderSign() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const textSize = fontSize || currWidgetsDetails?.options?.fontSize;
|
||||||
const getPosData = getXYdata;
|
const textColor = fontColor || currWidgetsDetails?.options?.fontColor;
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (position.type === textInputWidget) {
|
if (position.type === textInputWidget) {
|
||||||
return {
|
return {
|
||||||
@@ -1612,12 +1600,8 @@ function PlaceHolderSign() {
|
|||||||
defaultValue: defaultdata?.defaultValue || "",
|
defaultValue: defaultdata?.defaultValue || "",
|
||||||
validation:
|
validation:
|
||||||
{},
|
{},
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black",
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black",
|
|
||||||
isReadOnly: defaultdata?.isReadOnly || false
|
isReadOnly: defaultdata?.isReadOnly || false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1636,16 +1620,13 @@ function PlaceHolderSign() {
|
|||||||
),
|
),
|
||||||
validation:
|
validation:
|
||||||
{},
|
{},
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black",
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black",
|
|
||||||
isReadOnly: defaultdata?.isReadOnly || false
|
isReadOnly: defaultdata?.isReadOnly || false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (["signature"].includes(position.type)) {
|
}
|
||||||
|
else if (["signature"].includes(position.type)) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
options: {
|
options: {
|
||||||
@@ -1663,12 +1644,8 @@ function PlaceHolderSign() {
|
|||||||
status: defaultdata.status,
|
status: defaultdata.status,
|
||||||
defaultValue: defaultdata.defaultValue,
|
defaultValue: defaultdata.defaultValue,
|
||||||
hint: defaultdata?.hint || "",
|
hint: defaultdata?.hint || "",
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2535,6 +2512,8 @@ function PlaceHolderSign() {
|
|||||||
fontColor={fontColor}
|
fontColor={fontColor}
|
||||||
setFontColor={setFontColor}
|
setFontColor={setFontColor}
|
||||||
roleName={roleName}
|
roleName={roleName}
|
||||||
|
widgetsSource={signerPos}
|
||||||
|
activeSignerId={uniqueId}
|
||||||
/>
|
/>
|
||||||
<RotateAlert
|
<RotateAlert
|
||||||
showRotateAlert={showRotateAlert.status}
|
showRotateAlert={showRotateAlert.status}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ import {
|
|||||||
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
|
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
|
||||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||||
import CellsSettingModal from "../components/pdf/CellsSettingModal";
|
import CellsSettingModal from "../components/pdf/CellsSettingModal";
|
||||||
|
import { applyNumberFormulasToPages } from "../utils";
|
||||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||||
function SignYourSelf() {
|
function SignYourSelf() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -140,6 +141,8 @@ function SignYourSelf() {
|
|||||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||||
const [isResize, setIsResize] = useState(false);
|
const [isResize, setIsResize] = useState(false);
|
||||||
const [isUploadPdf, setIsUploadPdf] = useState(false);
|
const [isUploadPdf, setIsUploadPdf] = useState(false);
|
||||||
|
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
|
||||||
|
const [widgetsTour, setWidgetsTour] = useState(false);
|
||||||
|
|
||||||
const [owner, setOwner] = useState({});
|
const [owner, setOwner] = useState({});
|
||||||
const [, drop] = useDrop({
|
const [, drop] = useDrop({
|
||||||
@@ -586,9 +589,23 @@ function SignYourSelf() {
|
|||||||
}
|
}
|
||||||
if (!isEnableOTP || isEmailVerified) {
|
if (!isEnableOTP || isEmailVerified) {
|
||||||
let showAlert = false,
|
let showAlert = false,
|
||||||
isSignatureExist = false;
|
widgetKey,
|
||||||
|
tourPageNumber;
|
||||||
try {
|
try {
|
||||||
|
const isSignatureExist = xyPosition?.some((p) =>
|
||||||
|
p?.pos?.some((x) => x?.type === "signature")
|
||||||
|
);
|
||||||
|
if (!isSignatureExist) {
|
||||||
|
setIsAlert({
|
||||||
|
header: t("fields-required"),
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: t("signature-widget-alert-1")
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < xyPosition?.length; i++) {
|
for (let i = 0; i < xyPosition?.length; i++) {
|
||||||
|
tourPageNumber = xyPosition[i]?.pageNumber;
|
||||||
const requiredWidgets = xyPosition[i].pos.filter(
|
const requiredWidgets = xyPosition[i].pos.filter(
|
||||||
(position) => position.type !== "checkbox"
|
(position) => position.type !== "checkbox"
|
||||||
);
|
);
|
||||||
@@ -597,34 +614,24 @@ function SignYourSelf() {
|
|||||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
if (!checkSigned) {
|
if (!checkSigned) {
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
const checkSignUrl = requiredWidgets[i]?.SignUrl;
|
||||||
let checkDefaultSigned =
|
let checkDefaultSigned =
|
||||||
requiredWidgets[i]?.options?.defaultValue;
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
if (!checkSignUrl && !checkDefaultSigned && !showAlert) {
|
if (!checkSignUrl && !checkDefaultSigned && !showAlert) {
|
||||||
showAlert = true;
|
showAlert = true;
|
||||||
|
widgetKey = requiredWidgets[i]?.key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//condition to check exist signature widget or not
|
if (showAlert) {
|
||||||
if (!isSignatureExist) {
|
break;
|
||||||
isSignatureExist = xyPosition[i].pos.some(
|
|
||||||
(data) => data?.type === "signature"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xyPosition.length === 0 || !isSignatureExist) {
|
if (showAlert) {
|
||||||
setIsAlert({
|
setUnSignedWidgetId(widgetKey);
|
||||||
header: t("fields-required"),
|
setPageNumber(tourPageNumber);
|
||||||
isShow: true,
|
setWidgetsTour(true);
|
||||||
alertMessage: t("signature-widget-alert-1")
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else if (showAlert) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: t("signature-widget-alert-2")
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
setIsUiLoading(true);
|
setIsUiLoading(true);
|
||||||
@@ -771,9 +778,7 @@ function SignYourSelf() {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (filterDropPos?.length > 0) {
|
if (filterDropPos?.length > 0) {
|
||||||
const getXYdata = xyPosition[index].pos;
|
const addSign = xyPosition[index]?.pos?.map((url) => {
|
||||||
const getPosData = getXYdata;
|
|
||||||
const addSign = getPosData.map((url) => {
|
|
||||||
if (url.key === dragKey) {
|
if (url.key === dragKey) {
|
||||||
return {
|
return {
|
||||||
...url,
|
...url,
|
||||||
@@ -958,11 +963,9 @@ function SignYourSelf() {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
|
||||||
const getPosData = getXYdata;
|
|
||||||
const widgetLayout =
|
const widgetLayout =
|
||||||
currWidgetsDetails?.type === "checkbox" ? { layout: layout } : {};
|
currWidgetsDetails?.type === "checkbox" ? { layout: layout } : {};
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (addOption) {
|
if (addOption) {
|
||||||
return {
|
return {
|
||||||
@@ -1024,9 +1027,7 @@ function SignYourSelf() {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
const getPosData = getXYdata;
|
|
||||||
const addSignPos = getPosData.map((position) => {
|
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
@@ -1083,6 +1084,8 @@ function SignYourSelf() {
|
|||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const updatePos = getPageNumer[0].pos.map((position) => {
|
const updatePos = getPageNumer[0].pos.map((position) => {
|
||||||
|
const textSize = newFontSize || position?.options?.fontSize;
|
||||||
|
const textColor = newFontColor || position?.options?.fontColor;
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (position.type === cellsWidget) {
|
if (position.type === cellsWidget) {
|
||||||
const count = parseInt(
|
const count = parseInt(
|
||||||
@@ -1096,20 +1099,19 @@ function SignYourSelf() {
|
|||||||
name: defaultdata?.name || position.options?.name || "Cells",
|
name: defaultdata?.name || position.options?.name || "Cells",
|
||||||
cellCount: count,
|
cellCount: count,
|
||||||
defaultValue: (defaultdata?.defaultValue || "").slice(0, count),
|
defaultValue: (defaultdata?.defaultValue || "").slice(0, count),
|
||||||
fontSize: newFontSize || position.options?.fontSize || 12,
|
fontSize: textSize || 12,
|
||||||
fontColor:
|
fontColor: textColor || "black"
|
||||||
newFontColor || position.options?.fontColor || "black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
options: {
|
options: {
|
||||||
...position.options,
|
...position.options,
|
||||||
name: defaultdata?.name || position.options?.name,
|
name: defaultdata?.name || position.options?.name,
|
||||||
fontSize: newFontSize || position.options?.fontSize || 12,
|
fontSize: textSize || 12,
|
||||||
fontColor:
|
fontColor: textColor || "black"
|
||||||
newFontColor || position.options?.fontColor || "black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1119,7 +1121,7 @@ function SignYourSelf() {
|
|||||||
const updateXYposition = xyPosition.map((obj, ind) =>
|
const updateXYposition = xyPosition.map((obj, ind) =>
|
||||||
ind === index ? { ...obj, pos: updatePos } : obj
|
ind === index ? { ...obj, pos: updatePos } : obj
|
||||||
);
|
);
|
||||||
setXyPosition(updateXYposition);
|
setXyPosition(applyNumberFormulasToPages(updateXYposition));
|
||||||
}
|
}
|
||||||
setFontSize();
|
setFontSize();
|
||||||
setFontColor();
|
setFontColor();
|
||||||
@@ -1196,6 +1198,14 @@ function SignYourSelf() {
|
|||||||
setPdfBase64Url(urlDetails.base64);
|
setPdfBase64Url(urlDetails.base64);
|
||||||
setShowRotateAlert({ status: false, degree: 0 });
|
setShowRotateAlert({ status: false, degree: 0 });
|
||||||
};
|
};
|
||||||
|
const WidgetTourConfig = [
|
||||||
|
{
|
||||||
|
selector: '[data-tut="IsSigned"]',
|
||||||
|
content: t("signature-validate-alert-2"),
|
||||||
|
position: "top",
|
||||||
|
style: { fontSize: "13px" }
|
||||||
|
}
|
||||||
|
];
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading.isLoad ? (
|
{isLoading.isLoad ? (
|
||||||
@@ -1241,6 +1251,14 @@ function SignYourSelf() {
|
|||||||
isOpen={signTour}
|
isOpen={signTour}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<Tour
|
||||||
|
showNumber={false}
|
||||||
|
showNavigation={false}
|
||||||
|
showNavigationNumber={false}
|
||||||
|
onRequestClose={() => setWidgetsTour(false)}
|
||||||
|
steps={WidgetTourConfig}
|
||||||
|
isOpen={widgetsTour}
|
||||||
|
/>
|
||||||
{/* this component used to render all pdf pages in left side */}
|
{/* this component used to render all pdf pages in left side */}
|
||||||
<RenderAllPdfPage
|
<RenderAllPdfPage
|
||||||
allPages={allPages}
|
allPages={allPages}
|
||||||
@@ -1408,6 +1426,7 @@ function SignYourSelf() {
|
|||||||
divRef={divRef}
|
divRef={divRef}
|
||||||
currWidgetsDetails={currWidgetsDetails}
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
isShowModal={isShowModal}
|
isShowModal={isShowModal}
|
||||||
|
unSignedWidgetId={unSignedWidgetId}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1459,6 +1478,8 @@ function SignYourSelf() {
|
|||||||
setFontSize={setFontSize}
|
setFontSize={setFontSize}
|
||||||
fontColor={fontColor}
|
fontColor={fontColor}
|
||||||
setFontColor={setFontColor}
|
setFontColor={setFontColor}
|
||||||
|
widgetsSource={xyPosition}
|
||||||
|
isSelfSign={true}
|
||||||
/>
|
/>
|
||||||
<CellsSettingModal
|
<CellsSettingModal
|
||||||
isOpen={isCellsSetting}
|
isOpen={isCellsSetting}
|
||||||
|
|||||||
@@ -662,9 +662,7 @@ const TemplatePlaceholder = () => {
|
|||||||
(data) => data.pageNumber === pageNumber
|
(data) => data.pageNumber === pageNumber
|
||||||
);
|
);
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const addSignPos = getPageNumer?.[0]?.pos?.map((url) => {
|
||||||
const getPosData = getXYdata;
|
|
||||||
const addSignPos = getPosData.map((url) => {
|
|
||||||
if (url.key === keyValue) {
|
if (url.key === keyValue) {
|
||||||
return {
|
return {
|
||||||
...url,
|
...url,
|
||||||
@@ -756,7 +754,7 @@ const TemplatePlaceholder = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pdfDetails?.[0]?.CreatedBy?.objectId !== user?.objectId) return;
|
if (pdfDetails?.[0]?.CreatedBy?.objectId !== user?.objectId || isPrefillModal) return;
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
autosavedetails();
|
autosavedetails();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@@ -1319,10 +1317,9 @@ const TemplatePlaceholder = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const textSize = fontSize || currWidgetsDetails?.options?.fontSize;
|
||||||
|
const textColor = fontColor || currWidgetsDetails?.options?.fontColor;
|
||||||
const getPosData = getXYdata;
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
const addSignPos = getPosData.map((position) => {
|
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (currWidgetsDetails?.type === radioButtonWidget) {
|
if (currWidgetsDetails?.type === radioButtonWidget) {
|
||||||
if (addOption) {
|
if (addOption) {
|
||||||
@@ -1351,12 +1348,8 @@ const TemplatePlaceholder = () => {
|
|||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
isReadOnly: isReadOnly || false,
|
isReadOnly: isReadOnly || false,
|
||||||
isHideLabel: isHideLabel || false,
|
isHideLabel: isHideLabel || false,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1390,12 +1383,8 @@ const TemplatePlaceholder = () => {
|
|||||||
isReadOnly: isReadOnly || false,
|
isReadOnly: isReadOnly || false,
|
||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
isHideLabel: isHideLabel || false,
|
isHideLabel: isHideLabel || false,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1408,12 +1397,8 @@ const TemplatePlaceholder = () => {
|
|||||||
status: status,
|
status: status,
|
||||||
values: dropdownOptions,
|
values: dropdownOptions,
|
||||||
defaultValue: defaultValue,
|
defaultValue: defaultValue,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black",
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black",
|
|
||||||
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
|
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1428,9 +1413,11 @@ const TemplatePlaceholder = () => {
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
|
const recalculatedPlaceholders =
|
||||||
|
utils.applyNumberFormulasToPages(newUpdateSignPos);
|
||||||
const newUpdateSigner = signerPos.map((obj) => {
|
const newUpdateSigner = signerPos.map((obj) => {
|
||||||
if (obj.Id === uniqueId) {
|
if (obj.Id === uniqueId) {
|
||||||
return { ...obj, placeHolder: newUpdateSignPos };
|
return { ...obj, placeHolder: recalculatedPlaceholders };
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
});
|
});
|
||||||
@@ -1463,9 +1450,9 @@ const TemplatePlaceholder = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (getPageNumer.length > 0) {
|
if (getPageNumer.length > 0) {
|
||||||
const getXYdata = getPageNumer[0].pos;
|
const textSize = fontSize || currWidgetsDetails?.options?.fontSize;
|
||||||
const getPosData = getXYdata;
|
const textColor = fontColor || currWidgetsDetails?.options?.fontColor;
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||||||
if (position.key === currWidgetsDetails?.key) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (position.type === textInputWidget) {
|
if (position.type === textInputWidget) {
|
||||||
return {
|
return {
|
||||||
@@ -1479,12 +1466,8 @@ const TemplatePlaceholder = () => {
|
|||||||
isReadOnly: defaultdata?.isReadOnly || false,
|
isReadOnly: defaultdata?.isReadOnly || false,
|
||||||
validation:
|
validation:
|
||||||
{},
|
{},
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (position.type === cellsWidget) {
|
} else if (position.type === cellsWidget) {
|
||||||
@@ -1503,15 +1486,12 @@ const TemplatePlaceholder = () => {
|
|||||||
validation:
|
validation:
|
||||||
{},
|
{},
|
||||||
isReadOnly: defaultdata?.isReadOnly || false,
|
isReadOnly: defaultdata?.isReadOnly || false,
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (["signature"].includes(position.type)) {
|
}
|
||||||
|
else if (["signature"].includes(position.type)) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
options: {
|
options: {
|
||||||
@@ -1529,12 +1509,8 @@ const TemplatePlaceholder = () => {
|
|||||||
status: defaultdata.status,
|
status: defaultdata.status,
|
||||||
defaultValue: defaultdata.defaultValue,
|
defaultValue: defaultdata.defaultValue,
|
||||||
hint: defaultdata?.hint || "",
|
hint: defaultdata?.hint || "",
|
||||||
fontSize:
|
fontSize: textSize || 12,
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontColor: textColor || "black"
|
||||||
fontColor:
|
|
||||||
fontColor ||
|
|
||||||
currWidgetsDetails?.options?.fontColor ||
|
|
||||||
"black"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -2125,6 +2101,8 @@ const TemplatePlaceholder = () => {
|
|||||||
fontColor={fontColor}
|
fontColor={fontColor}
|
||||||
setFontColor={setFontColor}
|
setFontColor={setFontColor}
|
||||||
roleName={roleName}
|
roleName={roleName}
|
||||||
|
widgetsSource={signerPos}
|
||||||
|
activeSignerId={uniqueId}
|
||||||
/>
|
/>
|
||||||
{isPrefillModal && (
|
{isPrefillModal && (
|
||||||
<PrefillWidgetModal
|
<PrefillWidgetModal
|
||||||
@@ -2140,7 +2118,6 @@ const TemplatePlaceholder = () => {
|
|||||||
navigatePageToDoc={navigatePageToDoc}
|
navigatePageToDoc={navigatePageToDoc}
|
||||||
setIsNewContact={setIsNewContact}
|
setIsNewContact={setIsNewContact}
|
||||||
isNewContact={isNewContact}
|
isNewContact={isNewContact}
|
||||||
documentFlow={"template"}
|
|
||||||
docId={pdfDetails[0]?.objectId}
|
docId={pdfDetails[0]?.objectId}
|
||||||
isSubmit={isUseTemplate}
|
isSubmit={isUseTemplate}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "react-i18next";
|
} from "react-i18next";
|
||||||
import DeleteUserModal from "../primitives/DeleteUserModal";
|
import DeleteUserModal from "../primitives/DeleteUserModal";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import PasswordResetModal from "../primitives/PasswordResetModal";
|
||||||
|
|
||||||
const actions = [
|
const actions = [
|
||||||
{
|
{
|
||||||
@@ -23,6 +24,14 @@ const actions = [
|
|||||||
action: "delete",
|
action: "delete",
|
||||||
restrictAdmin: true
|
restrictAdmin: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
btnId: "1910",
|
||||||
|
hoverLabel: "Reset password",
|
||||||
|
btnIcon: "fa-light fa-key",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "resetpassword",
|
||||||
|
restrictAdmin: true
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
||||||
const UserList = () => {
|
const UserList = () => {
|
||||||
@@ -208,9 +217,9 @@ const UserList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// `showAlert` handle show/hide alert
|
// `showAlert` handle show/hide alert
|
||||||
const showAlert = (type, msg) => {
|
const showAlert = (type, msg, timer = 1500) => {
|
||||||
setIsAlert({ type, msg });
|
setIsAlert({ type, msg });
|
||||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
setTimeout(() => setIsAlert({ type: "success", msg: "" }), timer);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteAccount = async (item) => {
|
const handleDeleteAccount = async (item) => {
|
||||||
@@ -247,13 +256,15 @@ const UserList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleActionBtn = async (act, item) => {
|
const handleActionBtn = async (act, item) => {
|
||||||
if (act.action === "delete") {
|
setIsActModal({ [`${act.action}_${item.objectId}`]: true });
|
||||||
setIsActModal({ [`delete_${item.objectId}`]: true });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const handleBtnVisibility = (act, item) => {
|
const handleBtnVisibility = (act, item) => {
|
||||||
if (act.restrictAdmin) {
|
if (act.restrictAdmin) {
|
||||||
return item?.objectId !== extClass?.[0]?.objectId;
|
if (item?.UserRole === "contracts_Admin") {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return item?.objectId !== extClass?.[0]?.objectId;
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
act.restrictBtn === true &&
|
act.restrictBtn === true &&
|
||||||
item?.objectId === extClass?.[0]?.objectId
|
item?.objectId === extClass?.[0]?.objectId
|
||||||
@@ -263,6 +274,28 @@ const UserList = () => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleActiveToggleVisibility = (item) => {
|
||||||
|
if (item?.UserRole === "contracts_Admin") {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return item?.objectId !== extClass?.[0]?.objectId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function submitPassword(userId, password) {
|
||||||
|
setIsLoader(true);
|
||||||
|
setIsActModal({});
|
||||||
|
try {
|
||||||
|
const params = { userId, password };
|
||||||
|
await Parse.Cloud.run("resetpassword", params);
|
||||||
|
showAlert("success", t("password-has-been-reset"));
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err while reset password", err);
|
||||||
|
showAlert("danger", t(err.message), 2000);
|
||||||
|
} finally {
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
@@ -340,7 +373,7 @@ const UserList = () => {
|
|||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2">
|
||||||
{formatRow(item.TeamIds)}
|
{formatRow(item.TeamIds)}
|
||||||
</td>
|
</td>
|
||||||
{item.UserRole !== "contracts_Admin" ? (
|
{handleActiveToggleVisibility(item) ? (
|
||||||
<td className="px-4 py-2 font-semibold">
|
<td className="px-4 py-2 font-semibold">
|
||||||
<label className="cursor-pointer relative block items-center mb-0">
|
<label className="cursor-pointer relative block items-center mb-0">
|
||||||
<input
|
<input
|
||||||
@@ -436,6 +469,17 @@ const UserList = () => {
|
|||||||
deleteRes={deleteUserRes}
|
deleteRes={deleteUserRes}
|
||||||
handleClose={handleCloseModal}
|
handleClose={handleCloseModal}
|
||||||
/>
|
/>
|
||||||
|
<PasswordResetModal
|
||||||
|
isOpen={
|
||||||
|
isActModal[
|
||||||
|
"resetpassword_" + item.objectId
|
||||||
|
]
|
||||||
|
}
|
||||||
|
userId={item?.UserId?.objectId}
|
||||||
|
onClose={handleCloseModal}
|
||||||
|
onSubmit={submitPassword}
|
||||||
|
showAlert={showAlert}
|
||||||
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import ModalUi from "./ModalUi";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PasswordResetModal
|
||||||
|
* - Validates password rules (length >= 8, upper, lower, digit, special)
|
||||||
|
* - Buttons: Submit, Autogenerate (12 chars), Copy (Font Awesome icon only)
|
||||||
|
* - `Autogenerate` fills a valid 12-char password but remains editable
|
||||||
|
* - `Copy` copies the current password to clipboard
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* - isOpen: boolean
|
||||||
|
* - onClose: () => void
|
||||||
|
* - onSubmit: (password: string) => Promise<void> | void
|
||||||
|
*/
|
||||||
|
export default function PasswordResetModal({
|
||||||
|
userId,
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
showAlert
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
// Validation helpers
|
||||||
|
const rules = useMemo(
|
||||||
|
() => ({
|
||||||
|
hasUpper: /[A-Z]/,
|
||||||
|
hasLower: /[a-z]/,
|
||||||
|
hasDigit: /\d/,
|
||||||
|
// Richer special set including common keyboard symbols
|
||||||
|
hasSpecial: /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]/,
|
||||||
|
minLen: 8
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const checks = useMemo(() => {
|
||||||
|
return {
|
||||||
|
lenOK: password.length >= rules.minLen,
|
||||||
|
upperLowerDigitOK:
|
||||||
|
rules.hasUpper.test(password) &&
|
||||||
|
rules.hasLower.test(password) &&
|
||||||
|
rules.hasDigit.test(password),
|
||||||
|
specialOK: rules.hasSpecial.test(password)
|
||||||
|
};
|
||||||
|
}, [password, rules]);
|
||||||
|
|
||||||
|
// Always-present condition list with per-condition status
|
||||||
|
const conditions = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
key: "len",
|
||||||
|
ok: checks.lenOK,
|
||||||
|
text: "password-length"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "uld",
|
||||||
|
ok: checks.upperLowerDigitOK,
|
||||||
|
text: "password-case"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "spec",
|
||||||
|
ok: checks.specialOK,
|
||||||
|
text: "password-special-case"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[checks]
|
||||||
|
);
|
||||||
|
|
||||||
|
const allValid = conditions.every((c) => c.ok) && password.length > 0;
|
||||||
|
|
||||||
|
// Autogenerate a valid random password (12 chars)
|
||||||
|
const handleAutogen = () => {
|
||||||
|
const length = 12;
|
||||||
|
const upper = "ABCDEFGHJKLMNPQRSTUVWXYZ"; // omit ambiguous I/O
|
||||||
|
const lower = "abcdefghijkmnopqrstuvwxyz"; // omit ambiguous l
|
||||||
|
const digits = "23456789"; // omit 0/1
|
||||||
|
const special = "!@#$%^&*()-_=+[]{};:,.?";
|
||||||
|
const all = upper + lower + digits + special;
|
||||||
|
|
||||||
|
function pick(str) {
|
||||||
|
return str[Math.floor(Math.random() * str.length)];
|
||||||
|
}
|
||||||
|
function shuffle(arr) {
|
||||||
|
return arr.sort(() => Math.random() - 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pwd = [pick(upper), pick(lower), pick(digits), pick(special)];
|
||||||
|
while (pwd.length < length) pwd.push(pick(all));
|
||||||
|
pwd = shuffle(pwd).join("");
|
||||||
|
|
||||||
|
setPassword(pwd);
|
||||||
|
};
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(password);
|
||||||
|
showAlert("success", t("copied"), 1200);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false, 1200));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Clipboard error", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e?.preventDefault?.();
|
||||||
|
if (!allValid) return; // guard
|
||||||
|
try {
|
||||||
|
setSubmitting(true);
|
||||||
|
await onSubmit?.(userId, password);
|
||||||
|
setSubmitting(false);
|
||||||
|
onClose?.();
|
||||||
|
setPassword("");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ModalUi
|
||||||
|
isOpen={isOpen}
|
||||||
|
title={t("reset-password")}
|
||||||
|
handleClose={() => {
|
||||||
|
setPassword("");
|
||||||
|
onClose?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-3 pt-[15px] p-[20px]">
|
||||||
|
<p className="text-sm text-base-content/60">
|
||||||
|
{t("enter-strong-password")}
|
||||||
|
</p>
|
||||||
|
<label className="op-form-control w-full">
|
||||||
|
<div className="op-label">
|
||||||
|
<span className="op-label-text">{t("new-password")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
placeholder="Enter password or click Autogenerate"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-square op-btn-sm"
|
||||||
|
onClick={handleCopy}
|
||||||
|
title={copied ? "Copied" : "Copy"}
|
||||||
|
aria-label="Copy password"
|
||||||
|
disabled={!password}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className={`fa-regular fa-copy ${copied ? "opacity-70" : ""}`}
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{/* When everything is valid, show just a right tick */}
|
||||||
|
<ul className="mt-1 ml-2">
|
||||||
|
{conditions.map((c) => (
|
||||||
|
<li
|
||||||
|
key={c.key}
|
||||||
|
className={`${c.ok ? "text-success" : "text-error"} text-[12px] leading-snug`}
|
||||||
|
>
|
||||||
|
{c.ok ? "✓" : "✗"} {t(c.text)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="pt-2 flex flex-row gap-x-2">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
disabled={!allValid || submitting}
|
||||||
|
>
|
||||||
|
{t("submit")}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="op-btn" onClick={handleAutogen}>
|
||||||
|
{t("autogenerate")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost"
|
||||||
|
onClick={() => {
|
||||||
|
setPassword("");
|
||||||
|
onClose?.();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("cancel")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalUi>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
+13
-10
@@ -1,26 +1,29 @@
|
|||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ModalUi from "./ModalUi";
|
import { useLocation, useNavigate } from "react-router";
|
||||||
import { useNavigate } from "react-router";
|
import { useDispatch } from "react-redux";
|
||||||
|
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import ModalUi from "./ModalUi";
|
||||||
|
|
||||||
const ValidateSession = ({ children }) => {
|
const SessionExpiredModal = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleLoginBtn = async () => {
|
const handleLoginBtn = async () => {
|
||||||
try {
|
try {
|
||||||
await Parse?.User?.logOut();
|
await Parse?.User?.logOut();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
console.log(`err: ${err}`);
|
||||||
} finally {
|
} finally {
|
||||||
localStorage.removeItem("accesstoken");
|
localStorage.removeItem("accesstoken");
|
||||||
navigate("/", { replace: true });
|
dispatch(sessionStatus(true));
|
||||||
|
navigate("/", { replace: true, state: { from: location } });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return localStorage.getItem("accesstoken") ? (
|
|
||||||
children
|
return (
|
||||||
) : (
|
|
||||||
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
||||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
||||||
<p className="text-xl font-medium">{t("session-expired")}</p>
|
<p className="text-xl font-medium">{t("session-expired")}</p>
|
||||||
@@ -32,4 +35,4 @@ const ValidateSession = ({ children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ValidateSession;
|
export default SessionExpiredModal;
|
||||||
@@ -14,7 +14,7 @@ const Tooltip = ({
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleOnlickHelp ? handleOnlickHelp() : openInNewTab(url)
|
handleOnlickHelp ? handleOnlickHelp() : openInNewTab(url)
|
||||||
}
|
}
|
||||||
className={"text-center cursor-pointer"}
|
className="text-center cursor-pointer focus:outline-none"
|
||||||
>
|
>
|
||||||
<sup>
|
<sup>
|
||||||
<i
|
<i
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import { Outlet, useNavigate, useLocation } from "react-router";
|
import { Outlet } from "react-router";
|
||||||
import ModalUi from "./ModalUi";
|
import SessionExpiredModal from "./SessionExpiredModal";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
const Validate = () => {
|
const Validate = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const location = useLocation();
|
|
||||||
const [isUserValid, setIsUserValid] = useState(true);
|
const [isUserValid, setIsUserValid] = useState(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -37,30 +34,7 @@ const Validate = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLoginBtn = async () => {
|
return isUserValid ? <Outlet /> : <SessionExpiredModal />;
|
||||||
try {
|
|
||||||
await Parse.User.logOut();
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err ", err);
|
|
||||||
} finally {
|
|
||||||
localStorage.removeItem("accesstoken");
|
|
||||||
navigate("/", { replace: true, state: { from: location } });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return isUserValid ? (
|
|
||||||
<div>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
|
||||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
|
||||||
<p className="text-xl font-medium">{t("session-expired")}</p>
|
|
||||||
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
|
|
||||||
{t("login")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ModalUi>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Validate;
|
export default Validate;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
const initialState = { isValidSession: true };
|
||||||
|
const userReducer = createSlice({
|
||||||
|
name: "user",
|
||||||
|
initialState: initialState,
|
||||||
|
reducers: {
|
||||||
|
sessionStatus: (state, action) => {
|
||||||
|
state.isValidSession = action.payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { sessionStatus } = userReducer.actions;
|
||||||
|
|
||||||
|
export default userReducer.reducer;
|
||||||
@@ -4,6 +4,7 @@ import ShowTenant from "./reducers/ShowTenant";
|
|||||||
import TourStepsReducer from "./reducers/TourStepsReducer";
|
import TourStepsReducer from "./reducers/TourStepsReducer";
|
||||||
import widgetReducer from "./reducers/widgetSlice";
|
import widgetReducer from "./reducers/widgetSlice";
|
||||||
import sidebarReducer from "./reducers/sidebarReducer";
|
import sidebarReducer from "./reducers/sidebarReducer";
|
||||||
|
import userReducer from "./reducers/userReducer";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@@ -11,6 +12,7 @@ export const store = configureStore({
|
|||||||
TourSteps: TourStepsReducer,
|
TourSteps: TourStepsReducer,
|
||||||
ShowTenant,
|
ShowTenant,
|
||||||
widget: widgetReducer,
|
widget: widgetReducer,
|
||||||
sidebar: sidebarReducer
|
sidebar: sidebarReducer,
|
||||||
|
user: userReducer
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,6 +106,15 @@ const DocumentsReport = (props) => {
|
|||||||
}
|
}
|
||||||
}, [props.isSearchResult]);
|
}, [props.isSearchResult]);
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside or on button again
|
||||||
|
useEffect(() => {
|
||||||
|
const onDocClick = (e) => {
|
||||||
|
if (!e.target.closest('[data-dropdown-root="1"]')) setIsOption({});
|
||||||
|
};
|
||||||
|
document.addEventListener("click", onDocClick);
|
||||||
|
return () => document.removeEventListener("click", onDocClick);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const getPaginationRange = () => {
|
const getPaginationRange = () => {
|
||||||
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
|
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
|
||||||
const pages = [];
|
const pages = [];
|
||||||
@@ -1174,6 +1183,7 @@ const DocumentsReport = (props) => {
|
|||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
{handleBtnVisibility(act, item) && (
|
{handleBtnVisibility(act, item) && (
|
||||||
<div
|
<div
|
||||||
|
data-dropdown-root="1"
|
||||||
role="button"
|
role="button"
|
||||||
data-tut={act?.selector}
|
data-tut={act?.selector}
|
||||||
onClick={() => handleActionBtn(act, item)}
|
onClick={() => handleActionBtn(act, item)}
|
||||||
|
|||||||
@@ -165,6 +165,15 @@ const TemplatesReport = (props) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside or on button again
|
||||||
|
useEffect(() => {
|
||||||
|
const onDocClick = (e) => {
|
||||||
|
if (!e.target.closest('[data-dropdown-root="1"]')) setIsOption({});
|
||||||
|
};
|
||||||
|
document.addEventListener("click", onDocClick);
|
||||||
|
return () => document.removeEventListener("click", onDocClick);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// `fetchTeamList` is used to fetch team list for share with functionality
|
// `fetchTeamList` is used to fetch team list for share with functionality
|
||||||
const fetchTeamList = async () => {
|
const fetchTeamList = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1152,6 +1161,7 @@ const TemplatesReport = (props) => {
|
|||||||
role="button"
|
role="button"
|
||||||
data-tut={act?.selector}
|
data-tut={act?.selector}
|
||||||
key={index}
|
key={index}
|
||||||
|
data-dropdown-root="1"
|
||||||
onClick={() => handleActionBtn(act, item)}
|
onClick={() => handleActionBtn(act, item)}
|
||||||
title={t(`btnLabel.${act.hoverLabel}`)}
|
title={t(`btnLabel.${act.hoverLabel}`)}
|
||||||
className={
|
className={
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { generateTitleFromFilename, getBase64FromUrl } from "../constant/Utils";
|
import { generateTitleFromFilename } from "../constant/Utils";
|
||||||
import { base64StringtoFile, uploadFile } from "./fileUtils";
|
import { base64StringtoFile, uploadFile } from "./fileUtils";
|
||||||
import { sanitizeFileName } from "./sanitizeFileName";
|
import { sanitizeFileName } from "./sanitizeFileName";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
@@ -16,22 +16,20 @@ export const saveToMySign = async (widget) => {
|
|||||||
: `${replaceSpace}__initials`;
|
: `${replaceSpace}__initials`;
|
||||||
const file = base64StringtoFile(base64, fileName);
|
const file = base64StringtoFile(base64, fileName);
|
||||||
const fileUrl = await uploadFile(file, User?.id);
|
const fileUrl = await uploadFile(file, User?.id);
|
||||||
// below code is used to save or update default signature, initials, stamp
|
const params = {
|
||||||
const signCls = new Parse.Object("contracts_Signature");
|
id: widget?.defaultSignId, // pass id to update existing signature/initials
|
||||||
if (widget?.defaultSignId) {
|
userId: User.id
|
||||||
signCls.id = widget.defaultSignId;
|
};
|
||||||
}
|
|
||||||
if (widget?.type === "initials") {
|
if (widget?.type === "initials") {
|
||||||
signCls.set("Initials", fileUrl);
|
params.initials = fileUrl; // save initials image url
|
||||||
} else if (widget?.type === "signature") {
|
} else if (widget?.type === "signature") {
|
||||||
signCls.set("ImageURL", fileUrl);
|
params.signature = fileUrl; // save signature image url
|
||||||
}
|
}
|
||||||
signCls.set("UserId", Parse.User.createWithoutData(User.id));
|
const signRes = await Parse.Cloud.run("savesignature", params);
|
||||||
const signRes = await signCls.save();
|
|
||||||
return { base64File: base64, id: signRes?.id };
|
return { base64File: base64, id: signRes?.id };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Err while saving signature", err);
|
console.log("Err while saving signature", err);
|
||||||
return url;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -41,3 +39,40 @@ export const hasSignatureWidget = (s) =>
|
|||||||
(s.placeHolder ?? []).some((ph) =>
|
(s.placeHolder ?? []).some((ph) =>
|
||||||
(ph?.pos ?? []).some((p) => p?.type === "signature")
|
(ph?.pos ?? []).some((p) => p?.type === "signature")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Array<object>} pages
|
||||||
|
* Array of page objects. Each page is expected to have:
|
||||||
|
* - pos: Array<Widget> where Widget has shape:
|
||||||
|
* {
|
||||||
|
* key: string,
|
||||||
|
* type: string,
|
||||||
|
* options?: {
|
||||||
|
* response?: number|string,
|
||||||
|
* defaultValue?: number|string,
|
||||||
|
* formula?: string
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @returns {Array<object>}
|
||||||
|
*/
|
||||||
|
export const applyNumberFormulasToPages = (pages = []) => {
|
||||||
|
// Guard: if not an array or empty, just return as-is
|
||||||
|
if (!Array.isArray(pages) || pages.length === 0) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone pages and each widget.options so we do not mutate caller's data.
|
||||||
|
// (Shallow clone is sufficient for our current usage since we only touch `options`.)
|
||||||
|
const clonedPages = pages.map((page) => ({
|
||||||
|
...page,
|
||||||
|
pos: (page?.pos || []).map((widget) => ({
|
||||||
|
...widget,
|
||||||
|
options: { ...widget.options }
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Return the cloned, updated pages
|
||||||
|
return clonedPages;
|
||||||
|
};
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const resetPasswordAndDeleteSession = async userId => {
|
|||||||
await Parse.Object.destroyAll(sessions, { useMasterKey: true });
|
await Parse.Object.destroyAll(sessions, { useMasterKey: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export async function deleteUser(userId, adminId) {
|
export async function deleteUser(userId, adminId, adminTenantId, isOrgAdmin, orgPtr) {
|
||||||
const userPointer = { __type: 'Pointer', className: '_User', objectId: userId };
|
const userPointer = { __type: 'Pointer', className: '_User', objectId: userId };
|
||||||
let userDetails = {
|
let userDetails = {
|
||||||
UserRole: 'not found',
|
UserRole: 'not found',
|
||||||
@@ -47,13 +47,18 @@ export async function deleteUser(userId, adminId) {
|
|||||||
const Users = Parse.Object.extend('contracts_Users');
|
const Users = Parse.Object.extend('contracts_Users');
|
||||||
const userQuery = new Parse.Query(Users);
|
const userQuery = new Parse.Query(Users);
|
||||||
userQuery.equalTo('UserId', userPointer);
|
userQuery.equalTo('UserId', userPointer);
|
||||||
if (adminId) {
|
if (adminTenantId) {
|
||||||
|
userQuery.equalTo('TenantId', adminTenantId);
|
||||||
|
if (isOrgAdmin && orgPtr) {
|
||||||
|
userQuery.equalTo('OrganizationId', orgPtr);
|
||||||
|
}
|
||||||
|
} else if (adminId) {
|
||||||
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: adminId });
|
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: adminId });
|
||||||
}
|
}
|
||||||
const userResult = await userQuery.first({ useMasterKey: true });
|
const userResult = await userQuery.first({ useMasterKey: true });
|
||||||
userDetails = { ...userDetails, UserId: userId };
|
userDetails = { ...userDetails, UserId: userId };
|
||||||
if (!userResult) {
|
if (!userResult) {
|
||||||
const errorMessage = 'User not found.';
|
const errorMessage = isOrgAdmin ? 'Unauthorized.' : 'User not found.';
|
||||||
return { code: 400, message: errorMessage };
|
return { code: 400, message: errorMessage };
|
||||||
}
|
}
|
||||||
const contractsUserId = userResult.id;
|
const contractsUserId = userResult.id;
|
||||||
@@ -61,6 +66,10 @@ export async function deleteUser(userId, adminId) {
|
|||||||
const teamIds = userResult.get('TeamIds') || [];
|
const teamIds = userResult.get('TeamIds') || [];
|
||||||
const organizationId = userResult.get('OrganizationId')?.id;
|
const organizationId = userResult.get('OrganizationId')?.id;
|
||||||
const isAdmin = userResult?.get('UserRole') === 'contracts_Admin' ? true : false;
|
const isAdmin = userResult?.get('UserRole') === 'contracts_Admin' ? true : false;
|
||||||
|
if (isOrgAdmin && isAdmin) {
|
||||||
|
const errorMessage = 'Unauthorized.';
|
||||||
|
return { code: 400, message: errorMessage };
|
||||||
|
}
|
||||||
userDetails = {
|
userDetails = {
|
||||||
...userDetails,
|
...userDetails,
|
||||||
UserRole: userResult?.get('UserRole'),
|
UserRole: userResult?.get('UserRole'),
|
||||||
@@ -267,6 +276,21 @@ export const deleteUserPost = async (req, res) => {
|
|||||||
return res.send(errorMessage);
|
return res.send(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extUser?.get('UserRole') !== 'contracts_Admin') {
|
||||||
|
const errorMessage =
|
||||||
|
'This action is not permitted. Kindly contact your administrator to request account deletion.';
|
||||||
|
return res.send(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const extUsers = new Parse.Query('contracts_Users');
|
||||||
|
extUsers.equalTo('TenantId', extUser?.get('TenantId'));
|
||||||
|
extUsers.notEqualTo('UserRole', 'contracts_Admin');
|
||||||
|
const isTeamUsers = await extUsers.first({ useMasterKey: true });
|
||||||
|
if (isTeamUsers) {
|
||||||
|
const errorMessage = `To delete this account, start by removing all team users associated with it. Once all users are removed, you'll be able to permanently delete the account.`;
|
||||||
|
return res.send(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
// Get stored OTP info
|
// Get stored OTP info
|
||||||
const savedOtp = extUser.get('DeleteOTP') || '';
|
const savedOtp = extUser.get('DeleteOTP') || '';
|
||||||
const expiry = extUser.get('DeleteOTPExpiry');
|
const expiry = extUser.get('DeleteOTPExpiry');
|
||||||
@@ -352,7 +376,30 @@ export const deleteUserByAdmin = async (req, res) => {
|
|||||||
const errorMessage = 'User not found.';
|
const errorMessage = 'User not found.';
|
||||||
return res.status(400).json({ message: errorMessage });
|
return res.status(400).json({ message: errorMessage });
|
||||||
}
|
}
|
||||||
const response = await deleteUser(userId, adminId);
|
|
||||||
|
if (adminId === userId) {
|
||||||
|
return res.status(400).json({ message: 'You cannot delete your own account.' });
|
||||||
|
}
|
||||||
|
// 2. ext user details
|
||||||
|
const extUserQuery = new Parse.Query('contracts_Users');
|
||||||
|
extUserQuery.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: adminId });
|
||||||
|
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||||
|
if (!extUser) {
|
||||||
|
const errorMessage = 'User not found.';
|
||||||
|
return res.status(400).json({ message: errorMessage });
|
||||||
|
}
|
||||||
|
const isAdmin =
|
||||||
|
extUser?.get('UserRole') === 'contracts_Admin' ||
|
||||||
|
extUser?.get('UserRole') === 'contracts_OrgAdmin'
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
const isOrgAdmin = extUser?.get('UserRole') === 'contracts_OrgAdmin';
|
||||||
|
const tenantId = extUser?.get('TenantId');
|
||||||
|
const orgPtr = isOrgAdmin && extUser?.get('OrganizationId');
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(400).json({ message: 'Unauthorized.' });
|
||||||
|
}
|
||||||
|
const response = await deleteUser(userId, adminId, tenantId, isOrgAdmin, orgPtr);
|
||||||
const code = response?.code || 400;
|
const code = response?.code || 400;
|
||||||
const message = response?.message || 'An error occurred while deleting your account.';
|
const message = response?.message || 'An error occurred while deleting your account.';
|
||||||
return res.status(code).json({ message: message });
|
return res.status(code).json({ message: message });
|
||||||
|
|||||||
@@ -1,169 +1,149 @@
|
|||||||
import fs from 'node:fs';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
import libre from 'libreoffice-convert';
|
import libre from 'libreoffice-convert';
|
||||||
import util from 'node:util';
|
|
||||||
import { cloudServerUrl, getSecureUrl, serverAppId } from '../../Utils.js';
|
import { cloudServerUrl, getSecureUrl, serverAppId } from '../../Utils.js';
|
||||||
|
|
||||||
libre.convertAsync = util.promisify(libre.convert);
|
/** @returns {Promise<Buffer>} */
|
||||||
|
async function convertLibre(input, ext, opts) {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
libre.convert(input, ext, opts, (err, out) => (err ? reject(err) : resolve(out)));
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// -------------------- Concurrency limiter (no dependency) --------------------
|
||||||
|
const MAX_CONCURRENCY = Number(process.env.DOCX2PDF_CONCURRENCY || 2); // 1 for tiny droplets
|
||||||
|
let active = 0;
|
||||||
|
const queue = [];
|
||||||
|
function runWithLimit(task) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const run = () => {
|
||||||
|
active++;
|
||||||
|
Promise.resolve()
|
||||||
|
.then(task)
|
||||||
|
.then(resolve, reject)
|
||||||
|
.finally(() => {
|
||||||
|
active--;
|
||||||
|
if (queue.length) queue.shift()();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (active < MAX_CONCURRENCY) run();
|
||||||
|
else queue.push(run);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const storage = multer.diskStorage({
|
// -------------------- Timeout helper (async version = no TS hint) --------------------
|
||||||
destination(req, file, cb) {
|
/**
|
||||||
cb(null, 'exports');
|
* @template T
|
||||||
},
|
* @param {Promise<T>} promise
|
||||||
filename(req, file, cb) {
|
* @param {number} ms
|
||||||
cb(null, file.originalname);
|
* @param {string} [label='operation']
|
||||||
|
* @returns {Promise<T>}
|
||||||
|
*/
|
||||||
|
export async function withTimeout(promise, ms, label = 'operation') {
|
||||||
|
let timer;
|
||||||
|
try {
|
||||||
|
const timeout = new Promise((_, reject) => {
|
||||||
|
timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
||||||
|
});
|
||||||
|
return await Promise.race([promise, timeout]);
|
||||||
|
} finally {
|
||||||
|
if (timer) clearTimeout(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Multer: use memory storage --------------------
|
||||||
|
const storage = multer.memoryStorage();
|
||||||
|
export const upload = multer({
|
||||||
|
storage,
|
||||||
|
fileFilter: (req, file, cb) => {
|
||||||
|
const okExt = /\.docx$/i.test(file.originalname || '');
|
||||||
|
const okMime =
|
||||||
|
file.mimetype === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
|
||||||
|
file.mimetype === 'application/octet-stream';
|
||||||
|
if (okExt && okMime) return cb(null, true);
|
||||||
|
cb(new Error('Only .docx files are supported'));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const upload = multer({ storage });
|
|
||||||
|
|
||||||
function generatePdfName(length) {
|
function generatePdfName(length) {
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
let result = '';
|
let result = '';
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function docxtopdf(req, res) {
|
export default async function docxtopdf(req, res) {
|
||||||
const serverUrl = cloudServerUrl;
|
const serverUrl = cloudServerUrl;
|
||||||
const appId = serverAppId;
|
const parseAppKey = { 'X-Parse-Application-Id': serverAppId };
|
||||||
const masterKey = process.env.MASTER_KEY;
|
const masterKey = process.env.MASTER_KEY;
|
||||||
const inputPath = req.file.path;
|
|
||||||
const name = generatePdfName(16);
|
const masterHeader = { ...parseAppKey, 'X-Parse-Master-Key': masterKey };
|
||||||
const fileName = `${name}.pdf`;
|
const sessionHeader = { ...parseAppKey, 'X-Parse-Session-Token': req.headers['sessiontoken'] };
|
||||||
const outputPath = './exports/output.pdf';
|
|
||||||
|
if (!req.file || !req.file.buffer) {
|
||||||
|
return res.status(400).json({ error: 'No file uploaded.' });
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
// ---- Auth: current user ----
|
||||||
headers: {
|
const userRes = await axios.get(`${serverUrl}/users/me`, { headers: sessionHeader });
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Session-Token': req.headers['sessiontoken'],
|
// ---- contracts_Users ----
|
||||||
},
|
const whereUser = JSON.stringify({
|
||||||
});
|
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.data.objectId },
|
||||||
const userId = JSON.stringify({
|
|
||||||
UserId: {
|
|
||||||
__type: 'Pointer',
|
|
||||||
className: '_User',
|
|
||||||
objectId: userRes.data.objectId,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const resUser = await axios.get(
|
const resUser = await axios.get(
|
||||||
serverUrl + `/classes/contracts_Users?where=${userId}&limit=1&include=TenantId`,
|
`${serverUrl}/classes/contracts_Users?where=${whereUser}&limit=1&include=TenantId`,
|
||||||
{
|
{ headers: masterHeader }
|
||||||
headers: {
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Master-Key': masterKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resUser?.data?.results?.length > 0) {
|
if (!resUser?.data?.results?.length) {
|
||||||
const tenantId = resUser.data.results[0].TenantId?.objectId;
|
return res.status(403).json({ error: 'User not linked to tenant.' });
|
||||||
const ext = '.pdf';
|
|
||||||
const outPath = `./exports/output${ext}`;
|
|
||||||
const docxBuf = fs.readFileSync(inputPath);
|
|
||||||
const pdfBuffer = await libre.convertAsync(docxBuf, ext, undefined);
|
|
||||||
fs.writeFileSync(outPath, pdfBuffer);
|
|
||||||
const file = fs.readFileSync(outPath);
|
|
||||||
const size = fs.statSync(outPath).size;
|
|
||||||
const PartnersTenant = JSON.stringify({
|
|
||||||
PartnersTenant: {
|
|
||||||
__type: 'Pointer',
|
|
||||||
className: 'partners_Tenant',
|
|
||||||
objectId: tenantId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const resTenantCredit = await axios.get(
|
|
||||||
serverUrl + `/classes/partners_TenantCredits?where=${PartnersTenant}&limit=1`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Master-Key': masterKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (resTenantCredit.data?.results?.length > 0) {
|
|
||||||
const tenantCreditsId = resTenantCredit.data.results[0].objectId;
|
|
||||||
const activeFileAdapter = resUser.data.results[0].TenantId?.ActiveFileAdapter;
|
|
||||||
let fileUrl;
|
|
||||||
if (activeFileAdapter) {
|
|
||||||
const params = {
|
|
||||||
fileBase64: file.toString('base64'),
|
|
||||||
fileName,
|
|
||||||
id: activeFileAdapter,
|
|
||||||
};
|
|
||||||
const url = serverUrl + '/functions/savetofileadapter';
|
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Session-Token': req.headers['sessiontoken'],
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
const savetos3 = await axios.post(url, params, { headers });
|
|
||||||
fileUrl = savetos3?.data?.result?.url;
|
|
||||||
} catch (err) {
|
|
||||||
console.log('err in save to customfile', err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const parsefile = await axios.post(serverUrl + `/files/${fileName}`, file, {
|
|
||||||
headers: {
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Master-Key': masterKey,
|
|
||||||
'Content-Type': 'application/pdf',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const fileRes = getSecureUrl(parsefile.data.url);
|
|
||||||
fileUrl = fileRes.url;
|
|
||||||
}
|
|
||||||
const usedStorage = resTenantCredit.data.results[0].usedStorage
|
|
||||||
? resTenantCredit.data.results[0].usedStorage + size
|
|
||||||
: size;
|
|
||||||
await axios.put(
|
|
||||||
serverUrl + `/classes/partners_TenantCredits/${tenantCreditsId}`,
|
|
||||||
{ usedStorage },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Master-Key': masterKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await axios.post(
|
|
||||||
serverUrl + '/classes/partners_DataFiles',
|
|
||||||
{
|
|
||||||
FileSize: size,
|
|
||||||
FileUrl: fileUrl,
|
|
||||||
TenantPtr: {
|
|
||||||
__type: 'Pointer',
|
|
||||||
className: 'partners_Tenant',
|
|
||||||
objectId: tenantId,
|
|
||||||
},
|
|
||||||
UserId: {
|
|
||||||
__type: 'Pointer',
|
|
||||||
className: '_User',
|
|
||||||
objectId: userRes.data.objectId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-Parse-Application-Id': appId,
|
|
||||||
'X-Parse-Master-Key': masterKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
[inputPath, outPath].forEach(p => fs.existsSync(p) && fs.unlinkSync(p));
|
|
||||||
return res.status(200).json({ message: 'success.', url: fileUrl });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tenantId = resUser.data.results[0]?.TenantId?.objectId;
|
||||||
|
if (!tenantId) {
|
||||||
|
return res.status(403).json({ error: 'Tenant not found for user.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- DOCX -> PDF (buffer -> buffer), with concurrency + timeout ----
|
||||||
|
const fileName = `${generatePdfName(16)}.pdf`;
|
||||||
|
|
||||||
|
const pdfBuffer = await runWithLimit(() =>
|
||||||
|
withTimeout(convertLibre(req.file.buffer, '.pdf', undefined), 60_000, 'DOCX->PDF')
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---- Upload PDF (no disk IO) ----
|
||||||
|
const activeFileAdapter = resUser.data.results[0]?.TenantId?.ActiveFileAdapter;
|
||||||
|
let fileUrl;
|
||||||
|
if (activeFileAdapter) {
|
||||||
|
const params = {
|
||||||
|
fileBase64: Buffer.from(pdfBuffer).toString('base64'),
|
||||||
|
fileName,
|
||||||
|
id: activeFileAdapter,
|
||||||
|
};
|
||||||
|
const url = serverUrl + '/functions/savetofileadapter';
|
||||||
|
const headers = { 'Content-Type': 'application/json', ...sessionHeader };
|
||||||
|
const savetos3 = await axios.post(url, params, { headers });
|
||||||
|
fileUrl = savetos3?.data?.result?.url;
|
||||||
|
if (!fileUrl) throw new Error('No URL returned from file adapter');
|
||||||
|
} else {
|
||||||
|
const parsefile = await axios.post(`${serverUrl}/files/${fileName}`, pdfBuffer, {
|
||||||
|
headers: { ...masterHeader, 'Content-Type': 'application/pdf' },
|
||||||
|
});
|
||||||
|
const fileRes = getSecureUrl(parsefile.data.url);
|
||||||
|
fileUrl = fileRes.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({ message: 'success.', url: fileUrl });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
[inputPath, outputPath].forEach(p => fs.existsSync(p) && fs.unlinkSync(p));
|
|
||||||
const msg =
|
const msg =
|
||||||
err?.response?.data?.error || err?.response?.data || err?.message || 'Something went wrong.';
|
err?.response?.data?.error || err?.response?.data || err?.message || 'Something went wrong.';
|
||||||
console.log(`Error converting file: ${msg}`);
|
console.log(`Error in docxtopdf: ${msg}`);
|
||||||
|
// Friendly message to the client
|
||||||
const message =
|
const message =
|
||||||
'We are currently experiencing some issues with processing DOCX files. Please upload the PDF version or contact us on support@opensignlabs.com';
|
'We are currently experiencing some issues with processing DOCX files. Please upload the PDF version or contact us on support@opensignlabs.com';
|
||||||
return res.status(400).json({ error: message });
|
return res.status(400).json({ error: message });
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ import loginUser from './parsefunction/loginUser.js';
|
|||||||
import addUser from './parsefunction/addUser.js';
|
import addUser from './parsefunction/addUser.js';
|
||||||
import filterDocs from './parsefunction/filterDocs.js';
|
import filterDocs from './parsefunction/filterDocs.js';
|
||||||
import sendDeleteUserMail from './parsefunction/sendDeleteUserMail.js';
|
import sendDeleteUserMail from './parsefunction/sendDeleteUserMail.js';
|
||||||
|
import resetPassword from './parsefunction/resetPassword.js';
|
||||||
|
import saveSignature from './parsefunction/saveSignature.js';
|
||||||
|
import manageSign from './parsefunction/manageSign.js';
|
||||||
|
import getSignature from './parsefunction/getSignature.js';
|
||||||
|
|
||||||
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||||
@@ -122,3 +126,7 @@ Parse.Cloud.define('loginuser', loginUser);
|
|||||||
Parse.Cloud.define('adduser', addUser);
|
Parse.Cloud.define('adduser', addUser);
|
||||||
Parse.Cloud.define('filterdocs', filterDocs);
|
Parse.Cloud.define('filterdocs', filterDocs);
|
||||||
Parse.Cloud.define('senddeleterequest', sendDeleteUserMail);
|
Parse.Cloud.define('senddeleterequest', sendDeleteUserMail);
|
||||||
|
Parse.Cloud.define('resetpassword', resetPassword);
|
||||||
|
Parse.Cloud.define('savesignature', saveSignature);
|
||||||
|
Parse.Cloud.define('managesign', manageSign);
|
||||||
|
Parse.Cloud.define('getdefaultsignature', getSignature);
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export default async function getSignature(request) {
|
||||||
|
const { userId } = request.params;
|
||||||
|
if (!userId) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
|
||||||
|
}
|
||||||
|
if (userId !== request.user?.id) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot save signature for the current user.');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const query = new Parse.Query('contracts_Signature');
|
||||||
|
query.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||||
|
const result = await query.first({ useMasterKey: true });
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching signature:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
export default async function manageSign(request) {
|
||||||
|
const { signature, userId, initials, id, title } = request.params;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
|
||||||
|
}
|
||||||
|
if (userId !== request.user?.id) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot save signature for the current user.');
|
||||||
|
}
|
||||||
|
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
|
||||||
|
try {
|
||||||
|
const signatureCls = new Parse.Object('contracts_Signature');
|
||||||
|
if (id) {
|
||||||
|
signatureCls.id = id;
|
||||||
|
}
|
||||||
|
signatureCls.set('Initials', initials ? initials : '');
|
||||||
|
signatureCls.set('ImageURL', signature ? signature : '');
|
||||||
|
signatureCls.set('SignatureName', title ? title : '');
|
||||||
|
if (userPtr) {
|
||||||
|
signatureCls.set('UserId', userPtr);
|
||||||
|
}
|
||||||
|
const signRes = await signatureCls.save(null, { useMasterKey: true });
|
||||||
|
return signRes;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error saving signature:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
export default async function resetPassword(request) {
|
||||||
|
const userId = request.params.userId;
|
||||||
|
const newPassword = request.params.password;
|
||||||
|
|
||||||
|
if (!userId || !newPassword) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide required parameters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request.user) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.user.id === userId) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Unauthorized to reset your own password.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Get Admin User TenantId
|
||||||
|
const adminUserQuery = new Parse.Query('contracts_Users');
|
||||||
|
adminUserQuery.equalTo('UserId', request.user);
|
||||||
|
const adminUser = await adminUserQuery.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
const tenantId = adminUser?.get('TenantId');
|
||||||
|
if (!tenantId) {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Admin user tenant not found.');
|
||||||
|
}
|
||||||
|
const isAdmin =
|
||||||
|
adminUser.get('UserRole') === 'contracts_Admin' ||
|
||||||
|
adminUser.get('UserRole') === 'contracts_OrgAdmin';
|
||||||
|
if (!isAdmin) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Unauthorized.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Verify the user belongs to the same tenant and is not an admin
|
||||||
|
const targetUserQuery = new Parse.Query('contracts_Users');
|
||||||
|
targetUserQuery.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||||
|
targetUserQuery.equalTo('TenantId', tenantId);
|
||||||
|
targetUserQuery.notEqualTo('UserRole', 'contracts_Admin');
|
||||||
|
const targetUser = await targetUserQuery.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
if (!targetUser) {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found or not allowed.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update user's password
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
userQuery.equalTo('objectId', userId);
|
||||||
|
const user = await userQuery.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.set('password', newPassword);
|
||||||
|
await user.save(null, { useMasterKey: true });
|
||||||
|
|
||||||
|
return { status: 'success', message: 'Password has been reset.' };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error while resetting password:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ export default async function saveAsTemplate(request) {
|
|||||||
options: {
|
options: {
|
||||||
...p.options,
|
...p.options,
|
||||||
status: 'required',
|
status: 'required',
|
||||||
|
response: '',
|
||||||
...(p?.options?.defaultValue ? { defaultValue: '' } : {}),
|
...(p?.options?.defaultValue ? { defaultValue: '' } : {}),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
export default async function saveSignature(request) {
|
||||||
|
const { signature, userId, initials, id, title } = request.params;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
|
||||||
|
}
|
||||||
|
if (userId !== request.user?.id) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot save signature for the current user.');
|
||||||
|
}
|
||||||
|
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
|
||||||
|
try {
|
||||||
|
const signatureCls = new Parse.Object('contracts_Signature');
|
||||||
|
if (id) {
|
||||||
|
signatureCls.id = id;
|
||||||
|
}
|
||||||
|
if (initials) {
|
||||||
|
signatureCls.set('Initials', initials);
|
||||||
|
}
|
||||||
|
if (signature) {
|
||||||
|
signatureCls.set('ImageURL', signature);
|
||||||
|
}
|
||||||
|
if (title) {
|
||||||
|
signatureCls.set('SignatureName', title);
|
||||||
|
}
|
||||||
|
if (userPtr) {
|
||||||
|
signatureCls.set('UserId', userPtr);
|
||||||
|
}
|
||||||
|
const signRes = await signatureCls.save(null, { useMasterKey: true });
|
||||||
|
return signRes;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,15 +12,23 @@ async function sendMailProvider(req, plan, monthchange) {
|
|||||||
let mailgunClient;
|
let mailgunClient;
|
||||||
let mailgunDomain;
|
let mailgunDomain;
|
||||||
if (smtpenable) {
|
if (smtpenable) {
|
||||||
transporterSMTP = createTransport({
|
let transporterConfig = {
|
||||||
host: process.env.SMTP_HOST,
|
host: process.env.SMTP_HOST,
|
||||||
port: process.env.SMTP_PORT || 465,
|
port: process.env.SMTP_PORT || 465,
|
||||||
secure: smtpsecure,
|
secure: smtpsecure,
|
||||||
auth: {
|
};
|
||||||
|
|
||||||
|
// ✅ Add auth only if BOTH username & password exist
|
||||||
|
const smtpUser = process.env.SMTP_USERNAME;
|
||||||
|
const smtpPass = process.env.SMTP_PASS;
|
||||||
|
|
||||||
|
if (smtpUser && smtpPass) {
|
||||||
|
transporterConfig.auth = {
|
||||||
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
||||||
pass: process.env.SMTP_PASS,
|
pass: smtpPass,
|
||||||
},
|
};
|
||||||
});
|
}
|
||||||
|
transporterSMTP = createTransport(transporterConfig);
|
||||||
} else {
|
} else {
|
||||||
if (mailgunApiKey) {
|
if (mailgunApiKey) {
|
||||||
const mailgun = new Mailgun(formData);
|
const mailgun = new Mailgun(formData);
|
||||||
@@ -185,7 +193,7 @@ async function sendMailProvider(req, plan, monthchange) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err in sendmailv3', err);
|
console.log(`Error in sendmailv3: ${err}`);
|
||||||
if (fs.existsSync(testPdf)) {
|
if (fs.existsSync(testPdf)) {
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(testPdf);
|
fs.unlinkSync(testPdf);
|
||||||
@@ -236,7 +244,7 @@ async function sendMailProvider(req, plan, monthchange) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err in sendmailv3', err);
|
console.log(`Error in sendmailv3: ${err}`);
|
||||||
if (err) {
|
if (err) {
|
||||||
return { status: 'error' };
|
return { status: 'error' };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,20 +61,28 @@ let mailgunDomain;
|
|||||||
let isMailAdapter = false;
|
let isMailAdapter = false;
|
||||||
if (smtpenable) {
|
if (smtpenable) {
|
||||||
try {
|
try {
|
||||||
transporterMail = createTransport({
|
let transporterConfig = {
|
||||||
host: process.env.SMTP_HOST,
|
host: process.env.SMTP_HOST,
|
||||||
port: process.env.SMTP_PORT || 465,
|
port: process.env.SMTP_PORT || 465,
|
||||||
secure: smtpsecure,
|
secure: smtpsecure,
|
||||||
auth: {
|
};
|
||||||
|
|
||||||
|
// ✅ Add auth only if BOTH username & password exist
|
||||||
|
const smtpUser = process.env.SMTP_USERNAME;
|
||||||
|
const smtpPass = process.env.SMTP_PASS;
|
||||||
|
|
||||||
|
if (smtpUser && smtpPass) {
|
||||||
|
transporterConfig.auth = {
|
||||||
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
||||||
pass: process.env.SMTP_PASS,
|
pass: smtpPass,
|
||||||
},
|
};
|
||||||
});
|
}
|
||||||
|
transporterMail = createTransport(transporterConfig);
|
||||||
await transporterMail.verify();
|
await transporterMail.verify();
|
||||||
isMailAdapter = true;
|
isMailAdapter = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
isMailAdapter = false;
|
isMailAdapter = false;
|
||||||
console.log('Please provide valid SMTP credentials');
|
console.log(`Please provide valid SMTP credentials: ${err}`);
|
||||||
}
|
}
|
||||||
} else if (process.env.MAILGUN_API_KEY) {
|
} else if (process.env.MAILGUN_API_KEY) {
|
||||||
try {
|
try {
|
||||||
@@ -110,6 +118,7 @@ export const config = {
|
|||||||
appName: appName,
|
appName: appName,
|
||||||
allowClientClassCreation: false,
|
allowClientClassCreation: false,
|
||||||
allowExpiredAuthDataToken: false,
|
allowExpiredAuthDataToken: false,
|
||||||
|
enableInsecureAuthAdapters: false,
|
||||||
encodeParseObjectInCloudFunction: true,
|
encodeParseObjectInCloudFunction: true,
|
||||||
...(isMailAdapter === true
|
...(isMailAdapter === true
|
||||||
? {
|
? {
|
||||||
|
|||||||
Generated
+2763
-2607
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "open_sign_server",
|
"name": "open_sign_server",
|
||||||
"version": "2.26.0",
|
"version": "2.30.0",
|
||||||
"description": "An example Parse API server using the parse-server module",
|
"description": "opensign server using the parse-server module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ParsePlatform/parse-server-example"
|
"url": "https://github.com/OpenSignLabs/OpenSign"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
"watch": "nodemon index.js"
|
"watch": "nodemon index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.891.0",
|
"@aws-sdk/client-s3": "^3.901.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.891.0",
|
"@aws-sdk/s3-request-presigner": "^3.901.0",
|
||||||
"@parse/fs-files-adapter": "^3.0.0",
|
"@parse/fs-files-adapter": "^3.0.0",
|
||||||
"@parse/s3-files-adapter": "^4.2.0",
|
"@parse/s3-files-adapter": "^4.2.0",
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
"@pdf-lib/fontkit": "^1.1.1",
|
||||||
@@ -31,27 +31,27 @@
|
|||||||
"coherentpdf": "^2.5.5",
|
"coherentpdf": "^2.5.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
"dotenv": "^17.2.2",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.4",
|
||||||
"generate-api-key": "^1.0.2",
|
"generate-api-key": "^1.0.2",
|
||||||
"googleapis": "^159.0.0",
|
"googleapis": "^161.0.0",
|
||||||
"libreoffice-convert": "^1.7.0",
|
"libreoffice-convert": "^1.7.0",
|
||||||
"mailgun.js": "^12.0.3",
|
"mailgun.js": "^12.1.0",
|
||||||
"mongodb": "^6.19.0",
|
"mongodb": "^5.9.2",
|
||||||
"multer": "^2.0.2",
|
"multer": "^2.0.2",
|
||||||
"multer-s3": "^3.0.1",
|
"multer-s3": "^3.0.1",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"nodemailer": "^7.0.6",
|
"nodemailer": "^7.0.9",
|
||||||
"p-limit": "^7.1.1",
|
"p-limit": "^7.1.1",
|
||||||
"parse": "^6.1.1",
|
"parse": "^6.1.1",
|
||||||
"parse-dbtool": "^1.2.0",
|
"parse-dbtool": "^1.2.0",
|
||||||
"parse-server": "^8.2.4",
|
"parse-server": "^8.2.5",
|
||||||
"parse-server-api-mail-adapter": "^4.1.0",
|
"parse-server-api-mail-adapter": "^4.1.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"posthog-node": "^5.8.4",
|
"posthog-node": "^5.9.3",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"rate-limiter-flexible": "^7.3.1",
|
"rate-limiter-flexible": "^8.0.1",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.28.4",
|
"@babel/eslint-parser": "^7.28.4",
|
||||||
"eslint": "^9.35.0",
|
"eslint": "^9.37.0",
|
||||||
"jasmine": "^5.10.0",
|
"jasmine": "^5.12.0",
|
||||||
"mongodb-runner": "^5.9.3",
|
"mongodb-runner": "^5.10.0",
|
||||||
"nodemon": "^3.1.10",
|
"nodemon": "^3.1.10",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^17.1.0",
|
||||||
"prettier": "^3.6.2"
|
"prettier": "^3.6.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user