mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
Merge pull request #2060 from OpenSignLabs/staging
This commit is contained in:
+22
-23
@@ -1,58 +1,57 @@
|
||||
{
|
||||
"name": "open_sign",
|
||||
"version": "2.32.0",
|
||||
"version": "2.34.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@imgly/background-removal": "^1.7.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.17.8",
|
||||
"@lottiefiles/dotlottie-react": "^0.17.10",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
"@reduxjs/toolkit": "^2.11.0",
|
||||
"@reactour/tour": "^3.8.0",
|
||||
"@reduxjs/toolkit": "^2.11.2",
|
||||
"axios": "^1.13.2",
|
||||
"body-scroll-lock": "^4.0.0-beta.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^25.7.1",
|
||||
"i18next": "^25.7.3",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"parse": "^7.1.2",
|
||||
"parse": "^8.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pkijs": "^3.3.3",
|
||||
"print-js": "^1.6.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"quill-html-edit-button": "^3.0.0",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^18.3.1",
|
||||
"react": "^19.2.3",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-datepicker": "^8.10.0",
|
||||
"react-datepicker": "^9.0.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^16.3.5",
|
||||
"react-konva": "^18.2.12",
|
||||
"react-pdf": "^9.2.1",
|
||||
"react-i18next": "^16.5.0",
|
||||
"react-konva": "^19.2.1",
|
||||
"react-pdf": "^10.2.0",
|
||||
"react-quill-new": "^3.6.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router": "^7.10.0",
|
||||
"react-router": "^7.10.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.2",
|
||||
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||
"react-timezone-select": "^3.2.8",
|
||||
"react-to-print": "^3.2.0",
|
||||
"react-tooltip": "^5.30.0",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"regex-parser": "^2.3.1",
|
||||
"serve": "^14.2.5",
|
||||
"styled-components": "^5.3.11",
|
||||
"web-vitals": "^5.1.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
},
|
||||
@@ -99,31 +98,31 @@
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/runtime-corejs2": "^7.28.3",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/react": "^16.3.1",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/react": "^19.2.7",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitest/ui": "^4.0.15",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"babel-loader": "^10.0.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.24",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"jsdom": "^27.2.0",
|
||||
"jsdom": "^27.3.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.7.4",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"tmp": "^0.2.5",
|
||||
"vite": "^7.2.6",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-svgr": "^4.5.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vite-tsconfig-paths": "^6.0.1",
|
||||
"vitest": "^4.0.15"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "Unterschrift",
|
||||
"upload-image": "Bild hochladen",
|
||||
"clear": "Löschen",
|
||||
"upload-signature/Image": "Bitte Unterschrift/Bild hochladen",
|
||||
"upload-signature/Image": "Bitte Unterschrift/Bild hochladen/Stempel",
|
||||
"initials": "Initialen",
|
||||
"API": "API",
|
||||
"api-token": "API-Token",
|
||||
@@ -588,7 +588,7 @@
|
||||
"provide-your-details": "Bitte geben Sie Ihre Daten an",
|
||||
"provide-email": "Bitte E-Mail angeben.",
|
||||
"additional-info": "Zusätzliche Informationen",
|
||||
"signature-saved-alert": "Unterschrift erfolgreich gespeichert.",
|
||||
"signature-saved-alert": "erfolgreich gespeichert.",
|
||||
"saved-successfully": "Erfolgreich gespeichert.",
|
||||
"fill-field": "Bitte dieses Feld ausfüllen",
|
||||
"error-template": "Fehler: Vorlage nicht gefunden!",
|
||||
@@ -1308,5 +1308,25 @@
|
||||
"your-credits-usage": "Ihr Kreditverbrauch",
|
||||
"remaining": "verbleibend",
|
||||
"used": "verwendet",
|
||||
"your-credits-usage-help": "Dies ist die Anzahl der Credits, die mit Ihrem API-Schlüssel verbraucht wurden."
|
||||
"your-credits-usage-help": "Dies ist die Anzahl der Credits, die mit Ihrem API-Schlüssel verbraucht wurden.",
|
||||
"api-credits-updated": "API-Credits wurden erfolgreich zugewiesen.",
|
||||
"min-date": "Mindestdatum",
|
||||
"max-date": "Höchstdatum",
|
||||
"invalid-min-date": "Ungültiges Mindestdatum.",
|
||||
"min-less-max": "Das Mindestdatum muss kleiner oder gleich dem Höchstdatum sein.",
|
||||
"invalid-max-date": "Ungültiges Höchstdatum.",
|
||||
"max-less-min": "Das Höchstdatum muss größer oder gleich dem Mindestdatum sein.",
|
||||
"date-range": "Datumsbereich",
|
||||
"date-range-help": "Legen Sie ein Mindest- und/oder Höchstdatum fest, um die Auswahl einzuschränken. Das gewählte Datum muss innerhalb dieses Bereichs liegen.",
|
||||
"option-disabled-by-owner": "Der Dokumentersteller hat diese Option deaktiviert",
|
||||
"pen-colors": "Stiftfarben",
|
||||
"pen-colors-help": "Wählen Sie aus, welche Stiftfarben für Signatur- und Initialen-Widgets verfügbar sind. Aktivieren Sie ein Kontrollkästchen, um eine Farbe anzuzeigen, und deaktivieren Sie es, um sie auszublenden.",
|
||||
"security-key": "Sicherheitsschlüssel",
|
||||
"remove-limit": "Limit entfernen",
|
||||
"removed-api-credits": "Das API-Credit-Limit wurde entfernt.",
|
||||
"enable-authentication": "Authentifizierung aktivieren",
|
||||
"working": "In Bearbeitung",
|
||||
"test-security-key": "Sicherheitsschlüssel testen",
|
||||
"generate": "Generieren",
|
||||
"regenerate": "Neu generieren"
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "Signature",
|
||||
"upload-image": " Upload image",
|
||||
"clear": "Clear",
|
||||
"upload-signature/Image": "Please upload signature/Image",
|
||||
"upload-signature/Image": "Please upload signature/Image/stamp",
|
||||
"initials": "Initials",
|
||||
"API": "API",
|
||||
"api-token": "API token",
|
||||
@@ -589,7 +589,7 @@
|
||||
"provide-your-details": "Provide your details",
|
||||
"provide-email": "Please provide email.",
|
||||
"additional-info": "Additional info",
|
||||
"signature-saved-alert": "Signature saved successfully.",
|
||||
"signature-saved-alert": "Saved successfully.",
|
||||
"saved-successfully": "Saved successfully.",
|
||||
"fill-field": "Please fill out this field",
|
||||
"error-template": "Error: Template not found!",
|
||||
@@ -1309,5 +1309,25 @@
|
||||
"your-credits-usage": "Your credits usage",
|
||||
"remaining": "remaining",
|
||||
"used": "used",
|
||||
"your-credits-usage-help": "This is the number of credits consumed with your API key."
|
||||
"your-credits-usage-help": "This is the number of credits consumed with your API key.",
|
||||
"api-credits-updated": "API credits have been allocated successfully.",
|
||||
"min-date": "Min Date",
|
||||
"max-date": "Max Date",
|
||||
"invalid-min-date": "Invalid min date.",
|
||||
"min-less-max": "Min date must be less than or equal to max date.",
|
||||
"invalid-max-date": "Invalid max date.",
|
||||
"max-less-min": "Max date must be greater than or equal to min date.",
|
||||
"date-range": "Date range",
|
||||
"date-range-help": "Set Min and/or Max to restrict selection. Chosen date must fall within the range.",
|
||||
"option-disabled-by-owner": "Document creator has disabled this option",
|
||||
"pen-colors": "Pen colors",
|
||||
"pen-colors-help": "Choose which pen colors are available for Signature and Initials widgets. Check to show a color, uncheck to hide it.",
|
||||
"security-key": "Security key",
|
||||
"remove-limit": "Remove limit",
|
||||
"removed-api-credits": "The API credit limit has been removed.",
|
||||
"enable-authentication": "Enable authentication",
|
||||
"working": "Working",
|
||||
"test-security-key": "Test security key",
|
||||
"generate": "Generate",
|
||||
"regenerate": "Regenerate"
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen/estampilla",
|
||||
"initials": "Iniciales",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
@@ -589,7 +589,7 @@
|
||||
"provide-your-details": "Proporciona tus detalles",
|
||||
"provide-email": "Por favor, proporciona un correo.",
|
||||
"additional-info": "Información adicional",
|
||||
"signature-saved-alert": "Firma guardada exitosamente.",
|
||||
"signature-saved-alert": "guardada exitosamente.",
|
||||
"saved-successfully": "Guardado con éxito.",
|
||||
"fill-field": "Por favor, rellena este campo",
|
||||
"error-template": "Error: ¡Plantilla no encontrada!",
|
||||
@@ -1309,5 +1309,25 @@
|
||||
"your-credits-usage": "Uso de sus créditos",
|
||||
"remaining": "restantes",
|
||||
"used": "usados",
|
||||
"your-credits-usage-help": "Esta es la cantidad de créditos consumidos con su clave API."
|
||||
"your-credits-usage-help": "Esta es la cantidad de créditos consumidos con su clave API.",
|
||||
"api-credits-updated": "Los créditos de API se han asignado correctamente.",
|
||||
"min-date": "Fecha mínima",
|
||||
"max-date": "Fecha máxima",
|
||||
"invalid-min-date": "Fecha mínima no válida.",
|
||||
"min-less-max": "La fecha mínima debe ser menor o igual a la fecha máxima.",
|
||||
"invalid-max-date": "Fecha máxima no válida.",
|
||||
"max-less-min": "La fecha máxima debe ser mayor o igual a la fecha mínima.",
|
||||
"date-range": "Rango de fechas",
|
||||
"date-range-help": "Establezca una fecha mínima y/o máxima para restringir la selección. La fecha elegida debe estar dentro del rango.",
|
||||
"option-disabled-by-owner": "El creador del documento ha deshabilitado esta opción",
|
||||
"pen-colors": "Colores del bolígrafo",
|
||||
"pen-colors-help": "Elija qué colores de bolígrafo están disponibles para los widgets de Firma e Iniciales. Marque una casilla para mostrar un color y desmárquela para ocultarlo.",
|
||||
"security-key": "Clave de seguridad",
|
||||
"remove-limit": "Eliminar límite",
|
||||
"removed-api-credits": "El límite de créditos API ha sido eliminado.",
|
||||
"enable-authentication": "Habilitar autenticación",
|
||||
"working": "En funcionamiento",
|
||||
"test-security-key": "Probar clave de seguridad",
|
||||
"generate": "Generar",
|
||||
"regenerate": "Regenerar"
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "Signature",
|
||||
"upload-image": "Télécharger une image",
|
||||
"clear": "Vider",
|
||||
"upload-signature/Image": "télécharger-signature/Image",
|
||||
"upload-signature/Image": "télécharger-signature/Image/timbre",
|
||||
"initials": "Initiales",
|
||||
"API": "API",
|
||||
"api-token": "Jeton API",
|
||||
@@ -588,7 +588,7 @@
|
||||
"provide-your-details": "Fournissez vos coordonnées",
|
||||
"provide-email": "Veuillez fournir un e-mail.",
|
||||
"additional-info": "Information additionnelle",
|
||||
"signature-saved-alert": "Signature enregistrée avec succès.",
|
||||
"signature-saved-alert": "enregistrée avec succès.",
|
||||
"saved-successfully": "Enregistré avec succès.",
|
||||
"fill-field": "Veuillez remplir ce champ",
|
||||
"error-template": "Erreur : modèle introuvable ! ",
|
||||
@@ -1308,5 +1308,25 @@
|
||||
"your-credits-usage": "Votre utilisation des crédits",
|
||||
"remaining": "restants",
|
||||
"used": "utilisés",
|
||||
"your-credits-usage-help": "Ceci est le nombre de crédits consommés avec votre clé API."
|
||||
"your-credits-usage-help": "Ceci est le nombre de crédits consommés avec votre clé API.",
|
||||
"api-credits-updated": "Les crédits API ont été attribués avec succès.",
|
||||
"min-date": "Date minimale",
|
||||
"max-date": "Date maximale",
|
||||
"invalid-min-date": "Date minimale invalide.",
|
||||
"min-less-max": "La date minimale doit être inférieure ou égale à la date maximale.",
|
||||
"invalid-max-date": "Date maximale invalide.",
|
||||
"max-less-min": "La date maximale doit être supérieure ou égale à la date minimale.",
|
||||
"date-range": "Plage de dates",
|
||||
"date-range-help": "Définissez une date minimale et/ou maximale pour restreindre la sélection. La date choisie doit se situer dans cette plage.",
|
||||
"option-disabled-by-owner": "Le créateur du document a désactivé cette option",
|
||||
"pen-colors": "Couleurs du stylo",
|
||||
"pen-colors-help": "Choisissez quelles couleurs de stylo sont disponibles pour les widgets Signature et Initiales. Cochez pour afficher une couleur, décochez pour la masquer.",
|
||||
"security-key": "Clé de sécurité",
|
||||
"remove-limit": "Supprimer la limite",
|
||||
"removed-api-credits": "La limite de crédits API a été supprimée.",
|
||||
"enable-authentication": "Activer l'authentification",
|
||||
"working": "En cours",
|
||||
"test-security-key": "Tester la clé de sécurité",
|
||||
"generate": "Générer",
|
||||
"regenerate": "Régénérer"
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "हस्ताक्षर",
|
||||
"upload-image": " छवि अपलोड करें",
|
||||
"clear": "स्पष्ट",
|
||||
"upload-signature/Image": "कृपया हस्ताक्षर/छवि अपलोड करें",
|
||||
"upload-signature/Image": "कृपया हस्ताक्षर/छवि अपलोड करें/मोहर",
|
||||
"initials": "हस्ताक्षर",
|
||||
"API": "एपीआई",
|
||||
"api-token": "एपीआई टोकन",
|
||||
@@ -588,7 +588,7 @@
|
||||
"provide-your-details": "अपना विवरण प्रदान करें",
|
||||
"provide-email": "कृपया ईमेल प्रदान करें।",
|
||||
"additional-info": "अतिरिक्त जानकारी",
|
||||
"signature-saved-alert": "हस्ताक्षर सफलतापूर्वक सहेजा गया।",
|
||||
"signature-saved-alert": "सफलतापूर्वक सहेजा गया।",
|
||||
"saved-successfully": "सफलतापूर्वक सहेजा गया।",
|
||||
"fill-field": "कृपया इस फ़ील्ड को भरें",
|
||||
"error-template": "त्रुटि: टेम्पलेट नहीं मिला!",
|
||||
@@ -1308,5 +1308,25 @@
|
||||
"your-credits-usage": "आपके क्रेडिट उपयोग",
|
||||
"remaining": "शेष",
|
||||
"used": "उपयोग किए गए",
|
||||
"your-credits-usage-help": "यह आपके API कुंजी द्वारा उपभोग किए गए क्रेडिट की संख्या है।"
|
||||
"your-credits-usage-help": "यह आपके API कुंजी द्वारा उपभोग किए गए क्रेडिट की संख्या है।",
|
||||
"api-credits-updated": "API क्रेडिट सफलतापूर्वक आवंटित कर दिए गए हैं।",
|
||||
"min-date": "न्यूनतम तिथि",
|
||||
"max-date": "अधिकतम तिथि",
|
||||
"invalid-min-date": "अमान्य न्यूनतम तिथि।",
|
||||
"min-less-max": "न्यूनतम तिथि अधिकतम तिथि से कम या उसके बराबर होनी चाहिए।",
|
||||
"invalid-max-date": "अमान्य अधिकतम तिथि।",
|
||||
"max-less-min": "अधिकतम तिथि न्यूनतम तिथि से अधिक या उसके बराबर होनी चाहिए।",
|
||||
"date-range": "तिथि सीमा",
|
||||
"date-range-help": "चयन को सीमित करने के लिए न्यूनतम और/या अधिकतम तिथि सेट करें। चयनित तिथि इस सीमा के भीतर होनी चाहिए।",
|
||||
"option-disabled-by-owner": "दस्तावेज़ निर्माता ने इस विकल्प को अक्षम कर दिया है",
|
||||
"pen-colors": "पेन के रंग",
|
||||
"pen-colors-help": "हस्ताक्षर और प्रारंभिक विजेट्स के लिए उपलब्ध पेन रंग चुनें। किसी रंग को दिखाने के लिए चेक करें और छिपाने के लिए अनचेक करें।",
|
||||
"security-key": "सुरक्षा कुंजी",
|
||||
"remove-limit": "सीमा हटाएँ",
|
||||
"removed-api-credits": "API क्रेडिट सीमा हटा दी गई है।",
|
||||
"enable-authentication": "प्रमाणीकरण सक्षम करें",
|
||||
"working": "कार्यरत",
|
||||
"test-security-key": "सुरक्षा कुंजी का परीक्षण करें",
|
||||
"generate": "जनरेट करें",
|
||||
"regenerate": "पुनः जनरेट करें"
|
||||
}
|
||||
@@ -270,7 +270,7 @@
|
||||
"signature": "Firma",
|
||||
"upload-image": "Carica immagine",
|
||||
"clear": "Cancella",
|
||||
"upload-signature/Image": "Carica firma/immagine",
|
||||
"upload-signature/Image": "Carica firma/immagine/timbro",
|
||||
"initials": "Iniziali",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
@@ -588,7 +588,7 @@
|
||||
"provide-your-details": "Fornisci i tuoi dettagli",
|
||||
"provide-email": "Fornisci l'email.",
|
||||
"additional-info": "Informazioni aggiuntive",
|
||||
"signature-saved-alert": "Firma salvata con successo.",
|
||||
"signature-saved-alert": "salvata con successo.",
|
||||
"saved-successfully": "Salvato con successo.",
|
||||
"fill-field": "Compila questo campo",
|
||||
"error-template": "Errore: Modello non trovato!",
|
||||
@@ -1308,5 +1308,25 @@
|
||||
"your-credits-usage": "Utilizzo dei tuoi crediti",
|
||||
"remaining": "rimanenti",
|
||||
"used": "usati",
|
||||
"your-credits-usage-help": "Questo è il numero di crediti consumati con la tua chiave API."
|
||||
"your-credits-usage-help": "Questo è il numero di crediti consumati con la tua chiave API.",
|
||||
"api-credits-updated": "I crediti API sono stati assegnati con successo.",
|
||||
"min-date": "Data minima",
|
||||
"max-date": "Data massima",
|
||||
"invalid-min-date": "Data minima non valida.",
|
||||
"min-less-max": "La data minima deve essere minore o uguale alla data massima.",
|
||||
"invalid-max-date": "Data massima non valida.",
|
||||
"max-less-min": "La data massima deve essere maggiore o uguale alla data minima.",
|
||||
"date-range": "Intervallo di date",
|
||||
"date-range-help": "Imposta una data minima e/o massima per limitare la selezione. La data scelta deve rientrare nell'intervallo.",
|
||||
"option-disabled-by-owner": "Il creatore del documento ha disabilitato questa opzione.",
|
||||
"pen-colors": "Colori della penna",
|
||||
"pen-colors-help": "Scegli quali colori della penna sono disponibili per i widget Firma e Iniziali. Seleziona una casella per mostrare un colore e deselezionala per nasconderlo.",
|
||||
"security-key": "Chiave di sicurezza",
|
||||
"remove-limit": "Rimuovi limite",
|
||||
"removed-api-credits": "Il limite dei crediti API è stato rimosso.",
|
||||
"enable-authentication": "Abilita autenticazione",
|
||||
"working": "In funzione",
|
||||
"test-security-key": "Testa la chiave di sicurezza",
|
||||
"generate": "Genera",
|
||||
"regenerate": "Rigenera"
|
||||
}
|
||||
@@ -190,9 +190,7 @@ const BulkSendUi = (props) => {
|
||||
const batchQuery = async (Documents) => {
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const functionsUrl = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}functions/batchdocuments`;
|
||||
const functionsUrl = `${localStorage.getItem("baseUrl")}functions/batchdocuments`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
@@ -201,13 +199,14 @@ const BulkSendUi = (props) => {
|
||||
const params = { Documents: JSON.stringify(Documents) };
|
||||
try {
|
||||
const res = await axios.post(functionsUrl, params, { headers: headers });
|
||||
// console.log("res ", res);
|
||||
if (res.data && res.data.result) {
|
||||
props.handleClose("success", Documents?.length);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
props.handleClose("error", 0);
|
||||
const message =
|
||||
err?.response?.data?.error || err?.message || "something went wrong.";
|
||||
console.error("Error sending documents:", message);
|
||||
props.handleClose("error", 0, message);
|
||||
} finally {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ const Header = ({ isConsole, setIsLoggingOut }) => {
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className={`mt-3 z-[1] p-2 shadow op-dropdown-open op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-56 ${
|
||||
className={`mt-4 z-[1] p-2 shadow op-dropdown-open op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-56 ${
|
||||
isOpen ? "" : "hidden"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useLocation, matchPath } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMemo } from "react";
|
||||
@@ -70,13 +69,13 @@ export default function Title() {
|
||||
[t, prefix, appName]
|
||||
);
|
||||
const manifestUrl = useManifestUrl(appName, logo);
|
||||
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
<>
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={title} />
|
||||
{logo && <link rel="icon" type="image/png" href={logo} />}
|
||||
<link rel="manifest" href={manifestUrl} />
|
||||
</Helmet>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,9 +56,7 @@ function DriveBody(props) {
|
||||
setRename("");
|
||||
const trimmedValue = renameValue.trim();
|
||||
if (trimmedValue.length > 0) {
|
||||
const updateName = {
|
||||
Name: renameValue
|
||||
};
|
||||
const updateName = { Name: renameValue };
|
||||
const docId = data.objectId;
|
||||
const docData = props.pdfData;
|
||||
const updatedData = docData.map((item) => {
|
||||
@@ -84,7 +82,7 @@ function DriveBody(props) {
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
console.error("Error in rename doc", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
@@ -160,9 +158,7 @@ function DriveBody(props) {
|
||||
const handleDeleteDocument = async (docData) => {
|
||||
setIsDeleteDoc({});
|
||||
const docId = docData.objectId;
|
||||
const data = {
|
||||
IsArchive: true
|
||||
};
|
||||
const data = { IsArchive: true };
|
||||
|
||||
await axios
|
||||
.put(
|
||||
@@ -184,7 +180,7 @@ function DriveBody(props) {
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
console.error("Err in delete doc", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
@@ -218,16 +214,12 @@ function DriveBody(props) {
|
||||
}
|
||||
};
|
||||
} else {
|
||||
updateData = {
|
||||
Folder: { __op: "Delete" }
|
||||
};
|
||||
updateData = { Folder: { __op: "Delete" } };
|
||||
}
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${updateDocId}`,
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${updateDocId}`,
|
||||
updateData,
|
||||
{
|
||||
headers: {
|
||||
@@ -248,7 +240,7 @@ function DriveBody(props) {
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
console.error("err in move folder", err);
|
||||
});
|
||||
|
||||
setIsOpenMoveModal(false);
|
||||
@@ -296,7 +288,7 @@ function DriveBody(props) {
|
||||
props.setPdfData(updatedData);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
console.error("Err in delete folder", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
function DefaultSignature(props) {
|
||||
@@ -105,9 +105,7 @@ function DefaultSignature(props) {
|
||||
</button>
|
||||
{!props.isDefault && (
|
||||
<div className="absolute bg-black/70 text-white w-full h-full flex items-center justify-center text-[11px] cursor-default">
|
||||
<span className="-rotate-45">
|
||||
Document creator has disabled this option
|
||||
</span>
|
||||
<span className="-rotate-45">{t("option-disabled-by-owner")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -54,6 +54,10 @@ const EditTemplate = ({
|
||||
AllowModifications: template?.AllowModifications || false,
|
||||
TimeToCompleteDays: template?.TimeToCompleteDays || 15,
|
||||
});
|
||||
const pensList = ["blue", "red", "black"];
|
||||
const [selectedColors, setSelectedColors] = useState(
|
||||
template?.PenColors || pensList
|
||||
);
|
||||
const [isUpdate, setIsUpdate] = useState(false);
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [uploadPdf, setUploadPdf] = useState({
|
||||
@@ -211,6 +215,7 @@ const EditTemplate = ({
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
IsTourEnabled: isTourEnabled,
|
||||
AllowModifications: allowModify,
|
||||
PenColors: selectedColors,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
@@ -245,6 +250,19 @@ const EditTemplate = ({
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const handleColorsChange = (color) => {
|
||||
setSelectedColors((prev) => {
|
||||
// If user tries to uncheck the last remaining color → block it
|
||||
if (prev.length === 1 && prev.includes(color)) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Normal toggle behavior
|
||||
return prev.includes(color)
|
||||
? prev.filter((c) => c !== color) // remove
|
||||
: [...prev, color]; // add
|
||||
});
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen
|
||||
@@ -485,6 +503,41 @@ const EditTemplate = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3 mb-4">
|
||||
<label htmlFor="penColors">
|
||||
{t("pen-colors")}
|
||||
<a data-tooltip-id="pen-colors-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<Tooltip id="pen-colors-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("pen-colors")}</p>
|
||||
<div>{t("pen-colors-help")}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{pensList.map((color) => (
|
||||
<div
|
||||
key={color}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="penColors"
|
||||
checked={selectedColors.includes(color)}
|
||||
onChange={() => handleColorsChange(color)}
|
||||
/>
|
||||
<div className="hover:underline underline-offset-2 cursor-default capitalize">
|
||||
{color}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3">
|
||||
<SignersInput
|
||||
label={t("Bcc")}
|
||||
|
||||
@@ -136,7 +136,7 @@ function Placeholder(props) {
|
||||
data?.format,
|
||||
props.fontSize || props.pos?.options?.fontSize || 12,
|
||||
props.fontColor || props.pos?.options?.fontColor || "black",
|
||||
formdata
|
||||
formdata,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -620,12 +620,12 @@ function Placeholder(props) {
|
||||
};
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className="border-gray-400 rounded-[50px] border-[1px] px-3 md:ml-2 text-xs py-2 focus:outline-none hover:border-base-content "
|
||||
className="border-gray-400 rounded-[50px] border-[1px] px-3 text-xs py-2 focus:outline-none hover:border-base-content "
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className="fa-light fa-calendar ml-[5px]"></i>
|
||||
<i className={`${value ? "ml-[5px]" : ""} fa-light fa-calendar `}></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
@@ -655,12 +655,14 @@ function Placeholder(props) {
|
||||
{props.pos?.options?.response &&
|
||||
props.pos.key !== props?.currWidgetsDetails?.key &&
|
||||
props.pos.type === textWidget ? (
|
||||
<span
|
||||
<textarea
|
||||
readOnly
|
||||
onClick={() => {
|
||||
if (props?.ispublicTemplate) return;
|
||||
props.setCurrWidgetsDetails &&
|
||||
props.setCurrWidgetsDetails(props.pos);
|
||||
props?.setRoleName("prefill");
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
props?.setRoleName && props?.setRoleName("prefill");
|
||||
props.setUniqueId && props.setUniqueId(props?.data?.Id);
|
||||
}}
|
||||
style={{
|
||||
fontFamily: "Arial, sans-serif",
|
||||
@@ -671,13 +673,21 @@ function Placeholder(props) {
|
||||
...(props.pos?.Height ? { height: props.pos?.Height } : {}),
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
zIndex: 99
|
||||
zIndex: 99,
|
||||
cursor: getCursor(),
|
||||
resize: "none",
|
||||
background: "transparent",
|
||||
width: props.posWidth(props.pos, props.isSignYourself),
|
||||
whiteSpace: "pre-wrap",
|
||||
overflow: "hidden",
|
||||
outline: "none",
|
||||
border: "none"
|
||||
}}
|
||||
>
|
||||
{
|
||||
props.pos?.options?.response
|
||||
}
|
||||
</span>
|
||||
</textarea>
|
||||
) : (
|
||||
<Rnd
|
||||
id={props.pos.key}
|
||||
@@ -737,7 +747,7 @@ function Placeholder(props) {
|
||||
props?.data,
|
||||
props?.isNeedSign,
|
||||
props?.uniqueId
|
||||
) //handle block color of widget for
|
||||
)
|
||||
}}
|
||||
onDrag={(_, d) => {
|
||||
props?.handleTabDrag?.(props.pos.key);
|
||||
@@ -938,7 +948,7 @@ function Placeholder(props) {
|
||||
<span>{t("set-date")} :</span>
|
||||
<DatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
<div className="flex justify-start md:ml-2 ">
|
||||
<div className="flex justify-start md:ml-2">
|
||||
<select
|
||||
className="bg-transparent outline-none"
|
||||
value={months[getMonth(date)]}
|
||||
@@ -992,7 +1002,7 @@ function Placeholder(props) {
|
||||
}
|
||||
portalId="root-portal"
|
||||
/>
|
||||
<label className=" flex items-center gap-1 cursor-pointer">
|
||||
<label className="flex items-center gap-1 cursor-pointer mb-0">
|
||||
<input
|
||||
checked={isToday}
|
||||
type="checkbox"
|
||||
@@ -1006,7 +1016,7 @@ function Placeholder(props) {
|
||||
</label>
|
||||
<span
|
||||
onClick={() => handleClearDate()}
|
||||
className="underline text-blue-500 cursor-pointer md:ml-2"
|
||||
className="underline text-blue-500 cursor-pointer ml-2"
|
||||
>
|
||||
{t("clear")}
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { useEffect, useMemo, useState, forwardRef } from "react";
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
forwardRef,
|
||||
useRef
|
||||
} from "react";
|
||||
import {
|
||||
onChangeInput,
|
||||
getMonth,
|
||||
getYear,
|
||||
radioButtonWidget,
|
||||
@@ -9,9 +15,7 @@ import {
|
||||
months,
|
||||
years,
|
||||
selectCheckbox,
|
||||
checkRegularExpress,
|
||||
isBase64,
|
||||
getSignerPages
|
||||
isBase64
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
@@ -28,6 +32,7 @@ const widgetCls =
|
||||
function PlaceholderType(props) {
|
||||
const selectWidgetCls = `w-full h-full absolute left-0 top-0 focus:outline-none text-base-content`;
|
||||
const { t } = useTranslation();
|
||||
const textRef = useRef();
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const prefillImgLoad = useSelector((state) => state.widget.prefillImgLoad);
|
||||
const type = props?.pos?.type;
|
||||
@@ -42,10 +47,8 @@ function PlaceholderType(props) {
|
||||
// prefer the latest response value over any default value
|
||||
const widgetData =
|
||||
props.pos?.options?.response ?? props.pos?.options?.defaultValue ?? "";
|
||||
// const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
|
||||
const [widgetValue, setwidgetValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
// const [hint, setHint] = useState("");
|
||||
const [imgUrl, setImgUrl] = useState("");
|
||||
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||
const fontColor = props.pos.options?.fontColor || "black";
|
||||
@@ -72,11 +75,6 @@ function PlaceholderType(props) {
|
||||
// keep displayed value in sync with the stored response
|
||||
setwidgetValue(widgetData);
|
||||
}
|
||||
// if (props.pos?.options?.hint) {
|
||||
// setHint(props.pos?.options.hint);
|
||||
// } else if (props.pos?.options?.validation?.type) {
|
||||
// checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
||||
// }
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos, widgetData, type]);
|
||||
@@ -138,6 +136,39 @@ function PlaceholderType(props) {
|
||||
props?.pos?.type === name[0] ? `${name[0]}${lastWord}` : widgetName;
|
||||
return props?.pos?.options?.hint || title;
|
||||
};
|
||||
//useEffect is used to increase auto height of text/textInput widget when user using multiline option and enter value in next line
|
||||
useEffect(() => {
|
||||
if (!textRef.current) return;
|
||||
if (
|
||||
(type === "text" || type === textInputWidget) &&
|
||||
textRef.current &&
|
||||
widgetValue
|
||||
) {
|
||||
const el = textRef.current;
|
||||
// Count actual number of lines
|
||||
const lines = widgetValue?.split("\n")?.length || props.pos?.Height;
|
||||
// Get line height from computed style
|
||||
const lineHeight = parseInt(window.getComputedStyle(el).lineHeight);
|
||||
|
||||
const textWidgetHeight = lines * lineHeight;
|
||||
// Set widget box height (your logic)
|
||||
onChangeInput(
|
||||
null,
|
||||
props.pos,
|
||||
props?.xyPosition,
|
||||
props.index,
|
||||
props?.setXyPosition,
|
||||
props.data && props.data.Id,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
textWidgetHeight
|
||||
);
|
||||
}
|
||||
}, [widgetValue]);
|
||||
|
||||
switch (type) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
@@ -230,17 +261,19 @@ function PlaceholderType(props) {
|
||||
return props.isSignYourself || iswidgetEnable ? (
|
||||
<textarea
|
||||
placeholder={formatWidgetName()}
|
||||
rows={1}
|
||||
ref={textRef}
|
||||
rows={50}
|
||||
value={
|
||||
widgetValue
|
||||
}
|
||||
className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`}
|
||||
className={`w-full resize-none overflow-hidden text-base-content item-center outline-none ${isReadOnly ? "select-none" : ""}`}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
background: isReadOnly ? props.data?.blockColor : "white",
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
name="text"
|
||||
readOnly
|
||||
disabled={props.isNeedSign && isReadOnly}
|
||||
cols="50"
|
||||
@@ -530,20 +563,22 @@ function PlaceholderType(props) {
|
||||
case textWidget:
|
||||
return (
|
||||
<textarea
|
||||
name="text"
|
||||
ref={textRef}
|
||||
readOnly
|
||||
placeholder={t("widgets-name.text")}
|
||||
rows={1}
|
||||
value={
|
||||
rows={50}
|
||||
cols={50}
|
||||
defaultValue={
|
||||
widgetValue
|
||||
}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontFamily: "Arial, sans-serif",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
background: "white"
|
||||
}}
|
||||
cols="50"
|
||||
className="w-full resize-none overflow-hidden text-base-content item-center outline-none"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
||||
@@ -72,7 +72,7 @@ const ImageComponent = (props) => {
|
||||
<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
|
||||
ref={(el) => (imageRefs.current[props?.id] = el)} // Assign ref dynamicallys
|
||||
src={imgUrl}
|
||||
draggable="false"
|
||||
className="object-contain h-full w-full aspect-[5/2]"
|
||||
@@ -90,19 +90,14 @@ const ImageComponent = (props) => {
|
||||
) : (
|
||||
<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()}
|
||||
onClick={() => imageRefs.current[props?.id]?.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
|
||||
ref={(el) => (imageRefs.current[props?.id] = el)} // Assign ref dynamically
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light text-base-content fa-cloud-upload-alt text-[25px]"></i>
|
||||
@@ -120,16 +115,12 @@ function PrefillWidgetModal(props) {
|
||||
const loadedSet = useRef(new Set());
|
||||
const initializedRef = useRef(false); // prevent rerun on state updates
|
||||
const [image, setImage] = useState(null);
|
||||
const [imageLoaders, setImageLoaders] = useState({});
|
||||
const [currentWidget, setCurrentWidget] = useState("");
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [totalImages, setTotalImages] = useState(0);
|
||||
const [loadedImages, setLoadedImages] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const years = range(1950, getYear(new Date()) + 16, 1);
|
||||
const isAnyLoaderActive = Object.values(imageLoaders).some(
|
||||
(val) => val === true
|
||||
);
|
||||
|
||||
// useMemo to memoize the calculation of unique widgets
|
||||
const uniqueWidget = useMemo(() => {
|
||||
@@ -179,7 +170,7 @@ function PrefillWidgetModal(props) {
|
||||
if (Array.isArray(prefillImg)) {
|
||||
prefillImg.forEach((img) => dispatch(setPrefillImg(img)));
|
||||
}
|
||||
setImageLoaders({});
|
||||
setLoading(false);
|
||||
};
|
||||
savePrefillImg();
|
||||
}, [props.xyPosition]);
|
||||
@@ -271,7 +262,7 @@ function PrefillWidgetModal(props) {
|
||||
};
|
||||
|
||||
const handleSavePrefillImg = async (widgetDetails) => {
|
||||
setImageLoaders((prev) => ({ ...prev, [widgetDetails?.key]: true }));
|
||||
setLoading(true);
|
||||
try {
|
||||
const imageName = generatePdfName(16);
|
||||
const imageUrl = await convertBase64ToFile(
|
||||
@@ -419,9 +410,9 @@ function PrefillWidgetModal(props) {
|
||||
// (tracks how many images have finished loading)
|
||||
setLoadedImages((prev) => prev + 1);
|
||||
}
|
||||
setImageLoaders({});
|
||||
setLoading(false);
|
||||
};
|
||||
const handleWidgetType = (position) => {
|
||||
const handleWidgetType = (position, id) => {
|
||||
switch (position?.type) {
|
||||
case "checkbox":
|
||||
return (
|
||||
@@ -533,10 +524,10 @@ function PrefillWidgetModal(props) {
|
||||
return (
|
||||
<ImageComponent
|
||||
position={position}
|
||||
imageLoaders={imageLoaders}
|
||||
onImageChange={onImageChange}
|
||||
handleImageLoaded={handleImageLoaded}
|
||||
handleClearImage={handleClearImage}
|
||||
id={id}
|
||||
/>
|
||||
);
|
||||
case radioButtonWidget:
|
||||
@@ -658,7 +649,7 @@ function PrefillWidgetModal(props) {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-4 w-full">
|
||||
{uniqueWidget.map((x, id) => (
|
||||
<div key={id} className="flex flex-col gap-2 w-full">
|
||||
{handleWidgetType(x.widget)}
|
||||
{handleWidgetType(x.widget, id)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -727,7 +718,7 @@ function PrefillWidgetModal(props) {
|
||||
</div>
|
||||
<div className="flex gap-2 mx-4 mb-3">
|
||||
<button
|
||||
disabled={isAnyLoaderActive || props?.isSubmit}
|
||||
disabled={props?.isSubmit}
|
||||
className="op-btn op-btn-primary op-btn-sm w-[80px]"
|
||||
onClick={() => handleEmbedPrefill(props?.item)}
|
||||
>
|
||||
|
||||
@@ -222,6 +222,7 @@ const WidgetNameModal = (props) => {
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
@@ -373,7 +374,7 @@ const WidgetNameModal = (props) => {
|
||||
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
||||
{t("allowed-signature-types")}
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
<div className="ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => {
|
||||
return (
|
||||
<div key={i} className="flex flex-row gap-[5px] items-center">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
function DefaultSignature(props) {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
className={`${props?.currWidgetsDetails?.type === "initials" ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-gray-300 rounded-[4px]`}
|
||||
>
|
||||
<img
|
||||
src={props?.defaultSignImg}
|
||||
draggable="false"
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DefaultSignature;
|
||||
@@ -0,0 +1,21 @@
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
|
||||
function Draw(props) {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={props?.canvasRef}
|
||||
penColor={props?.penColor}
|
||||
canvasProps={{
|
||||
className: `${props?.currWidgetsDetails?.type === "initials" ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-gray-300 rounded-[4px]`
|
||||
}}
|
||||
onEnd={() =>
|
||||
props?.handleSignatureChange(props?.canvasRef.current?.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Draw;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const ALLOWED_COLORS = ["blue", "red", "black"];
|
||||
|
||||
const COLOR_CLASS = {
|
||||
blue: "text-blue-600",
|
||||
red: "text-red-600",
|
||||
black: "text-black",
|
||||
white: "text-white"
|
||||
};
|
||||
|
||||
function PenColorComponent({
|
||||
providedColors,
|
||||
penColor,
|
||||
convertToImg,
|
||||
fontSelect,
|
||||
typedSignature,
|
||||
setPenColor
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pensList = useMemo(() => {
|
||||
const allowed = new Set(ALLOWED_COLORS);
|
||||
const filtered = Array.isArray(providedColors)
|
||||
? providedColors
|
||||
.map((c) => String(c).toLowerCase())
|
||||
.filter((c) => allowed.has(c))
|
||||
: [];
|
||||
|
||||
// remove duplicates while preserving order
|
||||
return filtered.length ? [...new Set(filtered)] : ALLOWED_COLORS;
|
||||
}, [providedColors]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center m-[5px] gap-3">
|
||||
<span className="text-base-content">{t("options")}</span>
|
||||
{pensList.map((color) => {
|
||||
const selected = penColor === color;
|
||||
return (
|
||||
<i
|
||||
key={color}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`Select ${color} pen`}
|
||||
onClick={() => {
|
||||
setPenColor?.(color);
|
||||
convertToImg?.(fontSelect, typedSignature, color);
|
||||
}}
|
||||
className={`${COLOR_CLASS[color] || "text-base-content"} ${selected ? "border-current" : "border-white"} border-b-[2px] pb-0.5 cursor-pointer text-[16px] fa-light fa-pen-nib`}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default PenColorComponent;
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function TypeSignature(props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center tabWidth rounded-[4px]">
|
||||
<span className="ml-[5px] text-[12px] text-base-content">
|
||||
{props?.currWidgetsDetails?.type === "initials"
|
||||
? t("initial-teb")
|
||||
: t("signature-tab")}
|
||||
:
|
||||
</span>
|
||||
<input
|
||||
maxLength={props?.currWidgetsDetails?.type === "initials" ? 3 : 30}
|
||||
style={{ fontFamily: props?.fontSelect, color: props?.penColor }}
|
||||
type="text"
|
||||
className="bg-transparent ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||
placeholder={
|
||||
props?.currWidgetsDetails?.type === "initials"
|
||||
? t("initial-type")
|
||||
: t("signature-type")
|
||||
}
|
||||
value={props?.typedSignature}
|
||||
onChange={(e) => {
|
||||
props?.setTypedSignature(e.target.value);
|
||||
if (e.target.value?.trim()?.length > 0) {
|
||||
props?.convertToImg(props?.fontSelect, e.target.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] rounded-[4px] tabWidth">
|
||||
{props?.fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
props?.fontSelect === font.value && "rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
props?.setFontSelect(font.value);
|
||||
props?.convertToImg(font.value, props?.typedSignature);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="py-[5px] px-[10px] text-[20px]"
|
||||
style={{ color: props?.penColor }}
|
||||
>
|
||||
{props?.typedSignature
|
||||
? props?.typedSignature
|
||||
: t("Your-Signature")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TypeSignature;
|
||||
@@ -0,0 +1,58 @@
|
||||
import React, { useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function UploadImage(props) {
|
||||
const { t } = useTranslation();
|
||||
const imageRefs = useRef({});
|
||||
|
||||
const getImageRef = (key) => {
|
||||
if (!imageRefs.current[key]) {
|
||||
imageRefs.current[key] = React.createRef();
|
||||
}
|
||||
return imageRefs.current[key];
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{(props?.isImageSelect || props?.isStampOrImage) && !props?.image ? (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
className={`${props?.currWidgetsDetails?.type === "initials" ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-gray-300 flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
|
||||
onClick={() =>
|
||||
getImageRef(props?.currWidgetsDetails?.key).current.click()
|
||||
}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={props?.onImageChange}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={getImageRef(props?.currWidgetsDetails?.key)}
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo text-base-content"></i>
|
||||
<div className="text-[10px] text-base-content">{t("upload")}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
className={`${props?.currWidgetsDetails?.type === "initials" ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-gray-300 mb-[6px] overflow-hidden`}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={getImageRef(props?.currWidgetsDetails?.key)}
|
||||
src={props?.image?.src}
|
||||
draggable="false"
|
||||
className="object-contain h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UploadImage;
|
||||
@@ -0,0 +1,82 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
//This file is for both Text and textInput widgets
|
||||
export default function TextInput(props) {
|
||||
const [isMultiline, setIsMultiline] = useState();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (!isMultiline && e.key === "Enter") {
|
||||
e.preventDefault(); // disable new line in single-line mode
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (props?.widgetValue) {
|
||||
const isNextLine = props?.widgetValue.includes("\n");
|
||||
if (isNextLine) {
|
||||
setIsMultiline(true);
|
||||
}
|
||||
}
|
||||
}, [props?.widgetValue]);
|
||||
return (
|
||||
<div style={{ position: "relative", width: "100%" }}>
|
||||
{isMultiline ? (
|
||||
<textarea
|
||||
onBlur={props?.handleInputBlur}
|
||||
ref={props?.widgetRef}
|
||||
value={props?.widgetValue ?? ""} //use an empty string "" as a default
|
||||
onChange={(e) => props?.handleOnchangeTextBox(e)}
|
||||
onKeyDown={handleKeyDown}
|
||||
rows={3}
|
||||
placeholder={t("widgets-name.text")}
|
||||
// className={`${textInputcls} resize-none outline-none max-h-[100px] `}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "12px 40px 12px 12px",
|
||||
borderRadius: "20px",
|
||||
border: "1px solid #dcdcdc",
|
||||
fontSize: "16px",
|
||||
resize: "none",
|
||||
outline: "none"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
name="text"
|
||||
ref={props?.widgetRef}
|
||||
type="text"
|
||||
placeholder={t("widgets-name.text")}
|
||||
value={props?.widgetValue ?? ""} //use an empty string "" as a default
|
||||
onChange={(e) => props?.handleOnchangeTextBox(e)}
|
||||
className={`${props?.textInputcls} pr-4`}
|
||||
onBlur={props?.handleInputBlur}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isMultiline && (
|
||||
<>
|
||||
<i
|
||||
data-tooltip-id="toggle-multiline"
|
||||
className="fa-light fa-file-lines"
|
||||
size={20}
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: "12px",
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
color: "#444",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => setIsMultiline((prev) => !prev)}
|
||||
></i>
|
||||
|
||||
<Tooltip id={"toggle-multiline"}>
|
||||
<span>Toggle Multiline Text</span>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { NavLink } from "react-router";
|
||||
|
||||
const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||
@@ -7,6 +7,8 @@ const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||
"OpenSign™";
|
||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||
const { t } = useTranslation();
|
||||
const { selectedMenu } = useSelector((state) => state.sidebar);
|
||||
|
||||
return (
|
||||
<li key={item.title} role="none" className="my-0.5">
|
||||
<NavLink
|
||||
@@ -16,11 +18,9 @@ const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||
: `/${item.objectId}`
|
||||
}
|
||||
className={({ isActive }) =>
|
||||
`${
|
||||
isActive ? " bg-base-300 text-base-content" : ""
|
||||
} flex gap-x-5 items-center justify-start text-left p-3 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||
`${isActive && selectedMenu ? "bg-base-300 text-base-content" : ""} flex gap-x-5 items-center justify-start text-left p-3 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||
}
|
||||
onClick={closeSidebar}
|
||||
onClick={() => closeSidebar(item.title)}
|
||||
tabIndex={isOpen ? 0 : -1}
|
||||
role="menuitem"
|
||||
>
|
||||
|
||||
@@ -7,7 +7,10 @@ import sidebarList, { subSetting } from "../../json/menuJson";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useWindowSize } from "../../hook/useWindowSize";
|
||||
import { toggleSidebar } from "../../redux/reducers/sidebarReducer";
|
||||
import {
|
||||
setSelectedMenu,
|
||||
toggleSidebar
|
||||
} from "../../redux/reducers/sidebarReducer";
|
||||
|
||||
const Sidebar = () => {
|
||||
const { width } = useWindowSize();
|
||||
@@ -29,6 +32,7 @@ const Sidebar = () => {
|
||||
}, []);
|
||||
|
||||
const closeSidebar = () => {
|
||||
dispatch(setSelectedMenu(true));
|
||||
if (width <= 1023) {
|
||||
dispatch(toggleSidebar(false));
|
||||
}
|
||||
@@ -58,10 +62,12 @@ const Sidebar = () => {
|
||||
};
|
||||
|
||||
const toggleSubmenu = (title) => {
|
||||
dispatch(setSelectedMenu(false));
|
||||
setSubmenuOpen({ [title]: !submenuOpen[title] });
|
||||
};
|
||||
|
||||
const handleMenuItem = () => {
|
||||
dispatch(setSelectedMenu(true));
|
||||
closeSidebar();
|
||||
setSubmenuOpen({});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { NavLink } from "react-router";
|
||||
|
||||
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||
@@ -8,6 +8,8 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||
const { t } = useTranslation();
|
||||
const { title, icon, children } = item;
|
||||
const { selectedMenu } = useSelector((state) => state.sidebar);
|
||||
|
||||
return (
|
||||
<li role="none" className="my-0.5">
|
||||
<button
|
||||
@@ -45,11 +47,9 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||
: `/${childItem.objectId}`
|
||||
}
|
||||
className={({ isActive }) =>
|
||||
`${
|
||||
isActive ? "bg-base-300 text-base-content " : ""
|
||||
} pl-4 flex items-center gap-x-5 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||
`${isActive && selectedMenu ? "bg-base-300 text-base-content" : ""} pl-4 flex items-center gap-x-5 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||
}
|
||||
onClick={closeSidebar}
|
||||
onClick={() => closeSidebar(childItem.title)}
|
||||
role="menuitem"
|
||||
tabIndex={submenuOpen ? 0 : -1}
|
||||
>
|
||||
|
||||
@@ -28,6 +28,11 @@ const appName = "OpenSign™";
|
||||
|
||||
export const defaultMailBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
export const defaultMailSubject = `{{sender_name}} has requested you to sign {{document_title}}`;
|
||||
export const nonPresentMaskCss = (base) => ({
|
||||
...base,
|
||||
width: "0px",
|
||||
height: "0px"
|
||||
});
|
||||
|
||||
export const randomId = (digit = 8) => {
|
||||
// 1. Grab a cryptographically-secure 32-bit random value
|
||||
@@ -849,6 +854,7 @@ export const signPdfFun = async (
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const createDocument = async (
|
||||
template,
|
||||
placeholders,
|
||||
@@ -900,6 +906,8 @@ export const createDocument = async (
|
||||
const RedirectUrl = Doc?.RedirectUrl
|
||||
? { RedirectUrl: Doc?.RedirectUrl }
|
||||
: {};
|
||||
const PenColors =
|
||||
Doc?.PenColors?.length > 0 ? { PenColors: Doc?.PenColors } : {};
|
||||
const TemplateId = Doc?.objectId
|
||||
? {
|
||||
TemplateId: {
|
||||
@@ -922,11 +930,7 @@ export const createDocument = async (
|
||||
className: "contracts_Users",
|
||||
objectId: extUserId
|
||||
},
|
||||
CreatedBy: {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: creatorId
|
||||
},
|
||||
CreatedBy: { __type: "Pointer", className: "_User", objectId: creatorId },
|
||||
Signers: signers,
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
@@ -940,7 +944,8 @@ export const createDocument = async (
|
||||
...NotifyOnSignatures,
|
||||
...Bcc,
|
||||
...RedirectUrl,
|
||||
...TemplateId
|
||||
...TemplateId,
|
||||
...PenColors
|
||||
};
|
||||
const remindOnceInEvery = Doc?.RemindOnceInEvery;
|
||||
const TimeToCompleteDays = parseInt(Doc?.TimeToCompleteDays);
|
||||
@@ -949,23 +954,27 @@ export const createDocument = async (
|
||||
if (AutomaticReminders && reminderCount > 15) {
|
||||
return { status: "error", id: "only-15-reminder-allowed" };
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}classes/contracts_Document`;
|
||||
const token = {
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Document`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
const res = await axios.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
}
|
||||
);
|
||||
});
|
||||
if (res) {
|
||||
return { status: "success", id: res.data.objectId, data: res.data };
|
||||
const result = res.data;
|
||||
|
||||
return { status: "success", id: result.objectId, data: result };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("axois err ", err);
|
||||
const message =
|
||||
err?.response?.data?.error || err?.message || "something went wrong.";
|
||||
console.error("error in create document:", message);
|
||||
return { status: "error", id: "something-went-wrong-mssg" };
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1046,8 @@ export const onChangeInput = (
|
||||
dateFormat,
|
||||
fontSize,
|
||||
fontColor,
|
||||
dateDetails
|
||||
dateDetails,
|
||||
textWidgetHeight
|
||||
) => {
|
||||
const isSigners = xyPosition.some(
|
||||
(data) => data.signerPtr || data.Role === "prefill"
|
||||
@@ -1087,6 +1097,16 @@ export const onChangeInput = (
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if (
|
||||
(currentPosition?.type === "text" ||
|
||||
currentPosition?.type === textInputWidget) &&
|
||||
textWidgetHeight &&
|
||||
!value
|
||||
) {
|
||||
return {
|
||||
...position,
|
||||
Height: textWidgetHeight
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...position,
|
||||
@@ -1137,6 +1157,15 @@ export const onChangeInput = (
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if (
|
||||
currentPosition?.type === "text" &&
|
||||
textWidgetHeight &&
|
||||
!value
|
||||
) {
|
||||
return {
|
||||
...positionData,
|
||||
Height: textWidgetHeight
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...positionData,
|
||||
@@ -1605,7 +1634,9 @@ export function onSaveImage(
|
||||
isAutoSign,
|
||||
widgetsType,
|
||||
isApplyAll,
|
||||
imgUrl
|
||||
imgUrl,
|
||||
defaultStampImg,
|
||||
defaultStampType
|
||||
) {
|
||||
// let getIMGWH;
|
||||
const isSignOrInitials =
|
||||
@@ -1616,12 +1647,12 @@ export function onSaveImage(
|
||||
if (position.key === signKey) {
|
||||
return {
|
||||
...position,
|
||||
SignUrl: imgUrl || image.src,
|
||||
ImageType: image.imgType,
|
||||
SignUrl: imgUrl || image?.src || defaultStampImg,
|
||||
ImageType: image?.imgType || defaultStampType,
|
||||
...(isSignOrInitials && { signatureType: signatureType || "" }),
|
||||
options: {
|
||||
...position.options,
|
||||
response: imgUrl || image.src
|
||||
response: imgUrl || image?.src || defaultStampImg
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1643,16 +1674,17 @@ export function onSaveImage(
|
||||
item.type === widgetsType && item.type !== "image"
|
||||
? {
|
||||
...item,
|
||||
SignUrl: image.src,
|
||||
ImageType: image.imgType,
|
||||
SignUrl: image?.src || defaultStampImg,
|
||||
ImageType: image?.imgType || defaultStampType,
|
||||
options: {
|
||||
...item.options,
|
||||
response: image.src
|
||||
response: image?.src || defaultStampImg
|
||||
}
|
||||
}
|
||||
: item // Otherwise, keep it unchanged
|
||||
)
|
||||
}));
|
||||
|
||||
return updatedArray;
|
||||
} else {
|
||||
return updateXYposition;
|
||||
@@ -1792,6 +1824,26 @@ export const isBase64 = (str) => {
|
||||
/^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+)?;base64,[A-Za-z0-9+/=]+$/;
|
||||
return base64Pattern.test(str);
|
||||
};
|
||||
// Break a long word into smaller parts character-by-character
|
||||
const forceBreakLongWord = (word, width, font, fontSize) => {
|
||||
const parts = [];
|
||||
let current = "";
|
||||
|
||||
for (const char of word) {
|
||||
const lineWidth = font.widthOfTextAtSize(current + char, fontSize);
|
||||
|
||||
if (lineWidth <= width) {
|
||||
current += char;
|
||||
} else {
|
||||
parts.push(current);
|
||||
current = char;
|
||||
}
|
||||
}
|
||||
if (current) parts.push(current);
|
||||
|
||||
return parts;
|
||||
};
|
||||
|
||||
//function for embed all type widgets in document using pdf-lib
|
||||
export const embedWidgetsToDoc = async (
|
||||
widgets,
|
||||
@@ -2080,41 +2132,59 @@ export const embedWidgetsToDoc = async (
|
||||
const lines = [];
|
||||
let currentLine = "";
|
||||
|
||||
for (const word of textContent?.split(" ")) {
|
||||
//get text line width
|
||||
const lineWidth = font.widthOfTextAtSize(
|
||||
`${currentLine} ${word}`,
|
||||
fontSize
|
||||
);
|
||||
//check text content line width is less or equal to container width
|
||||
if (lineWidth <= width) {
|
||||
currentLine += ` ${word}`;
|
||||
const words = textContent?.split(" ");
|
||||
for (let word of words) {
|
||||
const testLine = currentLine ? currentLine + " " + word : word;
|
||||
const testWidth = font.widthOfTextAtSize(testLine, fontSize);
|
||||
|
||||
if (testWidth <= width) {
|
||||
currentLine = testLine;
|
||||
} else {
|
||||
lines.push(currentLine.trim());
|
||||
currentLine = `${word}`;
|
||||
// If single long word
|
||||
if (font.widthOfTextAtSize(word, fontSize) > width) {
|
||||
// Break a long word into smaller parts character-by-character
|
||||
const brokenParts = forceBreakLongWord(
|
||||
word,
|
||||
width,
|
||||
font,
|
||||
fontSize
|
||||
);
|
||||
|
||||
// push currentLine before breaking word
|
||||
if (currentLine.trim()) lines.push(currentLine.trim());
|
||||
|
||||
// push ALL broken parts in order
|
||||
brokenParts.forEach((p) => lines.push(p));
|
||||
|
||||
currentLine = "";
|
||||
} else {
|
||||
// push current line and start new
|
||||
if (currentLine.trim()) lines.push(currentLine.trim());
|
||||
currentLine = word;
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.push(currentLine.trim());
|
||||
if (currentLine.trim()) lines.push(currentLine.trim());
|
||||
return lines;
|
||||
};
|
||||
// Function to break text into lines based on when user go next line on press enter button
|
||||
const breakTextIntoLines = (textContent, width) => {
|
||||
const lines = [];
|
||||
for (const word of textContent.split("\n")) {
|
||||
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
|
||||
const finalLines = [];
|
||||
const paragraphs = textContent.split("\n"); // preserve user order
|
||||
for (const para of paragraphs) {
|
||||
const lineWidth = font.widthOfTextAtSize(para, fontSize);
|
||||
//checking string length to container width
|
||||
//if string length is less then container width it means user press enter button
|
||||
if (lineWidth <= width) {
|
||||
lines.push(word);
|
||||
}
|
||||
//else adjust text content according to width and send it in new line
|
||||
else {
|
||||
const newLine = NewbreakTextIntoLines(word, width);
|
||||
lines.push(...newLine);
|
||||
// user forced newline
|
||||
finalLines.push(para);
|
||||
} else {
|
||||
// auto wrap paragraph
|
||||
finalLines.push(...NewbreakTextIntoLines(para, width));
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
return finalLines;
|
||||
};
|
||||
//check if text content have `\n` string it means user press enter to go next line and handle condition
|
||||
//else auto adjust text content according to container width
|
||||
@@ -3022,13 +3092,17 @@ export const getDefaultSignature = async (objectId) => {
|
||||
const defaultInitial = res?.Initials
|
||||
? await getBase64FromUrl(res?.Initials, true)
|
||||
: "";
|
||||
const defaultStamp = res?.Stamp
|
||||
? await getBase64FromUrl(res?.Stamp, true)
|
||||
: "";
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
res: {
|
||||
id: result?.id,
|
||||
defaultSignature: defaultSignature,
|
||||
defaultInitial: defaultInitial
|
||||
defaultInitial: defaultInitial,
|
||||
defaultStamp: defaultStamp
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -3927,51 +4001,55 @@ export const sendEmailToSigners = async (
|
||||
* @returns {Promise<File>} - A promise that resolves with a new PNG File.
|
||||
*/
|
||||
export function convertJpegToPng(base64Image, filename) {
|
||||
const arr = base64Image.split(",");
|
||||
const mimeMatch = arr[0].match(/:(.*?);/);
|
||||
if (!mimeMatch) throw new Error("Invalid dataURL");
|
||||
const mime = mimeMatch[1];
|
||||
const type = mime.split("/")[1];
|
||||
if (type === "png") {
|
||||
return base64Image;
|
||||
} else {
|
||||
const bstr = atob(arr[1]);
|
||||
let n = bstr.length;
|
||||
const u8arr = new Uint8Array(n);
|
||||
while (n--) u8arr[n] = bstr.charCodeAt(n);
|
||||
const inputFile = new File([u8arr], `${filename}.${type}`, { type: mime });
|
||||
return new Promise((resolve, reject) => {
|
||||
// ensure it’s JPEG/JPG (you can remove this check if not needed)
|
||||
if (!/image\/jpe?g/.test(inputFile.type)) {
|
||||
return reject(new Error("Input must be a JPEG or JPG image"));
|
||||
}
|
||||
if (base64Image) {
|
||||
const arr = base64Image.split(",");
|
||||
const mimeMatch = arr[0].match(/:(.*?);/);
|
||||
if (!mimeMatch) throw new Error("Invalid dataURL");
|
||||
const mime = mimeMatch[1];
|
||||
const type = mime.split("/")[1];
|
||||
if (type === "png") {
|
||||
return base64Image;
|
||||
} else {
|
||||
const bstr = atob(arr[1]);
|
||||
let n = bstr.length;
|
||||
const u8arr = new Uint8Array(n);
|
||||
while (n--) u8arr[n] = bstr.charCodeAt(n);
|
||||
const inputFile = new File([u8arr], `${filename}.${type}`, {
|
||||
type: mime
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
// ensure it’s JPEG/JPG (you can remove this check if not needed)
|
||||
if (!/image\/jpe?g/.test(inputFile.type)) {
|
||||
return reject(new Error("Input must be a JPEG or JPG image"));
|
||||
}
|
||||
|
||||
// Read the file as a data URL
|
||||
const reader = new FileReader();
|
||||
reader.onerror = () => reject(new Error("Failed to read the file"));
|
||||
reader.onload = () => {
|
||||
const img = new Image();
|
||||
img.onerror = () => reject(new Error("Failed to load image"));
|
||||
img.onload = () => {
|
||||
// draw image onto a canvas
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
// Read the file as a data URL
|
||||
const reader = new FileReader();
|
||||
reader.onerror = () => reject(new Error("Failed to read the file"));
|
||||
reader.onload = () => {
|
||||
const img = new Image();
|
||||
img.onerror = () => reject(new Error("Failed to load image"));
|
||||
img.onload = () => {
|
||||
// draw image onto a canvas
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// directly get PNG as base64 data URL
|
||||
try {
|
||||
const pngDataUrl = canvas.toDataURL("image/png");
|
||||
resolve(pngDataUrl);
|
||||
} catch (err) {
|
||||
reject(new Error("Failed to convert canvas to PNG"));
|
||||
}
|
||||
// directly get PNG as base64 data URL
|
||||
try {
|
||||
const pngDataUrl = canvas.toDataURL("image/png");
|
||||
resolve(pngDataUrl);
|
||||
} catch (err) {
|
||||
reject(new Error("Failed to convert canvas to PNG"));
|
||||
}
|
||||
};
|
||||
img.src = reader.result;
|
||||
};
|
||||
img.src = reader.result;
|
||||
};
|
||||
reader.readAsDataURL(inputFile);
|
||||
});
|
||||
reader.readAsDataURL(inputFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//function is used to get assigned signer's email
|
||||
@@ -4080,3 +4158,8 @@ export const handleBackground = (data, isNeedSign, uniqueId) => {
|
||||
return "rgba(203, 233, 237, 0.69)";
|
||||
}
|
||||
};
|
||||
|
||||
export function getBase64MimeType(base64) {
|
||||
const match = base64.match(/^data:(image\/[a-zA-Z0-9+.-]+);base64,/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ export const templateReportTour = [
|
||||
selector: "[data-tut=reactourFirst]",
|
||||
content: <Trans i18nKey="tour-mssg.report-1" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "11px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=reactourSecond]",
|
||||
content: <Trans i18nKey="tour-mssg.redirect" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=tourbulksend]",
|
||||
content: <Trans i18nKey="tour-mssg.bulksend" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=reactourThird]",
|
||||
@@ -42,6 +42,6 @@ export const templateReportTour = [
|
||||
</Trans>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
nonPresentMaskCss
|
||||
} from "../constant/Utils";
|
||||
import Header from "../components/Header";
|
||||
import Footer from "../components/Footer";
|
||||
import Sidebar from "../components/sidebar/Sidebar";
|
||||
@@ -6,7 +9,9 @@ import Tour from "../primitives/Tour";
|
||||
import axios from "axios";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Parse from "parse";
|
||||
import { useNavigate, Outlet } from "react-router";
|
||||
import {
|
||||
Outlet
|
||||
} from "react-router";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
@@ -16,9 +21,8 @@ const HomeLayout = () => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const arr = useSelector((state) => state.TourSteps);
|
||||
const tourArr = useSelector((state) => state.TourSteps);
|
||||
const isValidSession = useSelector((state) => state.user.isValidSession);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||
@@ -55,7 +59,7 @@ const HomeLayout = () => {
|
||||
dispatch(sessionStatus(true));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("catch");
|
||||
console.error("error in authentication:", error?.message);
|
||||
// Session token is invalid or there was an error
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
@@ -68,40 +72,36 @@ const HomeLayout = () => {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (arr && arr.length > 0) {
|
||||
if (tourArr && tourArr.length > 0) {
|
||||
handleDynamicSteps();
|
||||
} else {
|
||||
setIsTour(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [arr]);
|
||||
}, [tourArr]);
|
||||
|
||||
const handleDynamicSteps = () => {
|
||||
const github = "https://github.com/OpenSignLabs/OpenSign";
|
||||
if (arr && arr.length > 0) {
|
||||
// const resArr = arr;
|
||||
const resArr = arr.map((obj, index) => {
|
||||
if (arr.length - 1 === index) {
|
||||
return { ...obj };
|
||||
if (tourArr && tourArr.length > 0) {
|
||||
const resArr = tourArr.map((obj, index) => {
|
||||
if (tourArr.length - 1 === index) {
|
||||
return obj;
|
||||
} else {
|
||||
return {
|
||||
...obj,
|
||||
actions: () => {
|
||||
setIsCloseBtn(false);
|
||||
}
|
||||
};
|
||||
return { ...obj, actions: () => setIsCloseBtn(false) };
|
||||
}
|
||||
});
|
||||
setTourConfigs([
|
||||
{
|
||||
selector: '[data-tut="nonpresentmask"]',
|
||||
content: t("tour-mssg.home-layout-1"),
|
||||
position: "center"
|
||||
position: "center",
|
||||
styles: { fontSize: "13px", maskArea: nonPresentMaskCss }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="tourbutton"]',
|
||||
content: t("tour-mssg.home-layout-2"),
|
||||
position: "top"
|
||||
position: "top",
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
...resArr,
|
||||
{
|
||||
@@ -122,20 +122,19 @@ const HomeLayout = () => {
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
position: "center"
|
||||
position: "center",
|
||||
styles: { fontSize: "13px", maskArea: nonPresentMaskCss }
|
||||
}
|
||||
]);
|
||||
checkTourStatus();
|
||||
}
|
||||
};
|
||||
const closeTour = async () => {
|
||||
// console.log("closeTour");
|
||||
setIsTour(false);
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const appId = localStorage.getItem("parseAppId");
|
||||
const json = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const extUserId = json && json.length > 0 && json[0].objectId;
|
||||
// console.log("extUserId ", extUserId)
|
||||
|
||||
let updatedTourStatus = [];
|
||||
if (tourStatusArr.length > 0) {
|
||||
@@ -209,15 +208,15 @@ const HomeLayout = () => {
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
// disableInteraction={true}
|
||||
scrollOffset={-100}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
{isTour && (
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
// scrollOffset={-100}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,8 @@ const Dashboard = () => {
|
||||
return {
|
||||
selector: `[data-tut=${col.widget.data.tourSection}]`,
|
||||
content: t(`tour-mssg.${col.widget.label}`),
|
||||
position: "top"
|
||||
position: "top",
|
||||
styles: { fontSize: "13px" }
|
||||
// style: { backgroundColor: "#abd4d2" },
|
||||
};
|
||||
});
|
||||
|
||||
@@ -84,6 +84,8 @@ const Forms = (props) => {
|
||||
const [isCorrectPass, setIsCorrectPass] = useState(true);
|
||||
const [isAdvanceOpt, setIsAdvanceOpt] = useState(false);
|
||||
const [bcc, setBcc] = useState([]);
|
||||
const pensList = ["blue", "red", "black"];
|
||||
const [selectedColors, setSelectedColors] = useState(pensList);
|
||||
|
||||
const handleStrInput = (e) => {
|
||||
setIsCorrectPass(true);
|
||||
@@ -417,6 +419,8 @@ const Forms = (props) => {
|
||||
object.set("RemindOnceInEvery", remindOnceInEvery);
|
||||
object.set("IsTourEnabled", isTourEnabled);
|
||||
object.set("TimeToCompleteDays", TimeToCompleteDays);
|
||||
object.set("PenColors", selectedColors);
|
||||
|
||||
object.set("AllowModifications", false);
|
||||
object.set("IsEnableOTP", false);
|
||||
if (formData.NotifyOnSignatures !== undefined) {
|
||||
@@ -456,6 +460,7 @@ const Forms = (props) => {
|
||||
if (res) {
|
||||
setSigners([]);
|
||||
setBcc([]);
|
||||
setSelectedColors(pensList);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
@@ -538,6 +543,7 @@ const Forms = (props) => {
|
||||
setIsReset(true);
|
||||
setSigners([]);
|
||||
setBcc([]);
|
||||
setSelectedColors(pensList);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
@@ -651,6 +657,20 @@ const Forms = (props) => {
|
||||
setFormData((obj) => ({ ...obj, NotifyOnSignatures: value }));
|
||||
};
|
||||
|
||||
const handleColorsChange = (color) => {
|
||||
setSelectedColors((prev) => {
|
||||
// If user tries to uncheck the last remaining color → block it
|
||||
if (prev.length === 1 && prev.includes(color)) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Normal toggle behavior
|
||||
return prev.includes(color)
|
||||
? prev.filter((c) => c !== color) // remove
|
||||
: [...prev, color]; // add
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${isSubmit ? "" : "shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content"}`}
|
||||
@@ -1214,6 +1234,46 @@ const Forms = (props) => {
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
/>
|
||||
</div>
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-3">
|
||||
<label htmlFor="penColors">
|
||||
{t("pen-colors")}
|
||||
<a
|
||||
data-tooltip-id="pen-colors-tooltip"
|
||||
className="ml-1"
|
||||
>
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<Tooltip id="pen-colors-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("pen-colors")}</p>
|
||||
<div>{t("pen-colors-help")}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{pensList.map((color) => (
|
||||
<div
|
||||
key={color}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="penColors"
|
||||
checked={selectedColors.includes(color)}
|
||||
onChange={() => handleColorsChange(color)}
|
||||
/>
|
||||
<div className="hover:underline underline-offset-2 cursor-default capitalize">
|
||||
{color}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,14 @@ import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { sanitizeFileName } from "../utils";
|
||||
|
||||
const COLOR_CLASS = {
|
||||
blue: "text-blue-600",
|
||||
red: "text-red-600",
|
||||
black: "text-black",
|
||||
white: "text-white"
|
||||
};
|
||||
|
||||
const ManageSign = () => {
|
||||
const { t } = useTranslation();
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
@@ -29,10 +37,12 @@ const ManageSign = () => {
|
||||
const [isInitials, setIsInitials] = useState(false);
|
||||
const [id, setId] = useState("");
|
||||
const [imgInitials, setImgInitials] = useState("");
|
||||
const [stamp, setStamp] = useState();
|
||||
const canvasRef = useRef(null);
|
||||
const imageRef = useRef(null);
|
||||
const initailsRef = useRef(null);
|
||||
const imgInitialsRef = useRef(null);
|
||||
const stampRef = useRef(null);
|
||||
useEffect(() => {
|
||||
fetchUserSign();
|
||||
// eslint-disable-next-line
|
||||
@@ -63,6 +73,9 @@ const ManageSign = () => {
|
||||
setIsInitials(true);
|
||||
setImgInitials(res?.Initials);
|
||||
}
|
||||
if (res && res?.Stamp) {
|
||||
setStamp(res?.Stamp);
|
||||
}
|
||||
} else {
|
||||
if (User?.get("name")) {
|
||||
const sanitizename = generateTitleFromFilename(User?.get("name"));
|
||||
@@ -171,11 +184,24 @@ const ManageSign = () => {
|
||||
} else {
|
||||
initialsUrl = imgInitials;
|
||||
}
|
||||
const isStampUrl = stamp?.includes("https") || stamp?.includes("http");
|
||||
|
||||
let stampFile;
|
||||
if (stamp && !isStampUrl) {
|
||||
stampFile = base64StringtoFile(stamp, `${replaceSpace}_stamp`);
|
||||
}
|
||||
let stampUrl;
|
||||
if (stampFile && !isStampUrl) {
|
||||
stampUrl = await uploadFile(stampFile);
|
||||
} else {
|
||||
stampUrl = stamp;
|
||||
}
|
||||
if (imgUrl) {
|
||||
await saveEntry({
|
||||
name: signName,
|
||||
url: imgUrl,
|
||||
initialsUrl: initialsUrl
|
||||
initialsUrl: initialsUrl,
|
||||
stampUrl: stampUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -232,7 +258,8 @@ const ManageSign = () => {
|
||||
userId: User,
|
||||
initials: obj.initialsUrl,
|
||||
id: id,
|
||||
title: obj.name
|
||||
title: obj.name,
|
||||
stamp: obj?.stampUrl
|
||||
});
|
||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||
return res;
|
||||
@@ -274,6 +301,28 @@ const ManageSign = () => {
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
const handleClearStamp = () => {
|
||||
if (stampRef.current) {
|
||||
stampRef.current.clear();
|
||||
}
|
||||
if (stampRef.current) {
|
||||
stampRef.current.value = "";
|
||||
}
|
||||
setStamp("");
|
||||
if (image) {
|
||||
setIsValue(true);
|
||||
}
|
||||
};
|
||||
const onStampChange = async (event) => {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
const file = event.target.files[0];
|
||||
const base64Img = await toDataUrl(file);
|
||||
setStamp(base64Img);
|
||||
setIsValue(true);
|
||||
} else {
|
||||
setStamp("");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
|
||||
{isLoader && (
|
||||
@@ -287,9 +336,9 @@ const ManageSign = () => {
|
||||
<div className="text-[20px] font-semibold m-[10px] md:m-0 mb-2">
|
||||
{t("my-signature")}
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-0 md:gap-[12px]">
|
||||
<div className="flex flex-col md:flex-row gap-2 md:gap-5">
|
||||
<div className="relative">
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
<span className="font-medium select-none flex mb-[10px]">
|
||||
{t("signature")}
|
||||
</span>
|
||||
<input
|
||||
@@ -300,7 +349,7 @@ const ManageSign = () => {
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<div className="relative">
|
||||
<div>
|
||||
{image ? (
|
||||
<div className="mysignatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden">
|
||||
<img
|
||||
@@ -323,23 +372,24 @@ const ManageSign = () => {
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
<div className="penContainerDefault flex flex-row justify-between">
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<div>
|
||||
{!image && (
|
||||
<div className="flex flex-row gap-1.5 m-[5px]">
|
||||
{allColor.map((data, key) => {
|
||||
<div className="flex flex-row gap-1 m-[5px]">
|
||||
{allColor.map((color, key) => {
|
||||
const selected = penColor === color;
|
||||
return (
|
||||
<i
|
||||
key={key}
|
||||
onClick={() => setPenColor(allColor[key])}
|
||||
className={`border-b-[2px] ${key === 0 && penColor === "blue" ? "border-blue-600" : key === 1 && penColor === "red" ? "border-red-500" : key === 2 && penColor === "black" ? "border-black" : "border-white"} text-[${data}] text-[16px] fa-light fa-pen-nib`}
|
||||
onClick={() => setPenColor(color)}
|
||||
className={`${COLOR_CLASS[color] ?? ""} ${selected ? "border-current" : "border-white"} cursor-pointer border-b-[2px] pb-[2px] text-[16px] fa-light fa-pen-nib`}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 text-sm md:text-base mr-1">
|
||||
<div className="flex flex-row gap-2 text-sm pr-2">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link"
|
||||
@@ -364,7 +414,8 @@ const ManageSign = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
|
||||
<div>
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
{t("initials")}
|
||||
</span>
|
||||
@@ -396,26 +447,29 @@ const ManageSign = () => {
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-row justify-between w-[183px]">
|
||||
<div
|
||||
className={`${isInitials ? "justify-between" : ""} flex flex-row gap-1`}
|
||||
>
|
||||
<div>
|
||||
{!isInitials && (
|
||||
<div className="flex flex-row gap-1.5 m-[5px]">
|
||||
{allColor.map((data, key) => {
|
||||
<div className="flex flex-row gap-1 m-[5px]">
|
||||
{allColor.map((color, key) => {
|
||||
const selected = initialPen === color;
|
||||
return (
|
||||
<i
|
||||
key={key}
|
||||
onClick={() => setInitialPen(allColor[key])}
|
||||
className={`border-b-[2px] ${key === 0 && initialPen === "blue" ? "border-blue-600" : key === 1 && initialPen === "red" ? "border-red-500" : key === 2 && initialPen === "black" ? "border-black" : "border-white"} text-[${data}] text-[16px] fa-light fa-pen-nib`}
|
||||
onClick={() => setInitialPen(color)}
|
||||
className={`${COLOR_CLASS[color] ?? ""} ${selected ? "border-current" : "border-white"} cursor-pointer border-b-[2px] pb-[2px] text-[16px] fa-light fa-pen-nib`}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row gap-1">
|
||||
<div className="flex flex-row gap-1.5 text-sm">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
className="op-link"
|
||||
onClick={() => handleUploadInitials()}
|
||||
>
|
||||
{t("upload")}
|
||||
@@ -423,7 +477,7 @@ const ManageSign = () => {
|
||||
<div>
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
className="op-link"
|
||||
onClick={() => handleClearInitials()}
|
||||
>
|
||||
{t("clear")}
|
||||
@@ -433,6 +487,48 @@ const ManageSign = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
Stamp
|
||||
</span>
|
||||
|
||||
<div
|
||||
onClick={() => stampRef.current.click()}
|
||||
className="mystampCanvas opensigndark:bg-[#1f2937] cursor-pointer border-[2px] opensigncss:border-[#888] opensigndark:border-[#4b5563] rounded-box flex flex-col overflow-hidden w-full h-full aspect-[5/2] justify-center items-center mr-2"
|
||||
>
|
||||
{stamp ? (
|
||||
<img
|
||||
alt="signature"
|
||||
src={stamp}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<input
|
||||
onChange={onStampChange}
|
||||
type="file"
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
hidden
|
||||
ref={stampRef}
|
||||
/>
|
||||
<i className="fa-light text-base-content fa-cloud-upload-alt text-[28px]"></i>
|
||||
<div className="text-[15px] text-base-content">
|
||||
{t("upload")}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end pr-3">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm"
|
||||
onClick={() => handleClearStamp()}
|
||||
>
|
||||
{t("clear")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-3 ml-2 md:ml-0">
|
||||
<button
|
||||
|
||||
@@ -20,7 +20,7 @@ const DriveBody = lazyWithRetry(
|
||||
() => import("../components/opensigndrive/DriveBody")
|
||||
);
|
||||
const dropdowncss =
|
||||
"absolute right-0 py-2 text-[1rem] text-left bg-white border-[1px] border-gray-300 min-w-1 hover:bg-gray-300 rounded-[0.25rem] z-[800]";
|
||||
"absolute right-0 py-2 text-[1rem] text-left bg-base-100 border-[1px] border-gray-300 min-w-1 rounded-[0.25rem] z-[800]";
|
||||
const AppLoader = () => {
|
||||
return (
|
||||
<div className="h-[100vh] flex justify-center items-center">
|
||||
@@ -98,7 +98,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSecond"]',
|
||||
@@ -109,7 +109,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourThird"]',
|
||||
@@ -120,7 +120,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourForth"]',
|
||||
@@ -131,7 +131,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const getDetails = async () => {
|
||||
@@ -161,7 +161,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSixth"]',
|
||||
@@ -172,7 +172,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSeventh"]',
|
||||
@@ -183,7 +183,7 @@ function Opensigndrive() {
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
let newTour = [...tourConfigs, ...addMoreTour];
|
||||
@@ -688,7 +688,7 @@ function Opensigndrive() {
|
||||
</div>
|
||||
{/* Mobile search toggle */}
|
||||
<button
|
||||
className="md:hidden p-2 flex justify-center items-center focus:outline-none rounded-md hover:bg-gray-200 text-[18px]"
|
||||
className="md:hidden p-2 flex justify-center items-center focus:outline-none rounded-md hover:bg-base-300 text-[18px]"
|
||||
aria-label="Search"
|
||||
onClick={() => setMobileSearchOpen((open) => !open)}
|
||||
>
|
||||
@@ -699,7 +699,7 @@ function Opensigndrive() {
|
||||
</button>
|
||||
<div
|
||||
id="folder-menu"
|
||||
className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`}
|
||||
className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-base-300 p-2 rounded-md`}
|
||||
onClick={handleFolderOptions}
|
||||
>
|
||||
<div data-tut="reactourSecond">
|
||||
@@ -749,7 +749,7 @@ function Opensigndrive() {
|
||||
>
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
className="cursor-pointer flex flex-row items-center justify-center p-2 hover:bg-gray-200 rounded-md"
|
||||
className="cursor-pointer flex flex-row items-center justify-center p-2 hover:bg-base-300 rounded-md"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i
|
||||
@@ -767,9 +767,7 @@ function Opensigndrive() {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isShowSort ? "dropdown-menu show" : "dropdown-menu"
|
||||
}
|
||||
className={`${isShowSort ? "dropdown-menu show" : "dropdown-menu"} bg-base-100 border-[1px] border-gray-300`}
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isShowSort ? "true" : "false"}
|
||||
>
|
||||
@@ -817,7 +815,7 @@ function Opensigndrive() {
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer p-2 hover:bg-gray-200 rounded-md flex justify-center items-center"
|
||||
className="cursor-pointer p-2 hover:bg-base-300 rounded-md flex justify-center items-center"
|
||||
data-tut="reactourForth"
|
||||
onClick={handleViewOption}
|
||||
>
|
||||
@@ -833,7 +831,7 @@ function Opensigndrive() {
|
||||
onClick={() => setIsOptions(!isOptions)}
|
||||
>
|
||||
<div
|
||||
className="p-3 flex items-center justify-center cursor-pointer rounded-md hover:bg-gray-200"
|
||||
className="p-3 flex items-center justify-center cursor-pointer rounded-md hover:bg-base-300"
|
||||
data-tut="reactourSecond"
|
||||
>
|
||||
<i
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
setDefaultSignImg,
|
||||
resetWidgetState,
|
||||
setPrefillImg,
|
||||
setTypedSignFont
|
||||
setTypedSignFont,
|
||||
setMyStamp
|
||||
} from "../redux/reducers/widgetSlice";
|
||||
import {
|
||||
contractDocument,
|
||||
@@ -155,12 +156,10 @@ function PdfRequestFiles(
|
||||
const [assignedWidgetId, setAssignedWidgetId] = useState([]);
|
||||
const [showSignPagenumber, setShowSignPagenumber] = useState([]);
|
||||
const [owner, setOwner] = useState({});
|
||||
const [tenant, setTenant] = useState({});
|
||||
const [tenantMailTemplate, setTenantMailTemplate] = useState({
|
||||
body: "",
|
||||
subject: ""
|
||||
});
|
||||
const [isOptionalDetails, setIsOptionalDetails] = useState(false);
|
||||
const isMobile = window.innerWidth < 767;
|
||||
let isGuestSignFlow = false;
|
||||
let sendmail;
|
||||
@@ -245,7 +244,6 @@ function PdfRequestFiles(
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
setTenant(tenantDetails || {});
|
||||
if (tenantDetails?.RequestBody) {
|
||||
setTenantMailTemplate({
|
||||
body: tenantDetails?.RequestBody,
|
||||
@@ -532,6 +530,7 @@ function PdfRequestFiles(
|
||||
const initials = defaultSignRes?.res?.defaultInitial || "";
|
||||
dispatch(setDefaultSignImg(sign));
|
||||
dispatch(setMyInitial(initials));
|
||||
dispatch(setMyStamp(defaultSignRes?.res?.defaultStamp));
|
||||
} else {
|
||||
dispatch(setSaveSignCheckbox({ isVisible: true }));
|
||||
}
|
||||
@@ -1103,7 +1102,7 @@ function PdfRequestFiles(
|
||||
? t("signature-validate-alert", { minRequiredCount })
|
||||
: t("signature-validate-alert-2"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
//function for get pdf page details
|
||||
@@ -1260,7 +1259,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourFirst"]',
|
||||
@@ -1272,7 +1271,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdfArea"]',
|
||||
@@ -1284,7 +1283,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdftools"]',
|
||||
@@ -1296,7 +1295,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourFifth"]',
|
||||
@@ -1308,7 +1307,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const signedByStep = {
|
||||
@@ -1321,7 +1320,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
};
|
||||
//checking if signed by user component exist then add signed by tour step
|
||||
const signedBy =
|
||||
@@ -1340,7 +1339,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
};
|
||||
//checking if AllowModifications is true then add allow widgets panel tour step
|
||||
const allModifyWidgets = {
|
||||
@@ -1353,7 +1352,7 @@ function PdfRequestFiles(
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
};
|
||||
|
||||
//handle signed by panel index if signed by exist then 2 else 1 to add tour step
|
||||
@@ -1823,6 +1822,7 @@ function PdfRequestFiles(
|
||||
!alreadySign &&
|
||||
requestSignTourFunction()}
|
||||
<Tour
|
||||
key={`tour-min-required-${minRequiredCount || 0}`}
|
||||
showNumber={false}
|
||||
showNavigation={false}
|
||||
showNavigationNumber={false}
|
||||
@@ -2224,6 +2224,7 @@ function PdfRequestFiles(
|
||||
setUniqueId={setUniqueId}
|
||||
signatureTypes={signatureType}
|
||||
allowCellResize={false}
|
||||
penColors={pdfDetails?.[0]?.PenColors}
|
||||
/>
|
||||
)}
|
||||
<DownloadPdfZip
|
||||
|
||||
@@ -75,7 +75,6 @@ function PlaceHolderSign() {
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const isShowModal = useSelector((state) => state.widget.isShowModal);
|
||||
const editorRef = useRef();
|
||||
const { state } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [defaultBody, setDefaultBody] = useState("");
|
||||
const [defaultSubject, setDefaultSubject] = useState("");
|
||||
@@ -1152,14 +1151,14 @@ function PlaceHolderSign() {
|
||||
className="flex flex-row items-center op-link op-link-primary"
|
||||
>
|
||||
<i className="fa-light fa-copy" />
|
||||
<span className=" hidden md:block ml-1 ">{t("copy-link")}</span>
|
||||
<span className="hidden md:block ml-1">{t("copy-link")}</span>
|
||||
</button>
|
||||
<ShareButton
|
||||
title={t("sign-url")}
|
||||
text={t("sign-url")}
|
||||
url={data.url}
|
||||
>
|
||||
<i className="fa-light fa-share-from-square op-link data-[theme=opensigndark]:op-link-primary data-[theme=opensigncss]:op-link-secondary no-underline"></i>
|
||||
<i className="fa-light fa-share-from-square op-link opensigncss:op-link-secondary opensigndark:op-link-primary no-underline"></i>
|
||||
</ShareButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1372,7 +1371,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="recipientArea"]',
|
||||
@@ -1384,7 +1383,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="addRecipient"]',
|
||||
@@ -1396,7 +1395,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="addWidgets"]',
|
||||
@@ -1408,7 +1407,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdfArea"]',
|
||||
@@ -1420,7 +1419,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdftools"]',
|
||||
@@ -1432,7 +1431,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="headerArea"]',
|
||||
@@ -1444,7 +1443,7 @@ function PlaceHolderSign() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1641,13 +1640,16 @@ function PlaceHolderSign() {
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (["signature"].includes(position.type)) {
|
||||
else if (["signature", "initials"].includes(position.type)) {
|
||||
return {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
name: defaultdata.name,
|
||||
hint: defaultdata?.hint || ""
|
||||
hint: defaultdata?.hint || "",
|
||||
...(defaultdata?.penColors?.length > 0 && {
|
||||
penColors: defaultdata?.penColors
|
||||
})
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -1844,7 +1846,7 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="assignSigner"]',
|
||||
content: t("attach-signer-tour"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const prefillWidgetTour = [
|
||||
@@ -1852,7 +1854,7 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="IsSigned"]',
|
||||
content: t("text-field-tour"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const signatureWidgetTour = [
|
||||
@@ -1860,7 +1862,7 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="isSignatureWidget"]',
|
||||
content: t("signature-field-widget", { signersName }),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1963,6 +1965,10 @@ function PlaceHolderSign() {
|
||||
const RedirectUrl = updateDocument?.[0]?.RedirectUrl
|
||||
? { RedirectUrl: updateDocument?.[0]?.RedirectUrl }
|
||||
: {};
|
||||
const penColors =
|
||||
updateDocument?.[0]?.PenColors?.length > 0
|
||||
? { PenColors: updateDocument?.[0]?.PenColors }
|
||||
: {};
|
||||
const data = {
|
||||
...(updateDocument?.[0]?.URL ? { URL: updateDocument?.[0]?.URL } : {}),
|
||||
Name: updateDocument?.[0]?.Name || "",
|
||||
@@ -1979,6 +1985,7 @@ function PlaceHolderSign() {
|
||||
: false,
|
||||
TimeToCompleteDays:
|
||||
parseInt(updateDocument?.[0]?.TimeToCompleteDays) || 15,
|
||||
...penColors,
|
||||
...Bcc,
|
||||
...RedirectUrl
|
||||
};
|
||||
@@ -2017,8 +2024,6 @@ function PlaceHolderSign() {
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfig}
|
||||
isOpen={placeholderTour}
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
<Tour
|
||||
onRequestClose={() => setSignerExistModal(false)}
|
||||
@@ -2489,6 +2494,7 @@ function PlaceHolderSign() {
|
||||
isSave={true}
|
||||
setUniqueId={setUniqueId}
|
||||
signatureTypes={signatureType}
|
||||
penColors={pdfDetails?.[0]?.PenColors}
|
||||
/>
|
||||
)}
|
||||
<ModalUi
|
||||
|
||||
@@ -22,8 +22,6 @@ const Preferences = () => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTopLoader, setIsTopLoader] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
@@ -44,7 +42,6 @@ const Preferences = () => {
|
||||
const [is12HourTime, setIs12HourTime] = useState(false);
|
||||
const [isLTVEnabled, setIsLTVEnabled] = useState(false);
|
||||
const [fileNameFormat, setFileNameFormat] = useState("DOCNAME");
|
||||
const [userInfo, setUserInfo] = useState({});
|
||||
const [tenantInfo, setTenantInfo] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -87,7 +84,6 @@ const Preferences = () => {
|
||||
const getUser = extUser.data?.result;
|
||||
if (getUser) {
|
||||
const _getUser = JSON.parse(JSON.stringify(getUser));
|
||||
setUserInfo(_getUser);
|
||||
setIsNotifyOnSignatures(
|
||||
_getUser?.NotifyOnSignatures !== undefined
|
||||
? _getUser?.NotifyOnSignatures
|
||||
|
||||
@@ -63,7 +63,8 @@ import {
|
||||
setDefaultSignImg,
|
||||
setIsShowModal,
|
||||
resetWidgetState,
|
||||
setTypedSignFont
|
||||
setTypedSignFont,
|
||||
setMyStamp
|
||||
} from "../redux/reducers/widgetSlice";
|
||||
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
@@ -192,7 +193,6 @@ function SignYourSelf() {
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current, isSidebar]);
|
||||
|
||||
//function for get document details for perticular signer with signer'object id
|
||||
const getDocumentDetails = async (showComplete) => {
|
||||
try {
|
||||
@@ -267,6 +267,7 @@ function SignYourSelf() {
|
||||
);
|
||||
dispatch(setDefaultSignImg(defaultSignRes?.res?.defaultSignature));
|
||||
dispatch(setMyInitial(defaultSignRes?.res?.defaultInitial));
|
||||
dispatch(setMyStamp(defaultSignRes?.res?.defaultStamp));
|
||||
} else {
|
||||
dispatch(setSaveSignCheckbox({ isVisible: true }));
|
||||
}
|
||||
@@ -892,7 +893,7 @@ function SignYourSelf() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSecond"]',
|
||||
@@ -904,7 +905,7 @@ function SignYourSelf() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdftools"]',
|
||||
@@ -916,7 +917,7 @@ function SignYourSelf() {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1215,7 +1216,7 @@ function SignYourSelf() {
|
||||
selector: '[data-tut="IsSigned"]',
|
||||
content: t("signature-validate-alert-2"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
return (
|
||||
|
||||
@@ -38,7 +38,8 @@ import {
|
||||
getOriginalWH,
|
||||
defaultMailBody,
|
||||
defaultMailSubject,
|
||||
handleDeleteWidget
|
||||
handleDeleteWidget,
|
||||
nonPresentMaskCss
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
@@ -71,7 +72,6 @@ import ShareButton from "../primitives/ShareButton";
|
||||
|
||||
const TemplatePlaceholder = () => {
|
||||
const { t } = useTranslation();
|
||||
const journey = "Use Template";
|
||||
const { templateId } = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
@@ -629,7 +629,7 @@ const TemplatePlaceholder = () => {
|
||||
selector: '[data-tut="reactourAddbtn"]',
|
||||
content: t("add-user-template"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -958,24 +958,16 @@ const TemplatePlaceholder = () => {
|
||||
);
|
||||
if (res?.status === "unfilled") {
|
||||
setIsUiLoading(false);
|
||||
const emptyWidget = res?.emptyResponseObjects.map((item, index) => (
|
||||
<span className="font-medium" key={index}>
|
||||
{item.options.name}
|
||||
</span>
|
||||
));
|
||||
const emptyWidget = res?.emptyResponseObjects
|
||||
?.map((item) => item.options.name)
|
||||
?.join(", ");
|
||||
const timeInMiliSec = 6000;
|
||||
showAlert(
|
||||
"danger",
|
||||
<>
|
||||
The following required field(s) cannot be left empty:{" "}
|
||||
{emptyWidget.map((item, index) => (
|
||||
<span key={index}>
|
||||
{index > 0 && ", "}
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
. Please fill them out to proceed.
|
||||
</>,
|
||||
6000
|
||||
t("prefill-unfilled-widget", {
|
||||
emptyWidget: emptyWidget ? `[${emptyWidget}]` : ""
|
||||
}),
|
||||
timeInMiliSec
|
||||
);
|
||||
} else if (res?.status === "unattach signer") {
|
||||
setIsUiLoading(false);
|
||||
@@ -996,7 +988,7 @@ const TemplatePlaceholder = () => {
|
||||
pdfDetails,
|
||||
signerPos,
|
||||
signersdata,
|
||||
updatedPdfUrl
|
||||
updatedPdfUrl,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
@@ -1025,7 +1017,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourAddbtn"]',
|
||||
@@ -1038,19 +1030,31 @@ const TemplatePlaceholder = () => {
|
||||
),
|
||||
position: "top",
|
||||
observe: '[data-tut="reactourAddbtn--observe"]',
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
// {
|
||||
// selector: '[data-tut="addWidgets"]',
|
||||
// content: () => (
|
||||
// <TourContentWithBtn
|
||||
// message={t("tour-mssg.template-placeholder-2")}
|
||||
// isDontShowCheckbox={!checkTourStatus}
|
||||
// isChecked={handleDontShow}
|
||||
// />
|
||||
// ),
|
||||
// position: "top",
|
||||
// styles: { fontSize: "13px" }
|
||||
// },
|
||||
{
|
||||
selector: '[data-tut="addWidgets"]',
|
||||
selector: '[data-tut="nonpresentmask"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.template-placeholder-2")}
|
||||
isDontShowCheckbox={!checkTourStatus}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
position: "center",
|
||||
styles: { fontSize: "13px", maskArea: nonPresentMaskCss },
|
||||
action: () => handleCloseRoleModal()
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="pdftools"]',
|
||||
@@ -1062,7 +1066,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="headerArea"]',
|
||||
@@ -1074,7 +1078,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1277,6 +1281,10 @@ const TemplatePlaceholder = () => {
|
||||
const RedirectUrl = updateTemplate?.[0]?.RedirectUrl
|
||||
? { RedirectUrl: updateTemplate?.[0]?.RedirectUrl }
|
||||
: {};
|
||||
const penColors =
|
||||
updateTemplate?.[0]?.PenColors?.length > 0
|
||||
? { PenColors: updateTemplate?.[0]?.PenColors }
|
||||
: {};
|
||||
const data = {
|
||||
...(updateTemplate?.[0]?.URL ? { URL: updateTemplate?.[0]?.URL } : {}),
|
||||
Name: updateTemplate?.[0]?.Name || "",
|
||||
@@ -1293,6 +1301,7 @@ const TemplatePlaceholder = () => {
|
||||
: false,
|
||||
TimeToCompleteDays:
|
||||
parseInt(updateTemplate?.[0]?.TimeToCompleteDays) || 15,
|
||||
...penColors,
|
||||
...Bcc,
|
||||
...RedirectUrl
|
||||
};
|
||||
@@ -1507,13 +1516,16 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
else if (["signature"].includes(position.type)) {
|
||||
else if (["signature", "initials"].includes(position.type)) {
|
||||
return {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
name: defaultdata.name,
|
||||
hint: defaultdata?.hint || ""
|
||||
hint: defaultdata?.hint || "",
|
||||
...(defaultdata?.penColors?.length > 0 && {
|
||||
penColors: defaultdata?.penColors
|
||||
})
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -1621,7 +1633,7 @@ const TemplatePlaceholder = () => {
|
||||
selector: '[data-tut="isSignatureWidget"]',
|
||||
content: t("signature-field-widget", { signersName }),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const showAlert = (type, message, duration = 1500) => {
|
||||
@@ -1633,7 +1645,7 @@ const TemplatePlaceholder = () => {
|
||||
selector: '[data-tut="IsSigned"]',
|
||||
content: t("text-field-tour"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
styles: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const copytoclipboard = (text) => {
|
||||
@@ -2150,6 +2162,7 @@ const TemplatePlaceholder = () => {
|
||||
isSave={true}
|
||||
setUniqueId={setUniqueId}
|
||||
signatureTypes={signatureType}
|
||||
penColors={pdfDetails?.[0]?.PenColors}
|
||||
/>
|
||||
)}
|
||||
<RotateAlert
|
||||
|
||||
@@ -11,7 +11,7 @@ import axios from "axios";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import {
|
||||
getSecureUrl,
|
||||
handleSendOTP,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// below code is used to handle undefined Promise.withResolvers for safari
|
||||
// below code is used to handle undefined Promise.withResolvers for safari for react-pdf
|
||||
if (typeof Promise.withResolvers === "undefined") {
|
||||
Promise.withResolvers = function () {
|
||||
let resolve, reject;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Loader from "./Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
import { getTenantDetails } from "../constant/Utils";
|
||||
import { emailRegex } from "../constant/const";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
|
||||
const AddContact = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -108,6 +112,7 @@ const AddContact = (props) => {
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
dispatch(sessionStatus(false));
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
@@ -139,7 +144,9 @@ const AddContact = (props) => {
|
||||
)}
|
||||
<div className="w-full mx-auto p-[8px]">
|
||||
{!props?.isDisableTitle && (
|
||||
<div className="text-[14px] font-[700] text-base-content mb-1">{t("add-contact")}</div>
|
||||
<div className="text-[14px] font-[700] text-base-content mb-1">
|
||||
{t("add-contact")}
|
||||
</div>
|
||||
)}
|
||||
{isUserExist && props?.isAddYourSelfCheckbox && (
|
||||
<div className="mb-[0.75rem] flex items-center mt-1">
|
||||
|
||||
@@ -1,33 +1,84 @@
|
||||
import ReactTour from "reactour";
|
||||
import { TourProvider, useTour } from "@reactour/tour";
|
||||
import { useEffect, useRef } from "react";
|
||||
import {
|
||||
clearAllBodyScrollLocks,
|
||||
disableBodyScroll,
|
||||
enableBodyScroll
|
||||
} from "body-scroll-lock";
|
||||
|
||||
function TourStateSync({ isOpen, onRequestClose }) {
|
||||
const { isOpen: contextIsOpen, setIsOpen } = useTour();
|
||||
const previousIsOpen = useRef(contextIsOpen);
|
||||
|
||||
useEffect(() => {
|
||||
setIsOpen(isOpen);
|
||||
}, [isOpen, setIsOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (previousIsOpen.current && !contextIsOpen && onRequestClose) {
|
||||
onRequestClose();
|
||||
}
|
||||
previousIsOpen.current = contextIsOpen;
|
||||
}, [contextIsOpen, onRequestClose]);
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function Tour({
|
||||
steps,
|
||||
isOpen,
|
||||
rounded,
|
||||
rounded = 5,
|
||||
className,
|
||||
showNumber,
|
||||
closeWithMask = false,
|
||||
onRequestClose,
|
||||
showNavigation,
|
||||
showCloseButton,
|
||||
showNavigationNumber,
|
||||
disableKeyboardNavigation
|
||||
showNavigationNumber
|
||||
}) {
|
||||
const radius = rounded || 5;
|
||||
function disableBody() {
|
||||
if (typeof window !== "undefined") {
|
||||
disableBodyScroll(document.body);
|
||||
}
|
||||
}
|
||||
|
||||
function enableBody() {
|
||||
if (typeof window !== "undefined") {
|
||||
enableBodyScroll(document.body);
|
||||
clearAllBodyScrollLocks();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ReactTour
|
||||
<TourProvider
|
||||
className={className}
|
||||
steps={steps}
|
||||
isOpen={isOpen}
|
||||
rounded={radius}
|
||||
scrollOffset={-100}
|
||||
closeWithMask={closeWithMask}
|
||||
disableKeyboardNavigation={disableKeyboardNavigation}
|
||||
showCloseButton={showCloseButton}
|
||||
onRequestClose={onRequestClose}
|
||||
showNumber={showNumber}
|
||||
defaultOpen={isOpen}
|
||||
// scrollOffset={-100} // available only for v1
|
||||
inViewThreshold={{ y: 150 }}
|
||||
onClickMask={() => {}}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
showBadge={showNumber}
|
||||
showNavigation={showNavigation}
|
||||
showNavigationNumber={showNavigationNumber}
|
||||
/>
|
||||
showPrevNextButtons={showNavigation}
|
||||
showDots={showNavigationNumber ?? showNavigation}
|
||||
showCloseButton={showCloseButton}
|
||||
padding={rounded}
|
||||
onClickClose={(clickProps) => {
|
||||
clickProps.setIsOpen(false);
|
||||
}}
|
||||
styles={{
|
||||
popover: (base) => ({
|
||||
...base,
|
||||
borderRadius: rounded,
|
||||
padding: "18px",
|
||||
fontSize: "13px"
|
||||
}),
|
||||
maskArea: (base) => ({ ...base, rx: rounded }),
|
||||
close: (base) => ({ ...base, right: 10, top: 10, outline: "none" })
|
||||
}}
|
||||
afterOpen={disableBody}
|
||||
beforeClose={enableBody}
|
||||
scrollSmooth
|
||||
>
|
||||
<TourStateSync isOpen={isOpen} onRequestClose={onRequestClose} />
|
||||
</TourProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const sidebarSlice = createSlice({
|
||||
name: "sidebar",
|
||||
initialState: { isOpen: true },
|
||||
initialState: { isOpen: true, selectedMenu: true },
|
||||
reducers: {
|
||||
toggleSidebar: (state, action) => {
|
||||
if (typeof action.payload === "undefined") {
|
||||
@@ -12,9 +12,12 @@ const sidebarSlice = createSlice({
|
||||
// payload provided → use it directly
|
||||
state.isOpen = action.payload;
|
||||
}
|
||||
},
|
||||
setSelectedMenu: (state, action) => {
|
||||
state.selectedMenu = action.payload;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const { toggleSidebar } = sidebarSlice.actions;
|
||||
export const { toggleSidebar, setSelectedMenu } = sidebarSlice.actions;
|
||||
export default sidebarSlice.reducer;
|
||||
|
||||
@@ -10,6 +10,7 @@ const initialState = {
|
||||
signatureTypes: null,
|
||||
defaultSignImg: null,
|
||||
myInitial: null,
|
||||
myStamp: null,
|
||||
lastIndex: "",
|
||||
scrollTriggerId: "",
|
||||
prefillImg: [],
|
||||
@@ -63,6 +64,9 @@ const widgetSlice = createSlice({
|
||||
setPrefillImgLoad: (state, action) => {
|
||||
state.prefillImgLoad = action.payload;
|
||||
},
|
||||
setMyStamp: (state, action) => {
|
||||
state.myStamp = action.payload;
|
||||
},
|
||||
resetWidgetState: () => initialState
|
||||
}
|
||||
});
|
||||
@@ -78,7 +82,8 @@ export const {
|
||||
setScrollTriggerId,
|
||||
setPrefillImg,
|
||||
setPrefillImgLoad,
|
||||
setTypedSignFont
|
||||
setTypedSignFont,
|
||||
setMyStamp
|
||||
} = widgetSlice.actions;
|
||||
|
||||
export default widgetSlice.reducer;
|
||||
|
||||
@@ -2,8 +2,12 @@ import { useEffect, useState } from "react";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../../redux/reducers/userReducer";
|
||||
|
||||
const EditContactForm = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
Name: "",
|
||||
@@ -30,39 +34,49 @@ const EditContactForm = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (props.handleEditContact) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const params = {
|
||||
contactId: props.contact.objectId,
|
||||
name: formData.Name,
|
||||
email: formData.Email,
|
||||
phone: formData?.Phone,
|
||||
company: formData?.Company,
|
||||
jobTitle: formData?.JobTitle,
|
||||
tenantId: localStorage.getItem("TenantId")
|
||||
};
|
||||
const res = await Parse.Cloud.run("editcontact", params);
|
||||
const updateContact = {
|
||||
...res,
|
||||
Name: formData.Name,
|
||||
Email: formData.Email,
|
||||
Phone: formData?.Phone,
|
||||
Company: formData?.Company,
|
||||
JobTitle: formData?.JobTitle
|
||||
};
|
||||
props.handleEditContact(updateContact);
|
||||
} catch (err) {
|
||||
console.log("err in edit contact ", err);
|
||||
if (err.code === 137) {
|
||||
alert(t("contact-already-exists"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
try {
|
||||
const sessionToken = Parse.User?.current()?.getSessionToken();
|
||||
if (localStorage.getItem("TenantId") && sessionToken) {
|
||||
if (props.handleEditContact) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const params = {
|
||||
contactId: props.contact.objectId,
|
||||
name: formData.Name,
|
||||
email: formData.Email,
|
||||
phone: formData?.Phone,
|
||||
company: formData?.Company,
|
||||
jobTitle: formData?.JobTitle,
|
||||
tenantId: localStorage.getItem("TenantId")
|
||||
};
|
||||
const res = await Parse.Cloud.run("editcontact", params);
|
||||
const updateContact = {
|
||||
...res,
|
||||
Name: formData.Name,
|
||||
Email: formData.Email,
|
||||
Phone: formData?.Phone,
|
||||
Company: formData?.Company,
|
||||
JobTitle: formData?.JobTitle
|
||||
};
|
||||
props.handleEditContact(updateContact);
|
||||
} catch (err) {
|
||||
console.log("err in edit contact ", err);
|
||||
if (err.code === 137) {
|
||||
alert(t("contact-already-exists"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
props.handleClose && props.handleClose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
props.handleClose && props.handleClose();
|
||||
} else {
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("invalid session or missing tenantId ", err);
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import * as XLSX from "xlsx";
|
||||
import Parse from "parse";
|
||||
import { emailRegex } from "../../constant/const";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../../redux/reducers/userReducer";
|
||||
|
||||
const ImportContact = ({ setLoader, onImport, showAlert }) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [currentImportPage, setCurrentImportPage] = useState(1);
|
||||
const [importedData, setImportedData] = useState([]);
|
||||
const [invalidRecords, setInvalidRecords] = useState(0);
|
||||
@@ -183,40 +186,50 @@ const ImportContact = ({ setLoader, onImport, showAlert }) => {
|
||||
e.stopPropagation();
|
||||
setLoader(true);
|
||||
try {
|
||||
const filterdata = importedData.map((x) => ({
|
||||
Name: x.Name,
|
||||
Email: x.Email,
|
||||
Phone: x.Phone,
|
||||
Company: x.Company,
|
||||
JobTitle: x.JobTitle,
|
||||
TenantId: localStorage.getItem("TenantId")
|
||||
}));
|
||||
const contacts = JSON.stringify(filterdata);
|
||||
const res = await Parse.Cloud.run("createbatchcontact", { contacts });
|
||||
if (res) {
|
||||
showAlert(
|
||||
"info",
|
||||
t("contact-imported", {
|
||||
imported: res?.success || 0,
|
||||
failed: res?.failed || 0
|
||||
})
|
||||
);
|
||||
if (res?.success > 0) {
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
const sessionToken = Parse.User?.current()?.getSessionToken();
|
||||
if (localStorage.getItem("TenantId") && sessionToken) {
|
||||
try {
|
||||
const filterdata = importedData.map((x) => ({
|
||||
Name: x.Name,
|
||||
Email: x.Email,
|
||||
Phone: x.Phone,
|
||||
Company: x.Company,
|
||||
JobTitle: x.JobTitle,
|
||||
TenantId: localStorage.getItem("TenantId")
|
||||
}));
|
||||
const contacts = JSON.stringify(filterdata);
|
||||
const res = await Parse.Cloud.run("createbatchcontact", { contacts });
|
||||
if (res) {
|
||||
showAlert(
|
||||
"info",
|
||||
t("contact-imported", {
|
||||
imported: res?.success || 0,
|
||||
failed: res?.failed || 0
|
||||
})
|
||||
);
|
||||
if (res?.success > 0) {
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while creating batch contact", err);
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
onImport && onImport();
|
||||
setImportedData([]);
|
||||
setInvalidRecords(0);
|
||||
}
|
||||
} else {
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while creating batch contact", err);
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
onImport && onImport();
|
||||
setImportedData([]);
|
||||
setInvalidRecords(0);
|
||||
console.error("invalid session or missing tenantId ", err);
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleImportData} className="p-[20px] h-full ">
|
||||
<form onSubmit={handleImportData} className="p-[20px] h-full">
|
||||
<div className="text-xs">
|
||||
<label className="block ml-2">
|
||||
{t("contacts-file")}
|
||||
|
||||
@@ -11,7 +11,6 @@ import Parse from "parse";
|
||||
import {
|
||||
copytoData,
|
||||
fetchUrl,
|
||||
formatDate,
|
||||
getSignedUrl,
|
||||
getTenantDetails,
|
||||
handleSignatureType,
|
||||
@@ -46,7 +45,6 @@ const DocumentsReport = (props) => {
|
||||
const copyUrlRef = useRef(null);
|
||||
const titleRef = useRef(null);
|
||||
const titleElement = useElSize(titleRef);
|
||||
const journey = "Use Template";
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
@@ -267,7 +265,7 @@ const DocumentsReport = (props) => {
|
||||
[templateRes || templateDeatils],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDeatils?.URL
|
||||
templateRes?.URL || templateDeatils?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
@@ -944,12 +942,14 @@ const DocumentsReport = (props) => {
|
||||
);
|
||||
if (res?.status === "unfilled") {
|
||||
const emptyWidget = res?.emptyResponseObjects
|
||||
.map((item) => `[ ${item.options.name}]`)
|
||||
.join(", ");
|
||||
?.map((item) => item.options.name)
|
||||
?.join(", ");
|
||||
const timeInMiliSec = 6000;
|
||||
showAlert(
|
||||
"danger",
|
||||
t("prefill-unfilled-widget", { emptyWidget: emptyWidget }),
|
||||
t("prefill-unfilled-widget", {
|
||||
emptyWidget: emptyWidget ? `[${emptyWidget}]` : ""
|
||||
}),
|
||||
timeInMiliSec
|
||||
);
|
||||
} else if (res?.status === "unattach signer") {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
handleSignatureType,
|
||||
replaceMailVaribles,
|
||||
signatureTypes,
|
||||
openInNewTab,
|
||||
createDocument,
|
||||
defaultMailBody,
|
||||
defaultMailSubject
|
||||
@@ -30,7 +29,9 @@ import "../../styles/quill.css";
|
||||
import BulkSendUi from "../../components/BulkSendUi";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { serverUrl_fn } from "../../constant/appinfo";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import {
|
||||
useTranslation
|
||||
} from "react-i18next";
|
||||
import { useElSize } from "../../hook/useElSize";
|
||||
import LottieWithLoader from "../../primitives/DotLottieReact";
|
||||
import PrefillWidgetModal from "../../components/pdf/PrefillWidgetsModal";
|
||||
@@ -42,7 +43,6 @@ import CustomizeMail from "../../components/pdf/CustomizeMail";
|
||||
const TemplatesReport = (props) => {
|
||||
const copyUrlRef = useRef(null);
|
||||
const titleRef = useRef(null);
|
||||
const journey = "Use Template";
|
||||
const titleElement = useElSize(titleRef);
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const appName =
|
||||
@@ -309,7 +309,7 @@ const TemplatesReport = (props) => {
|
||||
[templateRes || templateDetails],
|
||||
placeholder || xyPosition,
|
||||
signer || signerList,
|
||||
templateRes?.URL || templateDetails?.URL
|
||||
templateRes?.URL || templateDetails?.URL,
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
@@ -900,12 +900,14 @@ const TemplatesReport = (props) => {
|
||||
);
|
||||
if (res?.status === "unfilled") {
|
||||
const emptyWidget = res?.emptyResponseObjects
|
||||
.map((item) => `[ ${item.options.name}]`)
|
||||
.join(", ");
|
||||
?.map((item) => item.options.name)
|
||||
?.join(", ");
|
||||
const timeInMiliSec = 6000;
|
||||
showAlert(
|
||||
"danger",
|
||||
t("prefill-unfilled-widget", { emptyWidget: emptyWidget }),
|
||||
t("prefill-unfilled-widget", {
|
||||
emptyWidget: emptyWidget ? `[${emptyWidget}]` : ""
|
||||
}),
|
||||
timeInMiliSec
|
||||
);
|
||||
} else if (res?.status === "unattach signer") {
|
||||
|
||||
@@ -44,8 +44,26 @@
|
||||
}
|
||||
|
||||
.mysignatureCanvas {
|
||||
width: 456px;
|
||||
height: 180px;
|
||||
/* width: 456px;
|
||||
height: 180px; */
|
||||
width: 370px;
|
||||
height: 146px;
|
||||
}
|
||||
|
||||
.mystampCanvas {
|
||||
/* width: 446px;
|
||||
height: 180px; */
|
||||
width: 370px;
|
||||
height: 146px;
|
||||
}
|
||||
|
||||
.intialSignature {
|
||||
border: 2px solid #888;
|
||||
background-color: rgb(255, 255, 255);
|
||||
/* width: 180px;
|
||||
height: 180px; */
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
[data-theme="opensigndark"] .tabWidth {
|
||||
@@ -70,13 +88,6 @@
|
||||
/* blue-500 */
|
||||
}
|
||||
|
||||
.intialSignature {
|
||||
border: 2px solid #888;
|
||||
background-color: rgb(255, 255, 255);
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
/* Dark mode support for initials box in /managesign */
|
||||
[data-theme="opensigndark"] .intialSignature {
|
||||
background-color: #1f2937 !important;
|
||||
@@ -117,10 +128,6 @@
|
||||
border-color: #4b5563 !important;
|
||||
}
|
||||
|
||||
.penContainerDefault {
|
||||
width: 460px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
@@ -450,7 +457,7 @@ option {
|
||||
to prevent sudden quick movement (as the
|
||||
navigation bar gets a new position at the top of the
|
||||
page (position:fixed and top:0) */
|
||||
.stickyHead+.content {
|
||||
.stickyHead + .content {
|
||||
padding-top: 60px;
|
||||
}
|
||||
}
|
||||
@@ -466,11 +473,12 @@ option {
|
||||
height: 118px;
|
||||
}
|
||||
|
||||
.tabWidth {
|
||||
.mystampCanvas {
|
||||
width: 300px;
|
||||
height: 118px;
|
||||
}
|
||||
|
||||
.penContainerDefault {
|
||||
.tabWidth {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
@@ -498,18 +506,17 @@ option {
|
||||
width: 280px;
|
||||
height: 111px;
|
||||
}
|
||||
.mystampCanvas {
|
||||
width: 280px;
|
||||
height: 111px;
|
||||
}
|
||||
|
||||
.tabWidth {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.penContainerDefault {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.signTab {
|
||||
font-weight: 500 !important;
|
||||
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
@@ -528,12 +535,12 @@ option {
|
||||
width: 230px;
|
||||
height: 91px;
|
||||
}
|
||||
|
||||
.tabWidth {
|
||||
.mystampCanvas {
|
||||
width: 230px;
|
||||
height: 91px;
|
||||
}
|
||||
|
||||
.penContainerDefault {
|
||||
.tabWidth {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
@@ -545,4 +552,4 @@ option {
|
||||
.uploadImgLogo {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,8 @@ export const handleCheckPrefillCreateDoc = async (
|
||||
);
|
||||
if (res.status === "success") {
|
||||
return res;
|
||||
} else if (res.status === "error") {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
const res = { status: "unattach signer" };
|
||||
|
||||
@@ -13,7 +13,9 @@ export const saveToMySign = async (widget) => {
|
||||
const fileName =
|
||||
widget?.type === "signature"
|
||||
? `${replaceSpace}__sign`
|
||||
: `${replaceSpace}__initials`;
|
||||
: widget?.type === "initials"
|
||||
? `${replaceSpace}__initials`
|
||||
: `${replaceSpace}_stamp`;
|
||||
const file = base64StringtoFile(base64, fileName);
|
||||
const fileUrl = await uploadFile(file, User?.id);
|
||||
// below code is used to save or update default signature, initials, stamp
|
||||
@@ -25,6 +27,8 @@ export const saveToMySign = async (widget) => {
|
||||
params.initials = fileUrl; // save initials image url
|
||||
} else if (widget?.type === "signature") {
|
||||
params.signature = fileUrl; // save signature image url
|
||||
} else if (widget?.type === "stamp") {
|
||||
params.stamp = fileUrl; // save stamp image url
|
||||
}
|
||||
const signRes = await Parse.Cloud.run("savesignature", params);
|
||||
return { base64File: base64, id: signRes?.id };
|
||||
@@ -113,3 +117,21 @@ export const applyNumberFormulasToPages = (pages = []) => {
|
||||
// Return the cloned, updated pages
|
||||
return clonedPages;
|
||||
};
|
||||
|
||||
export const getInitials = (name) => {
|
||||
if (!name) return "";
|
||||
|
||||
// Trim and split by any amount of whitespace
|
||||
const parts = name.trim().split(/\s+/);
|
||||
|
||||
// Only one word → return first letter of that word
|
||||
if (parts.length === 1) {
|
||||
return parts[0][0].toUpperCase();
|
||||
}
|
||||
|
||||
// More than one word → take first and second words
|
||||
const first = parts[0];
|
||||
const second = parts[1];
|
||||
|
||||
return `${first[0].toUpperCase()} ${second[0].toUpperCase()}`;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,11 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
require("daisyui"),
|
||||
function ({ addUtilities, theme }) {
|
||||
function ({ addUtilities, addVariant }) {
|
||||
// ✅ Variants that match html[data-theme="..."] (or any ancestor with data-theme)
|
||||
addVariant("opensigncss", '[data-theme="opensigncss"] &');
|
||||
addVariant("opensigndark", '[data-theme="opensigndark"] &');
|
||||
|
||||
addUtilities({
|
||||
// Prevent iOS long-press popup
|
||||
".touch-callout-none": {
|
||||
|
||||
@@ -160,6 +160,17 @@ export const smtpsecure = process.env.SMTP_PORT && process.env.SMTP_PORT !== '46
|
||||
export const smtpenable =
|
||||
process.env.SMTP_ENABLE && process.env.SMTP_ENABLE.toLowerCase() === 'true' ? true : false;
|
||||
export const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
export function signPayload(payload, secret) {
|
||||
if (payload && secret) {
|
||||
const signature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(JSON.stringify(payload))
|
||||
.digest('hex');
|
||||
return { 'x-webhook-signature': signature };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// `generateId` is used to unique Id for fileAdapter
|
||||
export function generateId(length) {
|
||||
|
||||
@@ -8,12 +8,16 @@ async function SignatureAfterFind(request) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
const Stamp = obj?.get('Stamp') && obj?.get('Stamp');
|
||||
if (ImageURL) {
|
||||
obj.set('ImageURL', getPresignedUrl(ImageURL));
|
||||
}
|
||||
if (Initials) {
|
||||
obj.set('Initials', getPresignedUrl(Initials));
|
||||
}
|
||||
if (Stamp) {
|
||||
obj.set('Stamp', getPresignedUrl(Stamp));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
@@ -23,12 +27,16 @@ async function SignatureAfterFind(request) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
const Stamp = obj?.get('Stamp') && obj?.get('Stamp');
|
||||
if (ImageURL) {
|
||||
obj.set('ImageURL', presignedlocalUrl(ImageURL));
|
||||
}
|
||||
if (Initials) {
|
||||
obj.set('Initials', presignedlocalUrl(Initials));
|
||||
}
|
||||
if (Stamp) {
|
||||
obj.set('Stamp', presignedlocalUrl(Stamp));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,11 +101,7 @@ async function sendMail(document, publicUrl) {
|
||||
}
|
||||
async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
extCls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const resExt = await extCls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
@@ -189,6 +185,7 @@ async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) {
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(x?.PenColors?.length > 0 ? { PenColors: x?.PenColors } : {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default async function manageSign(request) {
|
||||
const { signature, userId, initials, id, title } = request.params;
|
||||
const { signature, userId, initials, id, title, stamp } = request.params;
|
||||
|
||||
if (!userId) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
|
||||
@@ -16,6 +16,7 @@ export default async function manageSign(request) {
|
||||
signatureCls.set('Initials', initials ? initials : '');
|
||||
signatureCls.set('ImageURL', signature ? signature : '');
|
||||
signatureCls.set('SignatureName', title ? title : '');
|
||||
signatureCls.set('Stamp', stamp ? stamp : '');
|
||||
if (userPtr) {
|
||||
signatureCls.set('UserId', userPtr);
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ export default async function saveAsTemplate(request) {
|
||||
if (_docRes?.Bcc?.length > 0) {
|
||||
templateCls.set('Bcc', _docRes?.Bcc);
|
||||
}
|
||||
if (_docRes?.PenColors?.length > 0) {
|
||||
templateCls.set('PenColors', _docRes?.PenColors);
|
||||
}
|
||||
const res = await templateCls.save(null, { useMasterKey: true });
|
||||
return res;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default async function saveSignature(request) {
|
||||
const { signature, userId, initials, id, title } = request.params;
|
||||
const { signature, userId, initials, id, title, stamp } = request.params;
|
||||
|
||||
if (!userId) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
|
||||
@@ -19,6 +19,9 @@ export default async function saveSignature(request) {
|
||||
if (signature) {
|
||||
signatureCls.set('ImageURL', signature);
|
||||
}
|
||||
if (stamp) {
|
||||
signatureCls.set('Stamp', stamp);
|
||||
}
|
||||
if (title) {
|
||||
signatureCls.set('SignatureName', title);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ export default async function triggerEvent(request) {
|
||||
const contactId = request.params.contactId;
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = serverAppId;
|
||||
const sessiontoken = request.headers?.sessiontoken;
|
||||
|
||||
try {
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.select(['Name', 'IsEnableOTP', 'SignedUrl', 'AuditTrail']);
|
||||
@@ -17,13 +19,16 @@ export default async function triggerEvent(request) {
|
||||
|
||||
let userId;
|
||||
if (isEnableOTP) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
userId = userRes.data && userRes.data.objectId;
|
||||
let userId;
|
||||
if (sessiontoken) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': sessiontoken,
|
||||
},
|
||||
});
|
||||
userId = userRes.data && userRes.data.objectId;
|
||||
}
|
||||
if (!userId) {
|
||||
return { message: 'User not found!' };
|
||||
}
|
||||
@@ -58,7 +63,10 @@ export default async function triggerEvent(request) {
|
||||
|
||||
return { message: 'event called!' };
|
||||
} catch (err) {
|
||||
console.log(`Err in triggerEvent: ${err}`);
|
||||
console.log(
|
||||
`triggerEvent error: `,
|
||||
err?.response?.data?.error || err?.message || 'Something went wrong!'
|
||||
);
|
||||
return { message: 'Something went wrong!' };
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.up = async Parse => {
|
||||
const schema = new Parse.Schema('contracts_Signature');
|
||||
schema.addString('Stamp');
|
||||
return schema.update();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.down = async Parse => {
|
||||
const schema = new Parse.Schema('contracts_Signature');
|
||||
schema.deleteField('Stamp');
|
||||
return schema.update();
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.up = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.addArray('PenColors');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.addArray('PenColors');
|
||||
await templateSchema.update();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.down = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.deleteField('PenColors');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.deleteField('PenColors');
|
||||
await templateSchema.update();
|
||||
};
|
||||
Generated
+14894
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open_sign_server",
|
||||
"version": "2.32.0",
|
||||
"version": "2.34.0",
|
||||
"description": "opensign server using the parse-server module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,16 +18,16 @@
|
||||
"watch": "nodemon index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.943.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.943.0",
|
||||
"@aws-sdk/client-s3": "^3.952.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.952.0",
|
||||
"@parse/fs-files-adapter": "^3.0.0",
|
||||
"@parse/push-adapter": "^8.0.2",
|
||||
"@parse/push-adapter": "^8.1.0",
|
||||
"@parse/s3-files-adapter": "^4.2.0",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@signpdf/placeholder-pdf-lib": "^3.2.6",
|
||||
"@signpdf/signer-p12": "^3.2.4",
|
||||
"@signpdf/signpdf": "^3.2.5",
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"aws-sdk": "^2.1693.0",
|
||||
"axios": "^1.13.2",
|
||||
"coherentpdf": "^2.5.5",
|
||||
"cors": "^2.8.5",
|
||||
@@ -36,23 +36,24 @@
|
||||
"express": "^5.2.1",
|
||||
"form-data": "^4.0.5",
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^167.0.0",
|
||||
"googleapis": "^169.0.0",
|
||||
"libreoffice-convert": "^1.7.0",
|
||||
"mailgun.js": "^12.2.0",
|
||||
"mongodb": "^5.9.2",
|
||||
"mailgun.js": "^12.4.0",
|
||||
"moment": "^2.30.1",
|
||||
"mongodb": "^7.0.0",
|
||||
"multer": "^2.0.2",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.3",
|
||||
"nodemailer": "^7.0.11",
|
||||
"p-limit": "^7.2.0",
|
||||
"parse": "^7.1.2",
|
||||
"parse": "^8.0.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^8.5.0",
|
||||
"parse-server": "^8.6.2",
|
||||
"parse-server-api-mail-adapter": "^5.0.2",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"posthog-node": "^5.14.0",
|
||||
"posthog-node": "^5.17.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"rate-limiter-flexible": "^8.3.0",
|
||||
"rate-limiter-flexible": "^9.0.1",
|
||||
"sharp": "^0.34.5",
|
||||
"speakeasy": "^2.0.0",
|
||||
"ws": "^8.18.3"
|
||||
@@ -60,9 +61,9 @@
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.28.5",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint": "^9.39.2",
|
||||
"jasmine": "^5.12.0",
|
||||
"mongodb-runner": "^5.11.1",
|
||||
"mongodb-runner": "^6.4.0",
|
||||
"nodemon": "^3.1.11",
|
||||
"nyc": "^17.1.0",
|
||||
"prettier": "^3.7.4"
|
||||
|
||||
Reference in New Issue
Block a user