mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-05 09:17:39 +02:00
Compare commits
@@ -1,8 +1,15 @@
|
|||||||
name: ci
|
name: ci
|
||||||
|
|
||||||
|
# 👇 add this block
|
||||||
|
permissions:
|
||||||
|
contents: read # allow checkout & metadata-action to read repo
|
||||||
|
id-token: write # needed by docker/metadata-action v4
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
|
- 'staging'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
||||||
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
|
<!-- <link rel="apple-touch-icon" href="/logo192.png" /> -->
|
||||||
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||||
@@ -17,5 +17,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root" style="touch-action:pan-x pan-y;"></div>
|
<div id="root" style="touch-action:pan-x pan-y;"></div>
|
||||||
|
<script type="module" src="/src/index.jsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Generated
+6793
-17494
File diff suppressed because it is too large
Load Diff
+48
-49
@@ -1,74 +1,70 @@
|
|||||||
{
|
{
|
||||||
"name": "open_sign",
|
"name": "open_sign",
|
||||||
"version": "0.1.0",
|
"version": "2.21.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^0.8.2",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@lottiefiles/dotlottie-react": "^0.13.2",
|
"@lottiefiles/dotlottie-react": "^0.13.5",
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
"@pdf-lib/fontkit": "^1.1.1",
|
||||||
"@radix-ui/themes": "^3.1.6",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@reduxjs/toolkit": "^2.5.1",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"axios": "^1.8.4",
|
"@imgly/background-removal": "^1.6.0",
|
||||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
"axios": "^1.9.0",
|
||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^23.16.8",
|
"i18next": "^23.16.8",
|
||||||
"i18next-browser-languagedetector": "^8.0.4",
|
"i18next-browser-languagedetector": "^8.1.0",
|
||||||
"i18next-http-backend": "^3.0.2",
|
"i18next-http-backend": "^3.0.2",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nth-check": "^2.1.1",
|
"parse": "^6.1.1",
|
||||||
"parse": "^5.3.0",
|
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"radix-ui": "^1.0.1",
|
"prismjs": "^1.30.0",
|
||||||
"react": "^18.2.0",
|
"radix-ui": "^1.4.2",
|
||||||
"react-bootstrap": "^2.10.9",
|
"react": "^18.3.1",
|
||||||
|
"react-bootstrap": "^2.10.10",
|
||||||
"react-confetti": "^6.4.0",
|
"react-confetti": "^6.4.0",
|
||||||
"react-cookie": "^7.2.2",
|
"react-datepicker": "^8.3.0",
|
||||||
"react-datepicker": "^7.6.0",
|
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dnd-multi-backend": "^9.0.0",
|
"react-dnd-multi-backend": "^9.0.0",
|
||||||
"react-dnd-touch-backend": "^16.0.1",
|
"react-dnd-touch-backend": "^16.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"react-gtm-module": "^2.0.11",
|
"react-gtm-module": "^2.0.11",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-pdf": "^9.2.1",
|
"react-pdf": "^9.2.1",
|
||||||
"react-quill-new": "^3.4.6",
|
"react-quill-new": "^3.4.6",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-rnd": "^10.5.2",
|
"react-rnd": "^10.5.2",
|
||||||
"react-router": "^7.1.5",
|
"react-router": "^7.6.0",
|
||||||
"react-scripts": "^5.0.1",
|
|
||||||
"react-scrollbars-custom": "^4.1.1",
|
"react-scrollbars-custom": "^4.1.1",
|
||||||
"react-select": "^5.10.1",
|
"react-select": "^5.10.1",
|
||||||
"react-signature-canvas": "^1.0.7",
|
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
|
||||||
"react-timezone-select": "^3.2.8",
|
"react-timezone-select": "^3.2.8",
|
||||||
"react-tooltip": "^5.28.0",
|
"react-tooltip": "^5.28.1",
|
||||||
"react-web-share": "^2.0.2",
|
|
||||||
"reactour": "^1.19.4",
|
"reactour": "^1.19.4",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-thunk": "^3.1.0",
|
|
||||||
"regex-parser": "^2.3.1",
|
"regex-parser": "^2.3.1",
|
||||||
"serve": "^14.2.4",
|
"serve": "^14.2.4",
|
||||||
"styled-components": "^5.3.0",
|
"styled-components": "^5.3.11",
|
||||||
"web-vitals": "^4.2.4",
|
"web-vitals": "^5.0.1",
|
||||||
"ws": "^8.18.1",
|
|
||||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run version && react-scripts build",
|
"build": "npm run version && NODE_OPTIONS=\"--max-old-space-size=8192\" vite build",
|
||||||
"start-dev": "react-scripts start",
|
"start-dev": "vite",
|
||||||
|
"dev": "vite",
|
||||||
|
"preview": "vite preview",
|
||||||
"start": "serve -s build",
|
"start": "serve -s build",
|
||||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||||
"build-win": "npm run version-win && react-scripts build",
|
"build-win": "npm run version-win && vite build",
|
||||||
"test": "react-scripts test",
|
"test": "vitest run",
|
||||||
"eject": "react-scripts eject",
|
"test:watch": "vitest",
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
"commit": "cz"
|
"commit": "cz"
|
||||||
},
|
},
|
||||||
@@ -96,33 +92,36 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.10",
|
"@babel/core": "^7.27.1",
|
||||||
"@babel/preset-env": "^7.26.9",
|
"@babel/preset-env": "^7.27.2",
|
||||||
"@babel/preset-react": "^7.26.3",
|
"@babel/preset-react": "^7.27.1",
|
||||||
"@babel/runtime-corejs2": "^7.27.0",
|
"@babel/runtime-corejs2": "^7.27.1",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.3.0",
|
||||||
|
"@testing-library/user-event": "^14.6.1",
|
||||||
|
"@types/react": "^18.3.22",
|
||||||
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.9.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.0.0",
|
||||||
"commitizen": "^4.3.1",
|
"commitizen": "^4.3.1",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"daisyui": "^4.12.23",
|
"daisyui": "^4.12.24",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.5.0",
|
||||||
"dotenv-webpack": "^8.1.0",
|
"eslint": "^9.27.0",
|
||||||
"eslint": "^9.23.0",
|
"eslint-plugin-prettier": "^5.4.0",
|
||||||
"eslint-plugin-prettier": "^5.2.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react": "^7.37.4",
|
"lint-staged": "^16.0.0",
|
||||||
"lint-staged": "^15.5.0",
|
|
||||||
"mini-css-extract-plugin": "^2.9.2",
|
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"pretty-quick": "^4.1.1",
|
"pretty-quick": "^4.1.1",
|
||||||
|
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"terser-webpack-plugin": "^5.3.11",
|
"vite": "^6.3.5",
|
||||||
"webpack-cli": "^5.1.4"
|
"vite-plugin-svgr": "^4.3.0",
|
||||||
},
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"overrides": {
|
"vitest": "^3.1.4"
|
||||||
"nth-check": "$nth-check",
|
|
||||||
"ws": "$ws"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18 || 20 || 22"
|
"node": "18 || 20 || 22"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"header-news": "Neue Funktion: Benutzer des Teams-Plans können jetzt ihre eigenen AWS S3-Buckets für die Dateispeicherung integrieren",
|
"header-news": "Neue Funktion: Benutzer des Teams-Plans können jetzt ihre eigenen AWS S3-Buckets für die Dateispeicherung integrieren",
|
||||||
"header-news-btn": "Jetzt einrichten",
|
"header-news-btn": "Jetzt einrichten",
|
||||||
"create-account": "Konto erstellen",
|
"create-account": "Konto erstellen",
|
||||||
@@ -39,8 +39,10 @@
|
|||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"upgrade-now": "Jetzt upgraden",
|
"upgrade-now": "Jetzt upgraden",
|
||||||
|
"contact-now": "Jetzt kontaktieren",
|
||||||
"upgrade-to": "Upgrade zu",
|
"upgrade-to": "Upgrade zu",
|
||||||
"plan": "Plan",
|
"plan": "Plan",
|
||||||
|
"subscription-renew-warning": "Ihr Abonnement läuft in {{remainingDays}} Tagen ab. Bitte verlängern Sie Ihr Abonnement.",
|
||||||
"subscribe-card-teamplan": "Entfesseln Sie die volle Kraft der Zusammenarbeit! Erstellen Sie unbegrenzt Organisationen, Teams und Hierarchien. Teilen Sie Vorlagen nahtlos zwischen Teams und weisen Sie benutzerdefinierte Benutzerrollen zu. Optimieren Sie Ihren Workflow noch heute!",
|
"subscribe-card-teamplan": "Entfesseln Sie die volle Kraft der Zusammenarbeit! Erstellen Sie unbegrenzt Organisationen, Teams und Hierarchien. Teilen Sie Vorlagen nahtlos zwischen Teams und weisen Sie benutzerdefinierte Benutzerrollen zu. Optimieren Sie Ihren Workflow noch heute!",
|
||||||
"subscribe-card-plan": "Entsperren Sie Premium-Funktionen ab nur {{premiumPrice}}/Monat. Genießen Sie eine verbesserte Leistung und zahlen Sie nur {{addonPrice}} pro zusätzlichem Credit nach den enthaltenen Premium-Credits.",
|
"subscribe-card-plan": "Entsperren Sie Premium-Funktionen ab nur {{premiumPrice}}/Monat. Genießen Sie eine verbesserte Leistung und zahlen Sie nur {{addonPrice}} pro zusätzlichem Credit nach den enthaltenen Premium-Credits.",
|
||||||
"user-name-limit-char": "Um einen Benutzernamen mit weniger als 8 Zeichen zu haben, abonnieren Sie bitte.",
|
"user-name-limit-char": "Um einen Benutzernamen mit weniger als 8 Zeichen zu haben, abonnieren Sie bitte.",
|
||||||
@@ -142,6 +144,7 @@
|
|||||||
"Quick send": "Schnell senden",
|
"Quick send": "Schnell senden",
|
||||||
"Edit": "Bearbeiten",
|
"Edit": "Bearbeiten",
|
||||||
"Share with team": "Mit Team teilen",
|
"Share with team": "Mit Team teilen",
|
||||||
|
"Share with user": "Mit Kollegen teilen",
|
||||||
"Share": "Teilen",
|
"Share": "Teilen",
|
||||||
"View": "Ansehen",
|
"View": "Ansehen",
|
||||||
"option": "Option",
|
"option": "Option",
|
||||||
@@ -152,7 +155,9 @@
|
|||||||
"Duplicate Template": "Vorlage duplizieren",
|
"Duplicate Template": "Vorlage duplizieren",
|
||||||
"Duplicate": "Duplikat",
|
"Duplicate": "Duplikat",
|
||||||
"daily-mail-quota": "Tägliches E-Mail-Kontingent",
|
"daily-mail-quota": "Tägliches E-Mail-Kontingent",
|
||||||
"Save as template": "Als Vorlage speichern"
|
"Save as template": "Als Vorlage speichern",
|
||||||
|
"Fix & resend": "Korrigieren und erneut senden",
|
||||||
|
"Kiosk Mode": "Kiosk Modus"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nr.",
|
"Sr.No": "Nr.",
|
||||||
@@ -243,6 +248,7 @@
|
|||||||
"API": "API",
|
"API": "API",
|
||||||
"api-token": "API-Token",
|
"api-token": "API-Token",
|
||||||
"regenerate-token": "Live-Token neu generieren",
|
"regenerate-token": "Live-Token neu generieren",
|
||||||
|
"remove-background": "Hintergrund entfernen",
|
||||||
"generate-token": "Live-Token generieren",
|
"generate-token": "Live-Token generieren",
|
||||||
"view-docs": "Dokumentation ansehen",
|
"view-docs": "Dokumentation ansehen",
|
||||||
"generate-token-alert": "Sind Sie sicher, dass Sie das Token neu generieren möchten? Das alte Token wird ablaufen.",
|
"generate-token-alert": "Sind Sie sicher, dass Sie das Token neu generieren möchten? Das alte Token wird ablaufen.",
|
||||||
@@ -303,7 +309,7 @@
|
|||||||
"send": "Senden",
|
"send": "Senden",
|
||||||
"quick-send-alert-1": "Alle Rollen in diesem Dokument sind derzeit mit Kontakten verknüpft. Um Kopien dieser Vorlage schnell an mehrere Unterzeichner zu senden, stellen Sie sicher, dass mindestens eine Rolle keinem Kontakt zugeordnet ist.",
|
"quick-send-alert-1": "Alle Rollen in diesem Dokument sind derzeit mit Kontakten verknüpft. Um Kopien dieser Vorlage schnell an mehrere Unterzeichner zu senden, stellen Sie sicher, dass mindestens eine Rolle keinem Kontakt zugeordnet ist.",
|
||||||
"quick-send-alert-2": "Bitte stellen Sie sicher, dass für alle Empfänger mindestens ein Signatur-Widget hinzugefügt wurde.",
|
"quick-send-alert-2": "Bitte stellen Sie sicher, dass für alle Empfänger mindestens ein Signatur-Widget hinzugefügt wurde.",
|
||||||
"quick-send-alert-3": "Bitte fügen Sie mindestens eine Rolle zu dieser Vorlage hinzu, um Kopien davon schnell an mehrere Unterzeichner zu senden.",
|
"quick-send-alert-3": "Bitte fügen Sie diesem template mindestens eine Rolle hinzu.",
|
||||||
"quick-send-alert-4": "Das Limit für Schnellsendungen wurde erreicht.",
|
"quick-send-alert-4": "Das Limit für Schnellsendungen wurde erreicht.",
|
||||||
"copy-link": "Link kopieren",
|
"copy-link": "Link kopieren",
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
@@ -340,7 +346,7 @@
|
|||||||
"verify-email-1": "E-Mail verifizieren",
|
"verify-email-1": "E-Mail verifizieren",
|
||||||
"resend": "Erneut senden",
|
"resend": "Erneut senden",
|
||||||
"contact-details": "Kontaktdetails",
|
"contact-details": "Kontaktdetails",
|
||||||
"verify-email": "Bitte verifizieren Sie Ihre E-Mail!",
|
"verify-email": "Bitte bestätigen Sie Ihre E-Mail, um fortzufahren. Dies ist ein einmaliger Schritt, um die mit Ihrem OpenSign-Konto verknüpfte E-Mail zu bestätigen.",
|
||||||
"send-otp": "OTP senden",
|
"send-otp": "OTP senden",
|
||||||
"otp-placeholder": "Verifizierungscode aus der E-Mail eingeben",
|
"otp-placeholder": "Verifizierungscode aus der E-Mail eingeben",
|
||||||
"loading-doc": "Dokument wird geladen...",
|
"loading-doc": "Dokument wird geladen...",
|
||||||
@@ -476,6 +482,7 @@
|
|||||||
"document-alert": "Dokument-Warnung",
|
"document-alert": "Dokument-Warnung",
|
||||||
"owner-subscription-expired": "Das Abonnement des Besitzers ist abgelaufen.",
|
"owner-subscription-expired": "Das Abonnement des Besitzers ist abgelaufen.",
|
||||||
"subscription-expired": "Abonnement abgelaufen",
|
"subscription-expired": "Abonnement abgelaufen",
|
||||||
|
"owner-doesnt-have-paid-plan": "Der Inhaber hat keinen kostenpflichtigen Plan.",
|
||||||
"alert-message": "Warnmeldung",
|
"alert-message": "Warnmeldung",
|
||||||
"document-decline": "Dokument ablehnen",
|
"document-decline": "Dokument ablehnen",
|
||||||
"decline-alert-1": "Sind Sie sicher, dass Sie dieses Dokument ablehnen möchten?",
|
"decline-alert-1": "Sind Sie sicher, dass Sie dieses Dokument ablehnen möchten?",
|
||||||
@@ -881,5 +888,97 @@
|
|||||||
"contact-billing-at-opensign": "Um weitere Plätze hinzuzufügen, kontaktieren Sie bitte OpenSign™ unter <1>billing@opensignlabs.com</1> für Unterstützung.",
|
"contact-billing-at-opensign": "Um weitere Plätze hinzuzufügen, kontaktieren Sie bitte OpenSign™ unter <1>billing@opensignlabs.com</1> für Unterstützung.",
|
||||||
"title-length-alert": "Der Titel darf höchstens 250 Zeichen lang sein.",
|
"title-length-alert": "Der Titel darf höchstens 250 Zeichen lang sein.",
|
||||||
"note-length-alert": "Die Notiz darf höchstens 200 Zeichen lang sein.",
|
"note-length-alert": "Die Notiz darf höchstens 200 Zeichen lang sein.",
|
||||||
"description-length-alert": "Die Beschreibung darf höchstens 500 Zeichen lang sein."
|
"description-length-alert": "Die Beschreibung darf höchstens 500 Zeichen lang sein.",
|
||||||
|
"fix-&-resend-document": "Dokument korrigieren und erneut senden",
|
||||||
|
"do-you-want-recreate-document?": "Dadurch wird ein Entwurf aus diesem Dokument mit allen vorhandenen Feldern erstellt. Sind Sie sicher, dass Sie dieses Dokument neu erstellen möchten?",
|
||||||
|
"start-editing": "Bearbeitung starten",
|
||||||
|
"unsaved-changes-discard-them?": "Sie haben ungespeicherte Änderungen. Verwerfen?",
|
||||||
|
"yes-discard": "Ja, verwerfen",
|
||||||
|
"LTV-enabled-signatures": "LTV-fähige Signaturen",
|
||||||
|
"BETA": "BETA",
|
||||||
|
"two-factor-authentication": "Zwei-Faktor-Authentifizierung",
|
||||||
|
"2fa-help-text": "Die Zwei-Faktor-Authentifizierung fügt Ihrem Konto eine zusätzliche Sicherheitsebene hinzu, indem sie mehr als nur ein Passwort zum Anmelden erfordert.",
|
||||||
|
"2fa-help-bullet1": "Erhöht die Sicherheit, indem sowohl Ihr Passwort als auch ein Bestätigungscode erforderlich sind.",
|
||||||
|
"2fa-help-bullet2": "Der Bestätigungscode wird von einer Authentifizierungs-App auf Ihrem Gerät generiert.",
|
||||||
|
"2fa-help-bullet3": "Schützt Ihr Konto, selbst wenn Ihr Passwort kompromittiert wurde.",
|
||||||
|
"setup-2fa": "2FA einrichten",
|
||||||
|
"setup-2fa-again": "2FA erneut einrichten",
|
||||||
|
"2fa-setup-intro": "Schützen Sie Ihr Konto mit der Zwei-Faktor-Authentifizierung. Wenn aktiviert, müssen Sie bei jeder Anmeldung einen Code aus Ihrer Authentifizierungs-App eingeben.",
|
||||||
|
"scan-qr-code": "QR-Code scannen",
|
||||||
|
"scan-qr-instructions": "Verwenden Sie eine Authentifizierungs-App wie Google Authenticator, Microsoft Authenticator oder Authy, um diesen QR-Code zu scannen.",
|
||||||
|
"manual-setup-instructions": "Können Sie den Code nicht scannen? Sie können Ihre Authentifizierungs-App manuell mit diesem geheimen Schlüssel einrichten:",
|
||||||
|
"secret-key": "Geheimer Schlüssel",
|
||||||
|
"copied-to-clipboard": "In die Zwischenablage kopiert",
|
||||||
|
"copy-to-clipboard": "In die Zwischenablage kopieren",
|
||||||
|
"recovery-codes": "Wiederherstellungscodes",
|
||||||
|
"recovery-codes-instructions": "Speichern Sie diese Wiederherstellungscodes an einem sicheren Ort. Wenn Sie den Zugriff auf Ihre Authentifizierungs-App verlieren, können Sie einen dieser Einmal-Codes verwenden, um sich anzumelden.",
|
||||||
|
"download-recovery-codes": "Wiederherstellungscodes herunterladen",
|
||||||
|
"verification-code": "Bestätigungscode",
|
||||||
|
"enter-code-from-authenticator-app": "Geben Sie den 6-stelligen Code aus Ihrer Authentifizierungs-App ein",
|
||||||
|
"verification-code-required": "Bestätigungscode ist erforderlich",
|
||||||
|
"verification-code-invalid": "Ungültiger Bestätigungscode. Bitte versuchen Sie es erneut.",
|
||||||
|
"2fa-enabled": "2FA aktiviert",
|
||||||
|
"2fa-enabled-successfully": "Zwei-Faktor-Authentifizierung erfolgreich aktiviert",
|
||||||
|
"2fa-setup-complete": "Einrichtung abgeschlossen!",
|
||||||
|
"2fa-setup-complete-instructions": "Ihr Konto ist jetzt mit der Zwei-Faktor-Authentifizierung geschützt. Sie müssen bei jeder Anmeldung einen Bestätigungscode eingeben.",
|
||||||
|
"two-factor-verification": "Zwei-Faktor-Verifizierung",
|
||||||
|
"enter-verification-code-instructions": "Geben Sie den 6-stelligen Bestätigungscode aus Ihrer Authentifizierungs-App ein, um fortzufahren.",
|
||||||
|
"recovery-code": "Wiederherstellungscode",
|
||||||
|
"enter-recovery-code-help": "Geben Sie einen Ihrer Wiederherstellungscodes ein",
|
||||||
|
"recovery-code-required": "Wiederherstellungscode ist erforderlich",
|
||||||
|
"use-verification-code-instead": "Stattdessen Bestätigungscode verwenden",
|
||||||
|
"use-recovery-code-instead": "Stattdessen Wiederherstellungscode verwenden",
|
||||||
|
"regenerate-2fa-remove-existing": "Sind Sie sicher, dass Sie die Zwei-Faktor-Authentifizierung neu generieren möchten? Diese Aktion entfernt Ihre bestehenden Authentifizierungseinstellungen.",
|
||||||
|
"use-passkey": "Mit Passkey anmelden",
|
||||||
|
"security-section": "Sicherheit",
|
||||||
|
"passkey-authentication": "Passkey-Authentifizierung",
|
||||||
|
"passkey-not-supported": "Ihr Browser oder Gerät unterstützt keine Passkey-Authentifizierung",
|
||||||
|
"passkey-description": "Passkeys bieten eine stärkere, phishing-resistente Alternative zu Passwörtern. Sie können Ihren Fingerabdruck, Ihre Gesichtserkennung oder Ihre Geräte-PIN verwenden, um sich sicher anzumelden.",
|
||||||
|
"passkey-tooltip": "Passkeys sind eine einfachere und sicherere Alternative zu Passwörtern. Sie verwenden biometrische Daten wie Fingerabdrücke oder Gesichtserkennung, die bereits auf Ihrem Gerät gespeichert sind.",
|
||||||
|
"security-section-help": "Verwalten Sie Sicherheitsoptionen, einschließlich Passkeys und Authentifizierungsmethoden, um Ihr Konto zu schützen.",
|
||||||
|
"passkey-register": "Passkey registrieren",
|
||||||
|
"passkey-register-another": "Weiteren Passkey registrieren",
|
||||||
|
"passkey-registered": "Passkey registriert",
|
||||||
|
"passkey-registering": "Passkey wird registriert...",
|
||||||
|
"passkey-registered-success": "Passkey erfolgreich registriert!",
|
||||||
|
"passkey-registration-failed": "Passkey-Registrierung fehlgeschlagen",
|
||||||
|
"passkey-auth-failed": "Passkey-Authentifizierung fehlgeschlagen",
|
||||||
|
"passkey-missing-user-info": "Benutzerinformationen erforderlich",
|
||||||
|
"passkeys-list": "Ihre Passkeys",
|
||||||
|
"default-passkey": "Ihr Passkey",
|
||||||
|
"passkey-rename": "Umbenennen",
|
||||||
|
"passkey-delete": "Löschen",
|
||||||
|
"passkey-rename-title": "Passkey umbenennen",
|
||||||
|
"passkey-delete-title": "Passkey löschen",
|
||||||
|
"passkey-delete-confirm": "Möchten Sie den Passkey \"{{name}}\" wirklich löschen?",
|
||||||
|
"passkey-name": "Passkey-Name",
|
||||||
|
"passkey-name-placeholder": "Geben Sie einen beschreibenden Namen für diesen Passkey ein",
|
||||||
|
"passkey-renamed-success": "Passkey erfolgreich umbenannt",
|
||||||
|
"passkey-deleted-success": "Passkey erfolgreich gelöscht",
|
||||||
|
"passkey-rename-failed": "Passkey konnte nicht umbenannt werden",
|
||||||
|
"passkey-delete-failed": "Passkey konnte nicht gelöscht werden",
|
||||||
|
"processing": "Wird verarbeitet...",
|
||||||
|
"today": "Heute",
|
||||||
|
"yesterday": "Gestern",
|
||||||
|
"days-ago": "Vor {{days}} Tagen",
|
||||||
|
"verify-with-passkey": "Mit Passkey verifizieren",
|
||||||
|
"verify-with-otp": "Mit OTP verifizieren",
|
||||||
|
"verify-identity": "Identität verifizieren",
|
||||||
|
"verify-account": "Verifizieren Sie Ihre Identität",
|
||||||
|
"verification": "Verifizierung",
|
||||||
|
"passkey-verification-failed": "Passkey-Verifizierung fehlgeschlagen",
|
||||||
|
"security-auth-help": {
|
||||||
|
"p1": "Administre la configuración de seguridad de su cuenta para mantener sus datos seguros. OpenSign admite métodos de autenticación avanzados para mejorar la protección de la cuenta.",
|
||||||
|
"2fa-auth-help": "Agregue una capa adicional de seguridad activando 2FA. Esto requiere ingresar un código de verificación desde una aplicación autenticadora después de su contraseña.",
|
||||||
|
"passkey-auth-help": "Use claves de acceso para iniciar sesión sin contraseña con verificación biométrica o basada en el dispositivo, proporcionando una seguridad sólida y comodidad."
|
||||||
|
},
|
||||||
|
"signer-already-present": "Unterzeichner bereits vorhanden",
|
||||||
|
"kiosk-sign": "Kiosk-Unterschrift",
|
||||||
|
"dont-have-access-to-template": "Das template wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.",
|
||||||
|
"kiosk-info": "Kiosk Modus ermöglicht es Ihnen, persönliche Unterschriften schnell und effizient zu erfassen. Ideal für Messen, Veranstaltungen oder Laufkundschaft, bei denen alle Unterzeichner physisch anwesend sind. ",
|
||||||
|
"learn-more": "Mehr erfahren",
|
||||||
|
"finish-mssg": "Sind Sie sicher, dass Sie das Dokument abschließen möchten?",
|
||||||
|
"review": "Überprüfen",
|
||||||
|
"next-field": "Nächstes Feld",
|
||||||
|
"required-mssg": "{{leftRequiredWidget}} von {{totalWidget}} Feldern übrig"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"header-news": "New feature: Teams plan users can now integrate their own AWS S3 buckets for file storage",
|
"header-news": "New feature: Protect your account with Two-Factor Authentication (2FA) and enjoy the future of login with Passkeys — no passwords needed.",
|
||||||
"header-news-btn": "Setup now",
|
"header-news-btn": "Setup now",
|
||||||
"create-account": "Create account",
|
"create-account": "Create account",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
@@ -39,8 +39,10 @@
|
|||||||
"save": "Save",
|
"save": "Save",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"upgrade-now": "Upgrade now",
|
"upgrade-now": "Upgrade now",
|
||||||
|
"contact-now": "Contact now",
|
||||||
"upgrade-to": "Upgrade to",
|
"upgrade-to": "Upgrade to",
|
||||||
"plan": "Plan",
|
"plan": "Plan",
|
||||||
|
"subscription-renew-warning": "Your subscription will expire in {{remainingDays}} days. Please renew your subscription.",
|
||||||
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
|
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
|
||||||
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
|
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
|
||||||
"user-name-limit-char": "To have a username less than 8 character please subscribe",
|
"user-name-limit-char": "To have a username less than 8 character please subscribe",
|
||||||
@@ -142,6 +144,7 @@
|
|||||||
"Quick send": "Quick send",
|
"Quick send": "Quick send",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
"Share with team": "Share with team",
|
"Share with team": "Share with team",
|
||||||
|
"Share with user": "Share with colleague",
|
||||||
"Share": "Share",
|
"Share": "Share",
|
||||||
"View": "View",
|
"View": "View",
|
||||||
"option": "Option",
|
"option": "Option",
|
||||||
@@ -152,7 +155,9 @@
|
|||||||
"Duplicate Template": "Duplicate template",
|
"Duplicate Template": "Duplicate template",
|
||||||
"Duplicate": "Duplicate",
|
"Duplicate": "Duplicate",
|
||||||
"daily-mail-quota": "Daily Email Quota",
|
"daily-mail-quota": "Daily Email Quota",
|
||||||
"Save as template": "Save as template"
|
"Save as template": "Save as template",
|
||||||
|
"Fix & resend": "Fix & Resend",
|
||||||
|
"Kiosk Mode": "Kiosk Mode"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Sr.No",
|
"Sr.No": "Sr.No",
|
||||||
@@ -243,6 +248,7 @@
|
|||||||
"API": "API",
|
"API": "API",
|
||||||
"api-token": "API token",
|
"api-token": "API token",
|
||||||
"regenerate-token": "Regenerate live token",
|
"regenerate-token": "Regenerate live token",
|
||||||
|
"remove-background": "Remove Background",
|
||||||
"generate-token": "Generate live token",
|
"generate-token": "Generate live token",
|
||||||
"view-docs": "View docs",
|
"view-docs": "View docs",
|
||||||
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
|
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
|
||||||
@@ -303,7 +309,7 @@
|
|||||||
"send": "Send",
|
"send": "Send",
|
||||||
"quick-send-alert-1": "All roles in this document are currently linked to contacts. To quick send copies of this template to multiple signers, please ensure that at least one role is not linked to any contact.",
|
"quick-send-alert-1": "All roles in this document are currently linked to contacts. To quick send copies of this template to multiple signers, please ensure that at least one role is not linked to any contact.",
|
||||||
"quick-send-alert-2": "Please ensure there's at least one signature widget added for all recipients.",
|
"quick-send-alert-2": "Please ensure there's at least one signature widget added for all recipients.",
|
||||||
"quick-send-alert-3": "Please add at least one role to this template in order to 'quick send' copies of it to multiple signers.",
|
"quick-send-alert-3": "Please add at least one role to this template.",
|
||||||
"quick-send-alert-4": "Quick send reached limit.",
|
"quick-send-alert-4": "Quick send reached limit.",
|
||||||
"copy-link": "Copy link",
|
"copy-link": "Copy link",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
@@ -340,7 +346,7 @@
|
|||||||
"verify-email-1": "Verify email",
|
"verify-email-1": "Verify email",
|
||||||
"resend": "Resend",
|
"resend": "Resend",
|
||||||
"contact-details": "Contact details",
|
"contact-details": "Contact details",
|
||||||
"verify-email": "Please verify your email !",
|
"verify-email": "Please verify your email to continue. This is a one-time step to confirm the email associated with your OpenSign account.",
|
||||||
"send-otp": " Send OTP",
|
"send-otp": " Send OTP",
|
||||||
"otp-placeholder": "Enter verification code received over email",
|
"otp-placeholder": "Enter verification code received over email",
|
||||||
"loading-doc": "Loading document..",
|
"loading-doc": "Loading document..",
|
||||||
@@ -476,6 +482,7 @@
|
|||||||
"document-alert": "Document alert",
|
"document-alert": "Document alert",
|
||||||
"owner-subscription-expired": "Owner's subscription has expired.",
|
"owner-subscription-expired": "Owner's subscription has expired.",
|
||||||
"subscription-expired": "Subscription Expired",
|
"subscription-expired": "Subscription Expired",
|
||||||
|
"owner-doesnt-have-paid-plan": "Owner doesn't have paid plan.",
|
||||||
"alert-message": "Alert message",
|
"alert-message": "Alert message",
|
||||||
"document-decline": "Document decline",
|
"document-decline": "Document decline",
|
||||||
"decline-alert-1": "Are you sure want to decline this document ?",
|
"decline-alert-1": "Are you sure want to decline this document ?",
|
||||||
@@ -881,5 +888,97 @@
|
|||||||
"contact-billing-at-opensign": "To add more seats, please contact OpenSign™ at <1>billing@opensignlabs.com</1> for assistance",
|
"contact-billing-at-opensign": "To add more seats, please contact OpenSign™ at <1>billing@opensignlabs.com</1> for assistance",
|
||||||
"title-length-alert": "Title must be at most 250 characters long.",
|
"title-length-alert": "Title must be at most 250 characters long.",
|
||||||
"note-length-alert": "Note must be at most 200 characters long.",
|
"note-length-alert": "Note must be at most 200 characters long.",
|
||||||
"description-length-alert": "Description must be at most 500 characters long."
|
"description-length-alert": "Description must be at most 500 characters long.",
|
||||||
}
|
"fix-&-resend-document": "Fix & Resend Document",
|
||||||
|
"do-you-want-recreate-document?": "This will create a draft from this document with all fields intact. Are you sure you want to recreate this document?",
|
||||||
|
"start-editing": "Start Editing",
|
||||||
|
"unsaved-changes-discard-them?": "You have unsaved changes. Discard them?",
|
||||||
|
"yes-discard": "Yes, Discard",
|
||||||
|
"LTV-enabled-signatures": "LTV enabled signatures",
|
||||||
|
"BETA": "BETA",
|
||||||
|
"two-factor-authentication": "Two-Factor Authentication",
|
||||||
|
"2fa-help-text": "Two-factor authentication adds an extra layer of security to your account by requiring more than just a password to sign in.",
|
||||||
|
"2fa-help-bullet1": "Enhances security by requiring both your password and a verification code.",
|
||||||
|
"2fa-help-bullet2": "The verification code is generated by an authenticator app on your device.",
|
||||||
|
"2fa-help-bullet3": "Protects your account even if your password is compromised.",
|
||||||
|
"setup-2fa": "Setup 2FA",
|
||||||
|
"setup-2fa-again": "Setup 2FA again",
|
||||||
|
"2fa-setup-intro": "Protect your account with two-factor authentication. When enabled, you'll need to enter a code from your authenticator app whenever you sign in.",
|
||||||
|
"scan-qr-code": "Scan QR Code",
|
||||||
|
"scan-qr-instructions": "Use an authenticator app like Google Authenticator, Microsoft Authenticator, or Authy to scan this QR code.",
|
||||||
|
"manual-setup-instructions": "Can't scan the code? You can manually set up your authenticator app using this secret key:",
|
||||||
|
"secret-key": "Secret Key",
|
||||||
|
"copied-to-clipboard": "Copied to clipboard",
|
||||||
|
"copy-to-clipboard": "Copy to clipboard",
|
||||||
|
"recovery-codes": "Recovery Codes",
|
||||||
|
"recovery-codes-instructions": "Save these recovery codes in a secure location. If you lose access to your authenticator app, you can use one of these one-time codes to sign in.",
|
||||||
|
"download-recovery-codes": "Download Recovery Codes",
|
||||||
|
"verification-code": "Verification Code",
|
||||||
|
"enter-code-from-authenticator-app": "Enter the 6-digit code from your authenticator app",
|
||||||
|
"verification-code-required": "Verification code is required",
|
||||||
|
"verification-code-invalid": "Invalid verification code. Please try again.",
|
||||||
|
"2fa-enabled": "2FA enabled",
|
||||||
|
"2fa-enabled-successfully": "Two-factor authentication enabled successfully",
|
||||||
|
"2fa-setup-complete": "Setup Complete!",
|
||||||
|
"2fa-setup-complete-instructions": "Your account is now protected with two-factor authentication. You'll need to enter a verification code each time you sign in.",
|
||||||
|
"two-factor-verification": "Two-Factor Verification",
|
||||||
|
"enter-verification-code-instructions": "Enter the 6-digit verification code from your authenticator app to continue.",
|
||||||
|
"recovery-code": "Recovery Code",
|
||||||
|
"enter-recovery-code-help": "Enter one of your recovery codes",
|
||||||
|
"recovery-code-required": "Recovery code is required",
|
||||||
|
"use-verification-code-instead": "Use verification code instead",
|
||||||
|
"use-recovery-code-instead": "Use recovery code instead",
|
||||||
|
"regenerate-2fa-remove-existing": "Are you sure you want to regenerate two-factor authentication? This action will remove your existing authentication settings.",
|
||||||
|
"use-passkey": "Sign in with passkey",
|
||||||
|
"security-section": "Security",
|
||||||
|
"passkey-authentication": "Passkey Authentication",
|
||||||
|
"passkey-not-supported": "Your browser or device doesn't support passkey authentication",
|
||||||
|
"passkey-description": "Passkeys provide a stronger, phishing-resistant alternative to passwords. You can use your fingerprint, face recognition, or device PIN to sign in securely.",
|
||||||
|
"passkey-tooltip": "Passkeys are a simpler and more secure alternative to passwords. They use biometric data like fingerprints or facial recognition that's already stored on your device.",
|
||||||
|
"security-section-help": "Manage security options including passkeys and authentication methods to protect your account.",
|
||||||
|
"passkey-register": "Register passkey",
|
||||||
|
"passkey-register-another": "Register another passkey",
|
||||||
|
"passkey-registered": "Passkey registered",
|
||||||
|
"passkey-registering": "Registering passkey...",
|
||||||
|
"passkey-registered-success": "Passkey successfully registered!",
|
||||||
|
"passkey-registration-failed": "Failed to register passkey",
|
||||||
|
"passkey-auth-failed": "Passkey authentication failed",
|
||||||
|
"passkey-missing-user-info": "User information is required",
|
||||||
|
"passkeys-list": "Your Passkeys",
|
||||||
|
"default-passkey": "Your Passkey",
|
||||||
|
"passkey-rename": "Rename",
|
||||||
|
"passkey-delete": "Delete",
|
||||||
|
"passkey-rename-title": "Rename Passkey",
|
||||||
|
"passkey-delete-title": "Delete Passkey",
|
||||||
|
"passkey-delete-confirm": "Are you sure you want to delete passkey \"{{name}}\"?",
|
||||||
|
"passkey-name": "Passkey Name",
|
||||||
|
"passkey-name-placeholder": "Enter a descriptive name for this passkey",
|
||||||
|
"passkey-renamed-success": "Passkey renamed successfully",
|
||||||
|
"passkey-deleted-success": "Passkey deleted successfully",
|
||||||
|
"passkey-rename-failed": "Failed to rename passkey",
|
||||||
|
"passkey-delete-failed": "Failed to delete passkey",
|
||||||
|
"processing": "Processing...",
|
||||||
|
"today": "Today",
|
||||||
|
"yesterday": "Yesterday",
|
||||||
|
"days-ago": "{{days}} days ago",
|
||||||
|
"verify-with-passkey": "Verify with passkey",
|
||||||
|
"verify-with-otp": "Verify with OTP",
|
||||||
|
"verification": "Verification",
|
||||||
|
"verify-account": "Verify your account",
|
||||||
|
"verify-identity": "Verify identity",
|
||||||
|
"passkey-verification-failed": "Passkey verification failed. Please try again or use OTP.",
|
||||||
|
"security-auth-help": {
|
||||||
|
"p1":"Manage your account's security settings to keep your data safe. OpenSign supports advanced authentication methods to enhance account protection.",
|
||||||
|
"2fa-auth-help":" Add an extra layer of security by enabling 2FA. This requires you to enter a verification code from an authenticator app after your password.",
|
||||||
|
"passkey-auth-help":"Use passkeys for passwordless sign-in with biometric or device-based verification, providing both strong security and convenience."
|
||||||
|
},
|
||||||
|
"signer-already-present": "Signer already present",
|
||||||
|
"kiosk-sign": "Kiosk Sign",
|
||||||
|
"dont-have-access-to-template": "The template has been deleted or you don't have access. Please contact the sender.",
|
||||||
|
"kiosk-info": "Kiosk Mode lets you collect in-person signatures quickly and efficiently. Ideal for trade shows, events, or walk-in scenarios where all signers are physically present. ",
|
||||||
|
"learn-more": "Learn more",
|
||||||
|
"finish-mssg":" Are you sure you want to finish the document ?",
|
||||||
|
"review":"Review",
|
||||||
|
"next-field":"Next Field",
|
||||||
|
"required-mssg":"{{leftRequiredWidget}} of {{totalWidget}} fields left"
|
||||||
|
}
|
||||||
@@ -39,8 +39,10 @@
|
|||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"upgrade-now": "Mejorar ahora",
|
"upgrade-now": "Mejorar ahora",
|
||||||
|
"contact-now": "Contactar ahora",
|
||||||
"upgrade-to": "Mejorar a",
|
"upgrade-to": "Mejorar a",
|
||||||
"plan": "Plan",
|
"plan": "Plan",
|
||||||
|
"subscription-renew-warning": "Su suscripción vencerá en {{remainingDays}} días. Por favor, renueve su suscripción.",
|
||||||
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
||||||
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
||||||
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
||||||
@@ -142,6 +144,7 @@
|
|||||||
"Quick send": "Envío rápido",
|
"Quick send": "Envío rápido",
|
||||||
"Edit": "Editar",
|
"Edit": "Editar",
|
||||||
"Share with team": "Compartir con el equipo",
|
"Share with team": "Compartir con el equipo",
|
||||||
|
"Share with user": "Compartir con un colega",
|
||||||
"Share": "Compartir",
|
"Share": "Compartir",
|
||||||
"View": "Ver",
|
"View": "Ver",
|
||||||
"option": "Opción",
|
"option": "Opción",
|
||||||
@@ -152,7 +155,9 @@
|
|||||||
"Duplicate Template": "Plantilla duplicada",
|
"Duplicate Template": "Plantilla duplicada",
|
||||||
"Duplicate": "Duplicada",
|
"Duplicate": "Duplicada",
|
||||||
"daily-mail-quota": "Cuota diaria de correos electrónicos",
|
"daily-mail-quota": "Cuota diaria de correos electrónicos",
|
||||||
"Save as template": "Guardar como plantilla"
|
"Save as template": "Guardar como plantilla",
|
||||||
|
"Fix & resend": "Corregir y reenviar",
|
||||||
|
"Kiosk Mode": "Modo Kiosco"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nº",
|
"Sr.No": "Nº",
|
||||||
@@ -244,6 +249,7 @@
|
|||||||
"API": "API",
|
"API": "API",
|
||||||
"api-token": "Token API",
|
"api-token": "Token API",
|
||||||
"regenerate-token": "Regenerar token activo",
|
"regenerate-token": "Regenerar token activo",
|
||||||
|
"remove-background": "Eliminar fondo",
|
||||||
"generate-token": "Generar token activo",
|
"generate-token": "Generar token activo",
|
||||||
"view-docs": "Ver documentación",
|
"view-docs": "Ver documentación",
|
||||||
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
||||||
@@ -304,7 +310,7 @@
|
|||||||
"send": "Enviar",
|
"send": "Enviar",
|
||||||
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
||||||
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
||||||
"quick-send-alert-3": "Por favor, añade al menos un rol a esta plantilla para poder hacer un «envío rápido» de copias a varios firmantes.",
|
"quick-send-alert-3": "Veuillez ajouter au moins un rôle à ce template.",
|
||||||
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
||||||
"copy-link": "Copiar enlace",
|
"copy-link": "Copiar enlace",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
@@ -341,7 +347,7 @@
|
|||||||
"verify-email-1": "Verificar correo",
|
"verify-email-1": "Verificar correo",
|
||||||
"resend": "Reenviar",
|
"resend": "Reenviar",
|
||||||
"contact-details": "Detalles del contacto",
|
"contact-details": "Detalles del contacto",
|
||||||
"verify-email": "¡Por favor, verifica tu correo!",
|
"verify-email": "Por favor verifique su correo electrónico para continuar. Este es un paso único para confirmar el correo electrónico asociado a su cuenta de OpenSign.",
|
||||||
"send-otp": " Enviar OTP",
|
"send-otp": " Enviar OTP",
|
||||||
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
||||||
"loading-doc": "Cargando documento...",
|
"loading-doc": "Cargando documento...",
|
||||||
@@ -475,6 +481,7 @@
|
|||||||
"mail-not-delivered": "correo no entregado",
|
"mail-not-delivered": "correo no entregado",
|
||||||
"document-alert": "Alerta de documento",
|
"document-alert": "Alerta de documento",
|
||||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||||
|
"owner-doesnt-have-paid-plan": "El propietario no tiene un plan de pago.",
|
||||||
"subscription-expired": "Suscripción expirada",
|
"subscription-expired": "Suscripción expirada",
|
||||||
"alert-message": "Mensaje de alerta",
|
"alert-message": "Mensaje de alerta",
|
||||||
"document-decline": "Rechazar documento",
|
"document-decline": "Rechazar documento",
|
||||||
@@ -881,5 +888,97 @@
|
|||||||
"contact-billing-at-opensign": "Para agregar más asientos, comuníquese con OpenSign™ a <1>billing@opensignlabs.com</1> para obtener ayuda.",
|
"contact-billing-at-opensign": "Para agregar más asientos, comuníquese con OpenSign™ a <1>billing@opensignlabs.com</1> para obtener ayuda.",
|
||||||
"title-length-alert": "El título debe tener como máximo 250 caracteres.",
|
"title-length-alert": "El título debe tener como máximo 250 caracteres.",
|
||||||
"note-length-alert": "La nota debe tener como máximo 200 caracteres.",
|
"note-length-alert": "La nota debe tener como máximo 200 caracteres.",
|
||||||
"description-length-alert": "La descripción debe tener como máximo 500 caracteres."
|
"description-length-alert": "La descripción debe tener como máximo 500 caracteres.",
|
||||||
|
"fix-&-resend-document": "Corregir y reenviar el documento",
|
||||||
|
"do-you-want-recreate-document?": "Esto creará un borrador a partir de este documento con todos los campos intactos. ¿Está seguro de que desea recrear este documento?",
|
||||||
|
"start-editing": "Comenzar a editar",
|
||||||
|
"unsaved-changes-discard-them?": "Tienes cambios sin guardar. ¿Deseas descartarlos?",
|
||||||
|
"yes-discard": "Sí, descartar",
|
||||||
|
"LTV-enabled-signatures": "Firmas con LTV habilitado",
|
||||||
|
"BETA": "BETA",
|
||||||
|
"two-factor-authentication": "Autenticación de dos factores",
|
||||||
|
"2fa-help-text": "La autenticación de dos factores agrega una capa adicional de seguridad a su cuenta al requerir más que solo una contraseña para iniciar sesión.",
|
||||||
|
"2fa-help-bullet1": "Mejora la seguridad al requerir tanto su contraseña como un código de verificación.",
|
||||||
|
"2fa-help-bullet2": "El código de verificación es generado por una aplicación de autenticación en su dispositivo.",
|
||||||
|
"2fa-help-bullet3": "Protege su cuenta incluso si su contraseña se ve comprometida.",
|
||||||
|
"setup-2fa": "Configurar 2FA",
|
||||||
|
"setup-2fa-again": "Configurar 2FA nuevamente",
|
||||||
|
"2fa-setup-intro": "Proteja su cuenta con la autenticación de dos factores. Cuando esté habilitada, deberá ingresar un código de su aplicación de autenticación cada vez que inicie sesión.",
|
||||||
|
"scan-qr-code": "Escanear código QR",
|
||||||
|
"scan-qr-instructions": "Use una aplicación de autenticación como Google Authenticator, Microsoft Authenticator o Authy para escanear este código QR.",
|
||||||
|
"manual-setup-instructions": "¿No puede escanear el código? Puede configurar manualmente su aplicación de autenticación utilizando esta clave secreta:",
|
||||||
|
"secret-key": "Clave secreta",
|
||||||
|
"copied-to-clipboard": "Copiado al portapapeles",
|
||||||
|
"copy-to-clipboard": "Copiar al portapapeles",
|
||||||
|
"recovery-codes": "Códigos de recuperación",
|
||||||
|
"recovery-codes-instructions": "Guarde estos códigos de recuperación en un lugar seguro. Si pierde el acceso a su aplicación de autenticación, puede usar uno de estos códigos de un solo uso para iniciar sesión.",
|
||||||
|
"download-recovery-codes": "Descargar códigos de recuperación",
|
||||||
|
"verification-code": "Código de verificación",
|
||||||
|
"enter-code-from-authenticator-app": "Ingrese el código de 6 dígitos de su aplicación de autenticación",
|
||||||
|
"verification-code-required": "Se requiere el código de verificación",
|
||||||
|
"verification-code-invalid": "Código de verificación inválido. Por favor, inténtelo de nuevo.",
|
||||||
|
"2fa-enabled": "2FA activado",
|
||||||
|
"2fa-enabled-successfully": "Autenticación de dos factores habilitada con éxito",
|
||||||
|
"2fa-setup-complete": "¡Configuración completa!",
|
||||||
|
"2fa-setup-complete-instructions": "Su cuenta ahora está protegida con autenticación de dos factores. Deberá ingresar un código de verificación cada vez que inicie sesión.",
|
||||||
|
"two-factor-verification": "Verificación de dos factores",
|
||||||
|
"enter-verification-code-instructions": "Ingrese el código de verificación de 6 dígitos de su aplicación de autenticación para continuar.",
|
||||||
|
"recovery-code": "Código de recuperación",
|
||||||
|
"enter-recovery-code-help": "Ingrese uno de sus códigos de recuperación",
|
||||||
|
"recovery-code-required": "Se requiere el código de recuperación",
|
||||||
|
"use-verification-code-instead": "Usar código de verificación en su lugar",
|
||||||
|
"use-recovery-code-instead": "Usar código de recuperación en su lugar",
|
||||||
|
"regenerate-2fa-remove-existing": "¿Está seguro de que desea regenerar la autenticación de dos factores? Esta acción eliminará su configuración de autenticación actual.",
|
||||||
|
"use-passkey": "Iniciar sesión con passkey",
|
||||||
|
"security-section": "Seguridad",
|
||||||
|
"passkey-authentication": "Autenticación con passkey",
|
||||||
|
"passkey-not-supported": "Tu navegador o dispositivo no admite la autenticación con passkey",
|
||||||
|
"passkey-description": "Las passkeys proporcionan una alternativa más segura y resistente al phishing que las contraseñas. Puedes usar tu huella digital, reconocimiento facial o PIN del dispositivo para iniciar sesión de forma segura.",
|
||||||
|
"passkey-tooltip": "Las passkeys son una alternativa más simple y segura a las contraseñas. Usan datos biométricos como huellas digitales o reconocimiento facial ya almacenados en tu dispositivo.",
|
||||||
|
"security-section-help": "Administra las opciones de seguridad, incluidas las passkeys y métodos de autenticación para proteger tu cuenta.",
|
||||||
|
"passkey-register": "Registrar passkey",
|
||||||
|
"passkey-register-another": "Registrar otra passkey",
|
||||||
|
"passkey-registered": "Passkey registrada",
|
||||||
|
"passkey-registering": "Registrando passkey...",
|
||||||
|
"passkey-registered-success": "¡Passkey registrada con éxito!",
|
||||||
|
"passkey-registration-failed": "Fallo al registrar la passkey",
|
||||||
|
"passkey-auth-failed": "Falló la autenticación con passkey",
|
||||||
|
"passkey-missing-user-info": "Se requiere información del usuario",
|
||||||
|
"passkeys-list": "Tus passkeys",
|
||||||
|
"default-passkey": "Tu passkey",
|
||||||
|
"passkey-rename": "Renombrar",
|
||||||
|
"passkey-delete": "Eliminar",
|
||||||
|
"passkey-rename-title": "Renombrar passkey",
|
||||||
|
"passkey-delete-title": "Eliminar passkey",
|
||||||
|
"passkey-delete-confirm": "¿Estás seguro de que deseas eliminar la passkey \"{{name}}\"?",
|
||||||
|
"passkey-name": "Nombre de la passkey",
|
||||||
|
"passkey-name-placeholder": "Introduce un nombre descriptivo para esta passkey",
|
||||||
|
"passkey-renamed-success": "Passkey renombrada con éxito",
|
||||||
|
"passkey-deleted-success": "Passkey eliminada con éxito",
|
||||||
|
"passkey-rename-failed": "No se pudo renombrar la passkey",
|
||||||
|
"passkey-delete-failed": "No se pudo eliminar la passkey",
|
||||||
|
"processing": "Procesando...",
|
||||||
|
"today": "Hoy",
|
||||||
|
"yesterday": "Ayer",
|
||||||
|
"days-ago": "Hace {{days}} días",
|
||||||
|
"verify-with-passkey": "Verificar con passkey",
|
||||||
|
"verify-with-otp": "Verificar con OTP",
|
||||||
|
"verify-identity": "Verificar identidad",
|
||||||
|
"verify-account": "Verifique su identidad",
|
||||||
|
"verification": "Verificación",
|
||||||
|
"passkey-verification-failed": "La verificación con passkey ha fallado",
|
||||||
|
"security-auth-help": {
|
||||||
|
"p1": "Administre la configuración de seguridad de su cuenta para mantener sus datos seguros. OpenSign admite métodos de autenticación avanzados para mejorar la protección de la cuenta.",
|
||||||
|
"2fa-auth-help": "Agregue una capa adicional de seguridad activando 2FA. Esto requiere ingresar un código de verificación desde una aplicación autenticadora después de su contraseña.",
|
||||||
|
"passkey-auth-help": "Use claves de acceso para iniciar sesión sin contraseña con verificación biométrica o basada en el dispositivo, proporcionando una seguridad sólida y comodidad."
|
||||||
|
},
|
||||||
|
"signer-already-present": "Firmante ya presente",
|
||||||
|
"kiosk-sign": "Firma en quiosco",
|
||||||
|
"dont-have-access-to-template": "El template ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.",
|
||||||
|
"kiosk-info": "El Modo Kiosco le permite recopilar firmas en persona de forma rápida y eficiente. Ideal para ferias, eventos o situaciones con personas que firman en el lugar. ",
|
||||||
|
"learn-more": "Más información",
|
||||||
|
"finish-mssg": "¿Está seguro de que desea finalizar el documento?",
|
||||||
|
"review": "Revisar",
|
||||||
|
"next-field": "Siguiente campo",
|
||||||
|
"required-mssg": "{{leftRequiredWidget}} de {{totalWidget}} campos restantes"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,11 @@
|
|||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"upgrade-now": "Mettre à jour maintenant",
|
"upgrade-now": "Mettre à jour maintenant",
|
||||||
|
"contact-now": "Contacter maintenant",
|
||||||
"upgrade-to": "Mettre à niveau vers",
|
"upgrade-to": "Mettre à niveau vers",
|
||||||
"pro": "PRO",
|
"pro": "PRO",
|
||||||
"plan": "Offre",
|
"plan": "Offre",
|
||||||
|
"subscription-renew-warning": "Votre abonnement expirera dans {{remainingDays}} jours. Veuillez renouveler votre abonnement.",
|
||||||
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
|
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
|
||||||
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
|
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
|
||||||
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
|
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
|
||||||
@@ -163,6 +165,7 @@
|
|||||||
"Quick send": "Envoi rapide",
|
"Quick send": "Envoi rapide",
|
||||||
"Edit": "Modifier",
|
"Edit": "Modifier",
|
||||||
"Share with team": "Partager avec l'équipe",
|
"Share with team": "Partager avec l'équipe",
|
||||||
|
"Share with user": "Partager avec un collègue",
|
||||||
"Share": "Partager",
|
"Share": "Partager",
|
||||||
"View": "Voir",
|
"View": "Voir",
|
||||||
"option": "Option",
|
"option": "Option",
|
||||||
@@ -173,7 +176,9 @@
|
|||||||
"Duplicate Template": "dupliquer le modèle",
|
"Duplicate Template": "dupliquer le modèle",
|
||||||
"Duplicate": "Double",
|
"Duplicate": "Double",
|
||||||
"daily-mail-quota": "Quota d'e-mails quotidien",
|
"daily-mail-quota": "Quota d'e-mails quotidien",
|
||||||
"Save as template": "Enregistrer comme modèle"
|
"Save as template": "Enregistrer comme modèle",
|
||||||
|
"Fix & resend": "Corriger et renvoyer",
|
||||||
|
"Kiosk Mode": "Mode Kiosque"
|
||||||
},
|
},
|
||||||
"report-help": {
|
"report-help": {
|
||||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||||
@@ -243,6 +248,7 @@
|
|||||||
"API": "API",
|
"API": "API",
|
||||||
"api-token": "Jeton API",
|
"api-token": "Jeton API",
|
||||||
"regenerate-token": "Régénérer en direct jeton",
|
"regenerate-token": "Régénérer en direct jeton",
|
||||||
|
"remove-background": "Supprimer l'arrière-plan",
|
||||||
"generate-token": "Générer en direct jeton",
|
"generate-token": "Générer en direct jeton",
|
||||||
"view-docs": "Afficher les documents",
|
"view-docs": "Afficher les documents",
|
||||||
"generate-token-alert": "Êtes-vous sûr de vouloir régénérer le jeton, votre ancien jeton sera supprimer?",
|
"generate-token-alert": "Êtes-vous sûr de vouloir régénérer le jeton, votre ancien jeton sera supprimer?",
|
||||||
@@ -303,7 +309,7 @@
|
|||||||
"send": "envoyer",
|
"send": "envoyer",
|
||||||
"quick-send-alert-1": "Tous les rôles dans ce document sont actuellement liés à des contacts. Pour envoyer rapidement des copies de ce modèle à plusieurs signataires, veuillez vous assurer qu'au moins un rôle n'est lié à aucun contact.",
|
"quick-send-alert-1": "Tous les rôles dans ce document sont actuellement liés à des contacts. Pour envoyer rapidement des copies de ce modèle à plusieurs signataires, veuillez vous assurer qu'au moins un rôle n'est lié à aucun contact.",
|
||||||
"quick-send-alert-2": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
|
"quick-send-alert-2": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
|
||||||
"quick-send-alert-3": "Veuillez ajouter au moins un rôle à ce modèle afin d'en « envoyer rapidement » des copies à plusieurs signataires.",
|
"quick-send-alert-3": "Veuillez ajouter au moins un rôle à ce template.",
|
||||||
"quick-send-alert-4": "L'envoi rapide a atteint la limite.",
|
"quick-send-alert-4": "L'envoi rapide a atteint la limite.",
|
||||||
"copy-link": "Copier le lien",
|
"copy-link": "Copier le lien",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
@@ -340,7 +346,7 @@
|
|||||||
"verify-email-1": "Vérifier l'e-mail",
|
"verify-email-1": "Vérifier l'e-mail",
|
||||||
"resend": "Renvoyer",
|
"resend": "Renvoyer",
|
||||||
"contact-details": "Coordonnées",
|
"contact-details": "Coordonnées",
|
||||||
"verify-email": "Veuillez vérifier votre email!",
|
"verify-email": "Veuillez vérifier votre e-mail pour continuer. C'est une étape unique pour confirmer l'adresse e-mail associée à votre compte OpenSign.",
|
||||||
"send-otp": "envoyer un code à usage unique",
|
"send-otp": "envoyer un code à usage unique",
|
||||||
"otp-placeholder": "Entrez le code de vérification reçu par e-mail",
|
"otp-placeholder": "Entrez le code de vérification reçu par e-mail",
|
||||||
"loading-doc": "Chargement du document..",
|
"loading-doc": "Chargement du document..",
|
||||||
@@ -475,6 +481,7 @@
|
|||||||
"mail-not-delivered": "Courrier non distribué",
|
"mail-not-delivered": "Courrier non distribué",
|
||||||
"document-alert": "Alerte document",
|
"document-alert": "Alerte document",
|
||||||
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
|
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
|
||||||
|
"owner-doesnt-have-paid-plan": "Le propriétaire n'a pas de plan payant.",
|
||||||
"subscription-expired": "Abonnement expiré",
|
"subscription-expired": "Abonnement expiré",
|
||||||
"alert-message": "Message d'alerte",
|
"alert-message": "Message d'alerte",
|
||||||
"document-decline": "Document-refusé",
|
"document-decline": "Document-refusé",
|
||||||
@@ -881,5 +888,97 @@
|
|||||||
"contact-billing-at-opensign": "Pour ajouter plus de places, veuillez contacter OpenSign™ à l'adresse <1>billing@opensignlabs.com</1> pour obtenir de l'aide.",
|
"contact-billing-at-opensign": "Pour ajouter plus de places, veuillez contacter OpenSign™ à l'adresse <1>billing@opensignlabs.com</1> pour obtenir de l'aide.",
|
||||||
"title-length-alert": "Le titre doit comporter au maximum 250 caractères.",
|
"title-length-alert": "Le titre doit comporter au maximum 250 caractères.",
|
||||||
"note-length-alert": "La note doit comporter au maximum 200 caractères.",
|
"note-length-alert": "La note doit comporter au maximum 200 caractères.",
|
||||||
"description-length-alert": "La description doit comporter au maximum 500 caractères"
|
"description-length-alert": "La description doit comporter au maximum 500 caractères",
|
||||||
|
"fix-&-resend-document": "Corriger et renvoyer le document",
|
||||||
|
"do-you-want-recreate-document?": "Cela créera un brouillon à partir de ce document avec tous les champs intacts. Êtes-vous sûr de vouloir recréer ce document ?",
|
||||||
|
"start-editing": "Commencer l'édition",
|
||||||
|
"unsaved-changes-discard-them?": "Vous avez des modifications non enregistrées. Les supprimer ?",
|
||||||
|
"yes-discard": "Oui, supprimer",
|
||||||
|
"LTV-enabled-signatures": "Signatures avec LTV activée",
|
||||||
|
"BETA": "BETA",
|
||||||
|
"two-factor-authentication": "Authentification à deux facteurs",
|
||||||
|
"2fa-help-text": "L'authentification à deux facteurs ajoute une couche de sécurité supplémentaire à votre compte en exigeant plus qu'un simple mot de passe pour se connecter.",
|
||||||
|
"2fa-help-bullet1": "Renforce la sécurité en demandant à la fois votre mot de passe et un code de vérification.",
|
||||||
|
"2fa-help-bullet2": "Le code de vérification est généré par une application d'authentification sur votre appareil.",
|
||||||
|
"2fa-help-bullet3": "Protège votre compte même si votre mot de passe est compromis.",
|
||||||
|
"setup-2fa": "Configurer 2FA",
|
||||||
|
"setup-2fa-again": "Reconfigurer 2FA",
|
||||||
|
"2fa-setup-intro": "Protégez votre compte avec l'authentification à deux facteurs. Une fois activée, vous devrez saisir un code provenant de votre application d'authentification à chaque connexion.",
|
||||||
|
"scan-qr-code": "Scanner le code QR",
|
||||||
|
"scan-qr-instructions": "Utilisez une application d'authentification comme Google Authenticator, Microsoft Authenticator ou Authy pour scanner ce code QR.",
|
||||||
|
"manual-setup-instructions": "Vous ne pouvez pas scanner le code ? Configurez manuellement votre application d'authentification avec cette clé secrète :",
|
||||||
|
"secret-key": "Clé secrète",
|
||||||
|
"copied-to-clipboard": "Copié dans le presse-papiers",
|
||||||
|
"copy-to-clipboard": "Copier dans le presse-papiers",
|
||||||
|
"recovery-codes": "Codes de récupération",
|
||||||
|
"recovery-codes-instructions": "Enregistrez ces codes de récupération dans un endroit sûr. Si vous perdez l'accès à votre application d'authentification, vous pourrez utiliser l'un de ces codes à usage unique pour vous connecter.",
|
||||||
|
"download-recovery-codes": "Télécharger les codes de récupération",
|
||||||
|
"verification-code": "Code de vérification",
|
||||||
|
"enter-code-from-authenticator-app": "Saisissez le code à 6 chiffres de votre application d'authentification",
|
||||||
|
"verification-code-required": "Le code de vérification est requis",
|
||||||
|
"verification-code-invalid": "Code de vérification invalide. Veuillez réessayer.",
|
||||||
|
"2fa-enabled": "2FA activé",
|
||||||
|
"2fa-enabled-successfully": "Authentification à deux facteurs activée avec succès",
|
||||||
|
"2fa-setup-complete": "Configuration terminée !",
|
||||||
|
"2fa-setup-complete-instructions": "Votre compte est maintenant protégé par l'authentification à deux facteurs. Vous devrez entrer un code de vérification à chaque connexion.",
|
||||||
|
"two-factor-verification": "Vérification en deux étapes",
|
||||||
|
"enter-verification-code-instructions": "Saisissez le code de vérification à 6 chiffres de votre application d'authentification pour continuer.",
|
||||||
|
"recovery-code": "Code de récupération",
|
||||||
|
"enter-recovery-code-help": "Saisissez l'un de vos codes de récupération",
|
||||||
|
"recovery-code-required": "Le code de récupération est requis",
|
||||||
|
"use-verification-code-instead": "Utiliser le code de vérification à la place",
|
||||||
|
"use-recovery-code-instead": "Utiliser le code de récupération à la place",
|
||||||
|
"regenerate-2fa-remove-existing": "Êtes-vous sûr de vouloir régénérer l'authentification à deux facteurs ? Cette action supprimera vos paramètres d'authentification existants.",
|
||||||
|
"use-passkey": "Se connecter avec une clé d'accès",
|
||||||
|
"security-section": "Sécurité",
|
||||||
|
"passkey-authentication": "Authentification par clé d'accès",
|
||||||
|
"passkey-not-supported": "Votre navigateur ou appareil ne prend pas en charge l'authentification par clé d'accès",
|
||||||
|
"passkey-description": "Les clés d'accès offrent une alternative plus sécurisée et résistante au phishing que les mots de passe. Vous pouvez utiliser votre empreinte digitale, reconnaissance faciale ou code PIN de l'appareil pour vous connecter en toute sécurité.",
|
||||||
|
"passkey-tooltip": "Les clés d'accès sont une alternative plus simple et plus sécurisée aux mots de passe. Elles utilisent des données biométriques comme les empreintes digitales ou la reconnaissance faciale déjà stockées sur votre appareil.",
|
||||||
|
"security-section-help": "Gérez les options de sécurité, y compris les clés d'accès et les méthodes d'authentification pour protéger votre compte.",
|
||||||
|
"passkey-register": "Enregistrer une clé d'accès",
|
||||||
|
"passkey-register-another": "Enregistrer une autre clé d'accès",
|
||||||
|
"passkey-registered": "Clé d'accès enregistrée",
|
||||||
|
"passkey-registering": "Enregistrement de la clé d'accès...",
|
||||||
|
"passkey-registered-success": "Clé d'accès enregistrée avec succès !",
|
||||||
|
"passkey-registration-failed": "Échec de l'enregistrement de la clé d'accès",
|
||||||
|
"passkey-auth-failed": "Échec de l'authentification avec la clé d'accès",
|
||||||
|
"passkey-missing-user-info": "Informations utilisateur requises",
|
||||||
|
"passkeys-list": "Vos clés d'accès",
|
||||||
|
"default-passkey": "Votre clé d'accès",
|
||||||
|
"passkey-rename": "Renommer",
|
||||||
|
"passkey-delete": "Supprimer",
|
||||||
|
"passkey-rename-title": "Renommer la clé d'accès",
|
||||||
|
"passkey-delete-title": "Supprimer la clé d'accès",
|
||||||
|
"passkey-delete-confirm": "Êtes-vous sûr de vouloir supprimer la clé d'accès « {{name}} » ?",
|
||||||
|
"passkey-name": "Nom de la clé d'accès",
|
||||||
|
"passkey-name-placeholder": "Entrez un nom descriptif pour cette clé d'accès",
|
||||||
|
"passkey-renamed-success": "Clé d'accès renommée avec succès",
|
||||||
|
"passkey-deleted-success": "Clé d'accès supprimée avec succès",
|
||||||
|
"passkey-rename-failed": "Échec du renommage de la clé d'accès",
|
||||||
|
"passkey-delete-failed": "Échec de la suppression de la clé d'accès",
|
||||||
|
"processing": "Traitement en cours...",
|
||||||
|
"today": "Aujourd'hui",
|
||||||
|
"yesterday": "Hier",
|
||||||
|
"days-ago": "Il y a {{days}} jours",
|
||||||
|
"verify-with-passkey": "Vérifier avec une passkey",
|
||||||
|
"verify-with-otp": "Vérifier avec OTP",
|
||||||
|
"verify-identity": "Vérifier l'identité",
|
||||||
|
"verify-account": "Vérifiez votre identité",
|
||||||
|
"verification": "Vérification",
|
||||||
|
"passkey-verification-failed": "La vérification par passkey a échoué",
|
||||||
|
"security-auth-help": {
|
||||||
|
"p1": "Gérez les paramètres de sécurité de votre compte pour protéger vos données. OpenSign prend en charge des méthodes d'authentification avancées pour renforcer la protection de votre compte.",
|
||||||
|
"2fa-auth-help": "Ajoutez une couche de sécurité supplémentaire en activant 2FA. Cela vous demandera de saisir un code de vérification provenant d’une application d’authentification après votre mot de passe.",
|
||||||
|
"passkey-auth-help": "Utilisez des passkeys pour une connexion sans mot de passe grâce à une vérification biométrique ou basée sur l’appareil, offrant à la fois une sécurité renforcée et une grande commodité."
|
||||||
|
},
|
||||||
|
"signer-already-present": "Signataire déjà présent",
|
||||||
|
"kiosk-sign": "Signature sur kiosque",
|
||||||
|
"dont-have-access-to-template": "Le template a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur.",
|
||||||
|
"kiosk-info": "Le Mode Kiosque vous permet de recueillir des signatures en personne rapidement et efficacement. Idéal pour les salons, événements ou situations où tous les signataires sont physiquement présents. ",
|
||||||
|
"learn-more": "En savoir plus",
|
||||||
|
"finish-mssg": "Êtes-vous sûr de vouloir terminer le document ?",
|
||||||
|
"review": "Revoir",
|
||||||
|
"next-field": "Champ suivant",
|
||||||
|
"required-mssg":"{{leftRequiredWidget}} champs sur {{totalWidget}} restants"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,10 @@
|
|||||||
"save": "Salva",
|
"save": "Salva",
|
||||||
"cancel": "Annulla",
|
"cancel": "Annulla",
|
||||||
"upgrade-now": "Aggiorna ora",
|
"upgrade-now": "Aggiorna ora",
|
||||||
|
"contact-now": "Contatta ora",
|
||||||
"upgrade-to": "Aggiorna a",
|
"upgrade-to": "Aggiorna a",
|
||||||
"plan": "Piano",
|
"plan": "Piano",
|
||||||
|
"subscription-renew-warning": "Il tuo abbonamento scadrà tra {{remainingDays}} giorni. Ti preghiamo di rinnovarlo.",
|
||||||
"subscribe-card-teamplan": "Sblocca tutto il potenziale della collaborazione! Crea organizzazioni, team e gerarchie illimitati. Condividi modelli senza problemi tra i team e assegna ruoli personalizzati agli utenti. Migliora il tuo flusso di lavoro oggi stesso!",
|
"subscribe-card-teamplan": "Sblocca tutto il potenziale della collaborazione! Crea organizzazioni, team e gerarchie illimitati. Condividi modelli senza problemi tra i team e assegna ruoli personalizzati agli utenti. Migliora il tuo flusso di lavoro oggi stesso!",
|
||||||
"subscribe-card-plan": "Sblocca le funzionalità premium a partire da soli {{premiumPrice}}/mese. Approfitta di prestazioni migliorate e paga solo {{addonPrice}} per ogni credito aggiuntivo dopo quelli inclusi.",
|
"subscribe-card-plan": "Sblocca le funzionalità premium a partire da soli {{premiumPrice}}/mese. Approfitta di prestazioni migliorate e paga solo {{addonPrice}} per ogni credito aggiuntivo dopo quelli inclusi.",
|
||||||
"user-name-limit-char": "Per un nome utente con meno di 8 caratteri, abbonati",
|
"user-name-limit-char": "Per un nome utente con meno di 8 caratteri, abbonati",
|
||||||
@@ -142,6 +144,7 @@
|
|||||||
"Quick send": "Invio rapido",
|
"Quick send": "Invio rapido",
|
||||||
"Edit": "Modifica",
|
"Edit": "Modifica",
|
||||||
"Share with team": "Condividi con il team",
|
"Share with team": "Condividi con il team",
|
||||||
|
"Share with user": "Condividi con un collega",
|
||||||
"Share": "Condividi",
|
"Share": "Condividi",
|
||||||
"View": "Visualizza",
|
"View": "Visualizza",
|
||||||
"option": "Opzione",
|
"option": "Opzione",
|
||||||
@@ -152,7 +155,9 @@
|
|||||||
"Duplicate Template": "Duplica modello",
|
"Duplicate Template": "Duplica modello",
|
||||||
"Duplicate": "Duplica",
|
"Duplicate": "Duplica",
|
||||||
"daily-mail-quota": "Quota e-mail giornaliera",
|
"daily-mail-quota": "Quota e-mail giornaliera",
|
||||||
"Save as template": "Salva come modello"
|
"Save as template": "Salva come modello",
|
||||||
|
"Fix & resend": "Correggi e reinvia",
|
||||||
|
"Kiosk Mode": "Modalità Kiosk"
|
||||||
},
|
},
|
||||||
"report-heading": {
|
"report-heading": {
|
||||||
"Sr.No": "Nr.",
|
"Sr.No": "Nr.",
|
||||||
@@ -243,6 +248,7 @@
|
|||||||
"API": "API",
|
"API": "API",
|
||||||
"api-token": "Token API",
|
"api-token": "Token API",
|
||||||
"regenerate-token": "Rigenera token live",
|
"regenerate-token": "Rigenera token live",
|
||||||
|
"remove-background": "Rimuovi sfondo",
|
||||||
"generate-token": "Genera token live",
|
"generate-token": "Genera token live",
|
||||||
"view-docs": "Visualizza documenti",
|
"view-docs": "Visualizza documenti",
|
||||||
"generate-token-alert": "Sei sicuro di voler rigenerare il token? Questo invaliderà il vecchio token.",
|
"generate-token-alert": "Sei sicuro di voler rigenerare il token? Questo invaliderà il vecchio token.",
|
||||||
@@ -303,7 +309,7 @@
|
|||||||
"send": "Invia",
|
"send": "Invia",
|
||||||
"quick-send-alert-1": "Tutti i ruoli in questo documento sono attualmente collegati a contatti. Per inviare rapidamente copie di questo modello a più firmatari, assicurati che almeno un ruolo non sia collegato a nessun contatto.",
|
"quick-send-alert-1": "Tutti i ruoli in questo documento sono attualmente collegati a contatti. Per inviare rapidamente copie di questo modello a più firmatari, assicurati che almeno un ruolo non sia collegato a nessun contatto.",
|
||||||
"quick-send-alert-2": "Assicurati che ci sia almeno un widget firma aggiunto per tutti i destinatari.",
|
"quick-send-alert-2": "Assicurati che ci sia almeno un widget firma aggiunto per tutti i destinatari.",
|
||||||
"quick-send-alert-3": "Aggiungi almeno un ruolo a questo modello per 'invio rapido' a più firmatari.",
|
"quick-send-alert-3": "Si prega di aggiungere almeno un ruolo a questo template.",
|
||||||
"quick-send-alert-4": "Limite di invio rapido raggiunto.",
|
"quick-send-alert-4": "Limite di invio rapido raggiunto.",
|
||||||
"copy-link": "Copia link",
|
"copy-link": "Copia link",
|
||||||
"copy": "Copia",
|
"copy": "Copia",
|
||||||
@@ -340,7 +346,7 @@
|
|||||||
"verify-email-1": "Verifica email",
|
"verify-email-1": "Verifica email",
|
||||||
"resend": "Reinvia",
|
"resend": "Reinvia",
|
||||||
"contact-details": "Dettagli contatto",
|
"contact-details": "Dettagli contatto",
|
||||||
"verify-email": "Verifica la tua email!",
|
"verify-email": "Si prega di verificare l'e-mail per continuare. Questo è un passaggio unico per confermare l'e-mail associata al tuo account OpenSign.",
|
||||||
"send-otp": "Invia OTP",
|
"send-otp": "Invia OTP",
|
||||||
"otp-placeholder": "Inserisci il codice di verifica ricevuto via email",
|
"otp-placeholder": "Inserisci il codice di verifica ricevuto via email",
|
||||||
"loading-doc": "Caricamento del documento...",
|
"loading-doc": "Caricamento del documento...",
|
||||||
@@ -475,6 +481,7 @@
|
|||||||
"mail-not-delivered": "Mail non consegnata",
|
"mail-not-delivered": "Mail non consegnata",
|
||||||
"document-alert": "Avviso Documento",
|
"document-alert": "Avviso Documento",
|
||||||
"owner-subscription-expired": "L'abbonamento del proprietario è scaduto.",
|
"owner-subscription-expired": "L'abbonamento del proprietario è scaduto.",
|
||||||
|
"owner-doesnt-have-paid-plan": "Il proprietario non ha un piano a pagamento.",
|
||||||
"subscription-expired": "Abbonamento Scaduto",
|
"subscription-expired": "Abbonamento Scaduto",
|
||||||
"alert-message": "Messaggio di avviso",
|
"alert-message": "Messaggio di avviso",
|
||||||
"document-decline": "Documento rifiutato",
|
"document-decline": "Documento rifiutato",
|
||||||
@@ -881,5 +888,97 @@
|
|||||||
"contact-billing-at-opensign": " Per aggiungere altri posti, contattare OpenSign™ all'indirizzo <1>billing@opensignlabs.com</1> per assistenza.",
|
"contact-billing-at-opensign": " Per aggiungere altri posti, contattare OpenSign™ all'indirizzo <1>billing@opensignlabs.com</1> per assistenza.",
|
||||||
"title-length-alert": "Il titolo può contenere al massimo 250 caratteri.",
|
"title-length-alert": "Il titolo può contenere al massimo 250 caratteri.",
|
||||||
"note-length-alert": "La nota può contenere al massimo 200 caratteri.",
|
"note-length-alert": "La nota può contenere al massimo 200 caratteri.",
|
||||||
"description-length-alert": " La descrizione può contenere al massimo 500 caratteri."
|
"description-length-alert": " La descrizione può contenere al massimo 500 caratteri.",
|
||||||
|
"fix-&-resend-document": "Correggi e reinvia il documento",
|
||||||
|
"do-you-want-recreate-document?": "Questo creerà una bozza da questo documento con tutti i campi intatti. Sei sicuro di voler ricreare questo documento?",
|
||||||
|
"start-editing": "Inizia a modificare",
|
||||||
|
"unsaved-changes-discard-them?": "Hai modifiche non salvate. Vuoi scartarle?",
|
||||||
|
"yes-discard": "Sì, scarta",
|
||||||
|
"LTV-enabled-signatures": "Firme con LTV abilitato",
|
||||||
|
"BETA": "BETA",
|
||||||
|
"two-factor-authentication": "Autenticazione a due fattori",
|
||||||
|
"2fa-help-text": "L'autenticazione a due fattori aggiunge un ulteriore livello di sicurezza al tuo account richiedendo più di una semplice password per accedere.",
|
||||||
|
"2fa-help-bullet1": "Aumenta la sicurezza richiedendo sia la password che un codice di verifica.",
|
||||||
|
"2fa-help-bullet2": "Il codice di verifica viene generato da un'app di autenticazione sul tuo dispositivo.",
|
||||||
|
"2fa-help-bullet3": "Protegge il tuo account anche se la password è stata compromessa.",
|
||||||
|
"setup-2fa": "Configura 2FA",
|
||||||
|
"setup-2fa-again": "Configura nuovamente 2FA",
|
||||||
|
"2fa-setup-intro": "Proteggi il tuo account con l'autenticazione a due fattori. Quando è attiva, dovrai inserire un codice dall'app di autenticazione ogni volta che accedi.",
|
||||||
|
"scan-qr-code": "Scansiona il codice QR",
|
||||||
|
"scan-qr-instructions": "Usa un'app di autenticazione come Google Authenticator, Microsoft Authenticator o Authy per scansionare questo codice QR.",
|
||||||
|
"manual-setup-instructions": "Non riesci a scansionare il codice? Puoi configurare manualmente l'app usando questa chiave segreta:",
|
||||||
|
"secret-key": "Chiave segreta",
|
||||||
|
"copied-to-clipboard": "Copiato negli appunti",
|
||||||
|
"copy-to-clipboard": "Copia negli appunti",
|
||||||
|
"recovery-codes": "Codici di recupero",
|
||||||
|
"recovery-codes-instructions": "Salva questi codici di recupero in un luogo sicuro. Se perdi l'accesso all'app di autenticazione, puoi usarne uno per accedere.",
|
||||||
|
"download-recovery-codes": "Scarica i codici di recupero",
|
||||||
|
"verification-code": "Codice di verifica",
|
||||||
|
"enter-code-from-authenticator-app": "Inserisci il codice a 6 cifre dalla tua app di autenticazione",
|
||||||
|
"verification-code-required": "È richiesto il codice di verifica",
|
||||||
|
"verification-code-invalid": "Codice di verifica non valido. Riprova.",
|
||||||
|
"2fa-enabled": "2FA abilitato",
|
||||||
|
"2fa-enabled-successfully": "Autenticazione a due fattori attivata con successo",
|
||||||
|
"2fa-setup-complete": "Configurazione completata!",
|
||||||
|
"2fa-setup-complete-instructions": "Il tuo account è ora protetto da autenticazione a due fattori. Dovrai inserire un codice ogni volta che accedi.",
|
||||||
|
"two-factor-verification": "Verifica a due fattori",
|
||||||
|
"enter-verification-code-instructions": "Inserisci il codice di verifica a 6 cifre dalla tua app di autenticazione per continuare.",
|
||||||
|
"recovery-code": "Codice di recupero",
|
||||||
|
"enter-recovery-code-help": "Inserisci uno dei tuoi codici di recupero",
|
||||||
|
"recovery-code-required": "È richiesto il codice di recupero",
|
||||||
|
"use-verification-code-instead": "Usa il codice di verifica invece",
|
||||||
|
"use-recovery-code-instead": "Usa il codice di recupero invece",
|
||||||
|
"regenerate-2fa-remove-existing": "Sei sicuro di voler rigenerare l'autenticazione a due fattori? Questa azione rimuoverà le impostazioni di autenticazione esistenti.",
|
||||||
|
"use-passkey": "Accedi con passkey",
|
||||||
|
"security-section": "Sicurezza",
|
||||||
|
"passkey-authentication": "Autenticazione passkey",
|
||||||
|
"passkey-not-supported": "Il tuo browser o dispositivo non supporta l'autenticazione passkey",
|
||||||
|
"passkey-description": "Le passkey offrono un’alternativa più sicura e resistente al phishing rispetto alle password. Puoi usare l’impronta digitale, il riconoscimento facciale o il PIN del dispositivo per accedere in sicurezza.",
|
||||||
|
"passkey-tooltip": "Le passkey sono un’alternativa più semplice e sicura alle password. Usano dati biometrici come impronte digitali o riconoscimento facciale già presenti sul tuo dispositivo.",
|
||||||
|
"security-section-help": "Gestisci le opzioni di sicurezza, incluse passkey e metodi di autenticazione, per proteggere il tuo account.",
|
||||||
|
"passkey-register": "Registra passkey",
|
||||||
|
"passkey-register-another": "Registra un'altra passkey",
|
||||||
|
"passkey-registered": "Passkey registrata",
|
||||||
|
"passkey-registering": "Registrazione passkey in corso...",
|
||||||
|
"passkey-registered-success": "Passkey registrata con successo!",
|
||||||
|
"passkey-registration-failed": "Registrazione della passkey non riuscita",
|
||||||
|
"passkey-auth-failed": "Autenticazione passkey fallita",
|
||||||
|
"passkey-missing-user-info": "Informazioni utente richieste",
|
||||||
|
"passkeys-list": "Le tue passkey",
|
||||||
|
"default-passkey": "La tua passkey",
|
||||||
|
"passkey-rename": "Rinomina",
|
||||||
|
"passkey-delete": "Elimina",
|
||||||
|
"passkey-rename-title": "Rinomina passkey",
|
||||||
|
"passkey-delete-title": "Elimina passkey",
|
||||||
|
"passkey-delete-confirm": "Sei sicuro di voler eliminare la passkey \"{{name}}\"?",
|
||||||
|
"passkey-name": "Nome della passkey",
|
||||||
|
"passkey-name-placeholder": "Inserisci un nome descrittivo per questa passkey",
|
||||||
|
"passkey-renamed-success": "Passkey rinominata con successo",
|
||||||
|
"passkey-deleted-success": "Passkey eliminata con successo",
|
||||||
|
"passkey-rename-failed": "Impossibile rinominare la passkey",
|
||||||
|
"passkey-delete-failed": "Impossibile eliminare la passkey",
|
||||||
|
"processing": "Elaborazione...",
|
||||||
|
"today": "Oggi",
|
||||||
|
"yesterday": "Ieri",
|
||||||
|
"days-ago": "{{days}} giorni fa",
|
||||||
|
"verify-with-passkey": "Verifica con passkey",
|
||||||
|
"verify-with-otp": "Verifica con OTP",
|
||||||
|
"verify-identity": "Verifica identità",
|
||||||
|
"verify-account": "Verifica la tua identità",
|
||||||
|
"verification": "Verifica",
|
||||||
|
"passkey-verification-failed": "Verifica con passkey fallita",
|
||||||
|
"security-auth-help": {
|
||||||
|
"p1": "Gestisci le impostazioni di sicurezza del tuo account per proteggere i tuoi dati. OpenSign supporta metodi di autenticazione avanzati per migliorare la protezione dell’account.",
|
||||||
|
"2fa-auth-help": "Aggiungi un ulteriore livello di sicurezza abilitando 2FA. Ti verrà richiesto di inserire un codice di verifica da un'app di autenticazione dopo la password.",
|
||||||
|
"passkey-auth-help": "Usa le passkey per accedere senza password con verifica biometrica o basata sul dispositivo, garantendo sicurezza elevata e praticità."
|
||||||
|
},
|
||||||
|
"signer-already-present": "Firmatario già presente",
|
||||||
|
"kiosk-sign": "Firma su chiosco",
|
||||||
|
"dont-have-access-to-template": "Il template è stato eliminato o non hai accesso. Si prega di contattare il mittente.",
|
||||||
|
"kiosk-info": "La Modalità Kiosk consente di raccogliere firme in presenza in modo rapido ed efficiente. Ideale per fiere, eventi o situazioni con firmatari fisicamente presenti. ",
|
||||||
|
"learn-more": "Scopri di più",
|
||||||
|
"finish-mssg": "Sei sicuro di voler completare il documento?",
|
||||||
|
"review": "Rivedere",
|
||||||
|
"next-field": "Campo successivo",
|
||||||
|
"required-mssg":"{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
import "@testing-library/jest-dom";
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useState, useEffect, lazy } from "react";
|
import React, { useState, useEffect, lazy } from "react";
|
||||||
import { Routes, Route, BrowserRouter } from "react-router";
|
import { Routes, Route, BrowserRouter } from "react-router";
|
||||||
import { pdfjs } from "react-pdf";
|
import { pdfjs } from "react-pdf";
|
||||||
import Login from "./pages/Login";
|
|
||||||
import Form from "./pages/Form";
|
import Form from "./pages/Form";
|
||||||
import Report from "./pages/Report";
|
import Report from "./pages/Report";
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Dashboard from "./pages/Dashboard";
|
||||||
@@ -30,6 +29,7 @@ const ManageSign = lazy(() => import("./pages/Managesign"));
|
|||||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||||
const Preferences = lazy(() => import("./pages/Preferences"));
|
const Preferences = lazy(() => import("./pages/Preferences"));
|
||||||
|
const Login = lazy(() => import("./pages/Login"));
|
||||||
|
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||||
const AppLoader = () => {
|
const AppLoader = () => {
|
||||||
@@ -67,7 +67,7 @@ function App() {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<ValidateRoute />}>
|
<Route element={<ValidateRoute />}>
|
||||||
<Route exact path="/" element={<Login />} />
|
<Route exact path="/" element={<LazyPage Page={Login} />} />
|
||||||
<Route
|
<Route
|
||||||
path="/addadmin"
|
path="/addadmin"
|
||||||
element={<LazyPage Page={AddAdmin} />}
|
element={<LazyPage Page={AddAdmin} />}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
+1
-1
@@ -24,7 +24,7 @@ const AddSigner = (props) => {
|
|||||||
if (savedUserDetails && addYourself) {
|
if (savedUserDetails && addYourself) {
|
||||||
setName(savedUserDetails.name);
|
setName(savedUserDetails.name);
|
||||||
setPhone(savedUserDetails?.phone || "");
|
setPhone(savedUserDetails?.phone || "");
|
||||||
setEmail(savedUserDetails.email);
|
setEmail(savedUserDetails.email?.toLowerCase()?.replace(/\s/g, ""));
|
||||||
}
|
}
|
||||||
}, [addYourself]);
|
}, [addYourself]);
|
||||||
|
|
||||||
@@ -1,385 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import Parse from "parse";
|
|
||||||
import Title from "./Title";
|
|
||||||
import Loader from "../primitives/Loader";
|
|
||||||
import { copytoData, usertimezone } from "../constant/Utils";
|
|
||||||
import { emailRegex } from "../constant/const";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
function generatePassword(length) {
|
|
||||||
const characters =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
let result = "";
|
|
||||||
const charactersLength = characters.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddUser = (props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [formdata, setFormdata] = useState({
|
|
||||||
name: "",
|
|
||||||
phone: "",
|
|
||||||
email: "",
|
|
||||||
team: "",
|
|
||||||
password: "",
|
|
||||||
role: ""
|
|
||||||
});
|
|
||||||
const [isFormLoader, setIsFormLoader] = useState(false);
|
|
||||||
const [teamList, setTeamList] = useState([]);
|
|
||||||
const role = ["OrgAdmin", "Editor", "User"];
|
|
||||||
useEffect(() => {
|
|
||||||
getTeamList();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getTeamList = async () => {
|
|
||||||
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
|
|
||||||
const teamRes = await Parse.Cloud.run("getteams", { active: true });
|
|
||||||
if (teamRes.length > 0) {
|
|
||||||
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
|
||||||
setTeamList(_teamRes);
|
|
||||||
const allUserId =
|
|
||||||
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
|
||||||
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const checkUserExist = async () => {
|
|
||||||
try {
|
|
||||||
const res = await Parse.Cloud.run("getUserDetails", {
|
|
||||||
email: formdata.email
|
|
||||||
});
|
|
||||||
if (res) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err", err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Define a function to handle form submission
|
|
||||||
const handleSubmit = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (!emailRegex.test(formdata.email)) {
|
|
||||||
alert("Please enter a valid email address.");
|
|
||||||
} else {
|
|
||||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
|
||||||
setIsFormLoader(true);
|
|
||||||
const res = await checkUserExist();
|
|
||||||
if (res) {
|
|
||||||
props.showAlert("danger", t("user-already-exist"));
|
|
||||||
setIsFormLoader(false);
|
|
||||||
} else {
|
|
||||||
if (localStorage.getItem("TenantId")) {
|
|
||||||
try {
|
|
||||||
const extUser = new Parse.Object("contracts_Users");
|
|
||||||
extUser.set("Name", formdata.name);
|
|
||||||
if (formdata.phone) {
|
|
||||||
extUser.set("Phone", formdata.phone);
|
|
||||||
}
|
|
||||||
extUser.set("Email", formdata.email);
|
|
||||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
|
||||||
if (formdata?.team) {
|
|
||||||
extUser.set("TeamIds", [
|
|
||||||
{
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "contracts_Teams",
|
|
||||||
objectId: formdata.team
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (localUser && localUser.OrganizationId) {
|
|
||||||
extUser.set("OrganizationId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "contracts_Organizations",
|
|
||||||
objectId: localUser.OrganizationId.objectId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (localUser && localUser.Company) {
|
|
||||||
extUser.set("Company", localUser.Company);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localStorage.getItem("TenantId")) {
|
|
||||||
extUser.set("TenantId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "partners_Tenant",
|
|
||||||
objectId: localStorage.getItem("TenantId")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const timezone = usertimezone;
|
|
||||||
if (timezone) {
|
|
||||||
extUser.set("Timezone", timezone);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const _users = Parse.Object.extend("User");
|
|
||||||
const _user = new _users();
|
|
||||||
_user.set("name", formdata.name);
|
|
||||||
_user.set("username", formdata.email);
|
|
||||||
_user.set("email", formdata.email);
|
|
||||||
_user.set("password", formdata.password);
|
|
||||||
if (formdata.phone) {
|
|
||||||
_user.set("phone", formdata.phone);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await _user.save();
|
|
||||||
if (user) {
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
extUser.set(
|
|
||||||
"CreatedBy",
|
|
||||||
Parse.User.createWithoutData(currentUser.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
extUser.set("UserId", user);
|
|
||||||
const acl = new Parse.ACL();
|
|
||||||
acl.setPublicReadAccess(true);
|
|
||||||
acl.setPublicWriteAccess(true);
|
|
||||||
acl.setReadAccess(currentUser.id, true);
|
|
||||||
acl.setWriteAccess(currentUser.id, true);
|
|
||||||
|
|
||||||
extUser.setACL(acl);
|
|
||||||
|
|
||||||
const res = await extUser.save();
|
|
||||||
|
|
||||||
const parseData = JSON.parse(JSON.stringify(res));
|
|
||||||
|
|
||||||
if (props.closePopup) {
|
|
||||||
props.closePopup();
|
|
||||||
}
|
|
||||||
if (props.handleUserData) {
|
|
||||||
if (formdata?.team) {
|
|
||||||
const team = teamList.find(
|
|
||||||
(x) => x.objectId === formdata.team
|
|
||||||
);
|
|
||||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
|
||||||
y.objectId === team.objectId ? team : y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
props.handleUserData(parseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsFormLoader(false);
|
|
||||||
setFormdata({
|
|
||||||
name: "",
|
|
||||||
email: "",
|
|
||||||
phone: "",
|
|
||||||
team: "",
|
|
||||||
role: ""
|
|
||||||
});
|
|
||||||
props.showAlert("success", t("user-created-successfully"));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err ", err);
|
|
||||||
if (err.code === 202) {
|
|
||||||
const params = { email: formdata.email };
|
|
||||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
extUser.set(
|
|
||||||
"CreatedBy",
|
|
||||||
Parse.User.createWithoutData(currentUser.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
extUser.set("UserId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "_User",
|
|
||||||
objectId: userRes.id
|
|
||||||
});
|
|
||||||
const acl = new Parse.ACL();
|
|
||||||
acl.setPublicReadAccess(true);
|
|
||||||
acl.setPublicWriteAccess(true);
|
|
||||||
acl.setReadAccess(currentUser.id, true);
|
|
||||||
acl.setWriteAccess(currentUser.id, true);
|
|
||||||
|
|
||||||
extUser.setACL(acl);
|
|
||||||
const res = await extUser.save();
|
|
||||||
|
|
||||||
const parseData = JSON.parse(JSON.stringify(res));
|
|
||||||
if (props.closePopup) {
|
|
||||||
props.closePopup();
|
|
||||||
}
|
|
||||||
if (props.handleUserData) {
|
|
||||||
if (formdata?.team) {
|
|
||||||
const team = teamList.find(
|
|
||||||
(x) => x.objectId === formdata.team
|
|
||||||
);
|
|
||||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
|
||||||
y.objectId === team.objectId ? team : y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
props.handleUserData(parseData);
|
|
||||||
}
|
|
||||||
setIsFormLoader(false);
|
|
||||||
setFormdata({
|
|
||||||
name: "",
|
|
||||||
email: "",
|
|
||||||
phone: "",
|
|
||||||
team: "",
|
|
||||||
role: ""
|
|
||||||
});
|
|
||||||
props.showAlert("success", t("user-created-successfully"));
|
|
||||||
} else {
|
|
||||||
setIsFormLoader(false);
|
|
||||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err", err);
|
|
||||||
setIsFormLoader(false);
|
|
||||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define a function to handle the "add yourself" checkbox
|
|
||||||
const handleReset = () => {
|
|
||||||
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
|
||||||
if (props.closePopup) {
|
|
||||||
props.closePopup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleChange = (event) => {
|
|
||||||
let { name, value } = event.target;
|
|
||||||
if (name === "email") {
|
|
||||||
value = value?.toLowerCase()?.replace(/\s/g, "");
|
|
||||||
}
|
|
||||||
setFormdata((prev) => ({ ...prev, [name]: value }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const copytoclipboard = (text) => {
|
|
||||||
copytoData(text);
|
|
||||||
props.showAlert("success", t("copied"));
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
|
|
||||||
<Title title={t("add-user")} />
|
|
||||||
{isFormLoader && (
|
|
||||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="w-full mx-auto">
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label
|
|
||||||
htmlFor="name"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
{t("name")}
|
|
||||||
<span className="text-[red] text-[13px]"> *</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
value={formdata.name}
|
|
||||||
onChange={(e) => handleChange(e)}
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
required
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label
|
|
||||||
htmlFor="email"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
{t("email")}
|
|
||||||
<span className="text-[red] text-[13px]"> *</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
value={formdata.email}
|
|
||||||
onChange={(e) => handleChange(e)}
|
|
||||||
required
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label className="block text-xs text-gray-700 font-semibold">
|
|
||||||
{t("password")}
|
|
||||||
</label>
|
|
||||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
|
||||||
<div className="break-all">{formdata?.password}</div>
|
|
||||||
<i
|
|
||||||
onClick={() => copytoclipboard(formdata?.password)}
|
|
||||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
|
||||||
></i>
|
|
||||||
</div>
|
|
||||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
|
||||||
{t("password-generateed")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label
|
|
||||||
htmlFor="phone"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
{t("phone")}
|
|
||||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="phone"
|
|
||||||
placeholder={t("phone-optional")}
|
|
||||||
value={formdata.phone}
|
|
||||||
onChange={(e) => handleChange(e)}
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-3">
|
|
||||||
<label
|
|
||||||
htmlFor="phone"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
{t("Role")}
|
|
||||||
<span className="text-[red] text-[13px]"> *</span>
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={formdata.role}
|
|
||||||
onChange={(e) => handleChange(e)}
|
|
||||||
name="role"
|
|
||||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option defaultValue={""} value={""}>
|
|
||||||
{t("Select")}
|
|
||||||
</option>
|
|
||||||
{role.length > 0 &&
|
|
||||||
role.map((x) => (
|
|
||||||
<option key={x} value={x}>
|
|
||||||
{x}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center mt-3 gap-2 text-white">
|
|
||||||
<button type="submit" className="op-btn op-btn-primary">
|
|
||||||
{t("submit")}
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleReset()}
|
|
||||||
className="op-btn op-btn-secondary"
|
|
||||||
>
|
|
||||||
{t("cancel")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddUser;
|
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import Title from "./Title";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
import {
|
||||||
|
copytoData,
|
||||||
|
usertimezone
|
||||||
|
} from "../constant/Utils";
|
||||||
|
import {
|
||||||
|
emailRegex,
|
||||||
|
} from "../constant/const";
|
||||||
|
import {
|
||||||
|
useTranslation
|
||||||
|
} from "react-i18next";
|
||||||
|
function generatePassword(length) {
|
||||||
|
const characters =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let result = "";
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddUser = (props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [formdata, setFormdata] = useState({
|
||||||
|
name: "",
|
||||||
|
phone: "",
|
||||||
|
email: "",
|
||||||
|
team: "",
|
||||||
|
password: "",
|
||||||
|
role: ""
|
||||||
|
});
|
||||||
|
const [isFormLoader, setIsFormLoader] = useState(false);
|
||||||
|
const [teamList, setTeamList] = useState([]);
|
||||||
|
const role = ["OrgAdmin", "Editor", "User"];
|
||||||
|
useEffect(() => {
|
||||||
|
getTeamList();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getTeamList = async () => {
|
||||||
|
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
|
||||||
|
const teamRes = await Parse.Cloud.run("getteams", { active: true });
|
||||||
|
if (teamRes.length > 0) {
|
||||||
|
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
||||||
|
setTeamList(_teamRes);
|
||||||
|
const allUserId =
|
||||||
|
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
||||||
|
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const checkUserExist = async () => {
|
||||||
|
try {
|
||||||
|
const res = await Parse.Cloud.run("getUserDetails", {
|
||||||
|
email: formdata.email
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Define a function to handle form submission
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!emailRegex.test(formdata.email)) {
|
||||||
|
alert("Please enter a valid email address.");
|
||||||
|
} else {
|
||||||
|
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||||
|
setIsFormLoader(true);
|
||||||
|
const res = await checkUserExist();
|
||||||
|
if (res) {
|
||||||
|
props.showAlert("danger", t("user-already-exist"));
|
||||||
|
setIsFormLoader(false);
|
||||||
|
} else {
|
||||||
|
if (localStorage.getItem("TenantId")) {
|
||||||
|
const timezone = usertimezone;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
name: formdata.name,
|
||||||
|
email: formdata.email,
|
||||||
|
phone: formdata.phone,
|
||||||
|
password: formdata.password,
|
||||||
|
role: formdata.role,
|
||||||
|
team: formdata.team,
|
||||||
|
timezone: timezone,
|
||||||
|
tenantId: localStorage.getItem("TenantId"),
|
||||||
|
organization: {
|
||||||
|
objectId: localUser?.OrganizationId?.objectId,
|
||||||
|
company: localUser?.Company
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const res = await Parse.Cloud.run("adduser", params);
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
console.log("parseData ", parseData);
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
if (formdata?.team) {
|
||||||
|
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||||
|
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||||
|
y.objectId === team.objectId ? team : y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
setIsFormLoader(false);
|
||||||
|
setFormdata({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
team: "",
|
||||||
|
role: ""
|
||||||
|
});
|
||||||
|
props.showAlert("success", t("user-created-successfully"));
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err", err);
|
||||||
|
setIsFormLoader(false);
|
||||||
|
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define a function to handle the "add yourself" checkbox
|
||||||
|
const handleReset = () => {
|
||||||
|
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleChange = (event) => {
|
||||||
|
let { name, value } = event.target;
|
||||||
|
if (name === "email") {
|
||||||
|
value = value?.toLowerCase()?.replace(/\s/g, "");
|
||||||
|
}
|
||||||
|
setFormdata((prev) => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const copytoclipboard = (text) => {
|
||||||
|
copytoData(text);
|
||||||
|
props.showAlert("success", t("copied"));
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
|
||||||
|
<Title title={t("add-user")} />
|
||||||
|
{isFormLoader && (
|
||||||
|
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full mx-auto">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="name"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={formdata.name}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
{t("email")}
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={formdata.email}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
required
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label className="block text-xs text-gray-700 font-semibold">
|
||||||
|
{t("password")}
|
||||||
|
</label>
|
||||||
|
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||||
|
<div className="break-all">{formdata?.password}</div>
|
||||||
|
<i
|
||||||
|
onClick={() => copytoclipboard(formdata?.password)}
|
||||||
|
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||||
|
{t("password-generateed")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
{t("phone")}
|
||||||
|
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="phone"
|
||||||
|
placeholder={t("phone-optional")}
|
||||||
|
value={formdata.phone}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
{t("Role")}
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={formdata.role}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
name="role"
|
||||||
|
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option defaultValue={""} value={""}>
|
||||||
|
{t("Select")}
|
||||||
|
</option>
|
||||||
|
{role.length > 0 &&
|
||||||
|
role.map((x) => (
|
||||||
|
<option key={x} value={x}>
|
||||||
|
{x}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center mt-3 gap-2 text-white">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
{t("submit")}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleReset()}
|
||||||
|
className="op-btn op-btn-secondary"
|
||||||
|
>
|
||||||
|
{t("cancel")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddUser;
|
||||||
+80
-77
@@ -3,7 +3,9 @@ import axios from "axios";
|
|||||||
import SuggestionInput from "./shared/fields/SuggestionInput";
|
import SuggestionInput from "./shared/fields/SuggestionInput";
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { emailRegex } from "../constant/const";
|
import {
|
||||||
|
emailRegex,
|
||||||
|
} from "../constant/const";
|
||||||
const BulkSendUi = (props) => {
|
const BulkSendUi = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [forms, setForms] = useState([]);
|
const [forms, setForms] = useState([]);
|
||||||
@@ -20,15 +22,15 @@ const BulkSendUi = (props) => {
|
|||||||
|
|
||||||
//function to check atleast one signature field exist
|
//function to check atleast one signature field exist
|
||||||
const signatureExist = async () => {
|
const signatureExist = async () => {
|
||||||
setIsDisableBulkSend(false);
|
setIsDisableBulkSend(false);
|
||||||
const getPlaceholder = props?.Placeholders;
|
const getPlaceholder = props?.Placeholders;
|
||||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||||
placeholderObj?.placeHolder?.some((holder) =>
|
placeholderObj?.placeHolder?.some((holder) =>
|
||||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
setIsSignatureExist(checkIsSignatureExistt);
|
setIsSignatureExist(checkIsSignatureExistt);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollOnNextUpdate && formRef.current) {
|
if (scrollOnNextUpdate && formRef.current) {
|
||||||
@@ -72,6 +74,7 @@ const BulkSendUi = (props) => {
|
|||||||
setForms(newForms);
|
setForms(newForms);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleRemoveForm = (index) => {
|
const handleRemoveForm = (index) => {
|
||||||
const updatedForms = forms.filter((_, i) => i !== index);
|
const updatedForms = forms.filter((_, i) => i !== index);
|
||||||
setForms(updatedForms);
|
setForms(updatedForms);
|
||||||
@@ -92,83 +95,82 @@ const BulkSendUi = (props) => {
|
|||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsSubmit(true);
|
setIsSubmit(true);
|
||||||
if (validateEmails(forms)) {
|
if (validateEmails(forms)) {
|
||||||
// Create a copy of Placeholders array from props.item
|
// Create a copy of Placeholders array from props.item
|
||||||
let Placeholders = [...props.Placeholders];
|
let Placeholders = [...props.Placeholders];
|
||||||
// Initialize an empty array to store updated documents
|
// Initialize an empty array to store updated documents
|
||||||
let Documents = [];
|
let Documents = [];
|
||||||
// Loop through each form
|
// Loop through each form
|
||||||
forms.forEach((form) => {
|
forms.forEach((form) => {
|
||||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||||
let existSigner = [];
|
let existSigner = [];
|
||||||
form.fields.map((data) => {
|
form.fields.map((data) => {
|
||||||
if (data.signer) {
|
if (data.signer) {
|
||||||
existSigner.push(data.signer);
|
existSigner.push(data.signer);
|
||||||
}
|
|
||||||
});
|
|
||||||
// Map through the copied Placeholders array to update email values
|
|
||||||
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
|
||||||
// Find the field in the current form that matches the placeholder Id
|
|
||||||
const field = form.fields.find(
|
|
||||||
(element) => parseInt(element.fieldId) === placeholder.Id
|
|
||||||
);
|
|
||||||
// If a matching field is found, update the email value in the placeholder
|
|
||||||
const signer = field?.signer?.objectId ? field.signer : "";
|
|
||||||
if (field) {
|
|
||||||
if (signer) {
|
|
||||||
return {
|
|
||||||
...placeholder,
|
|
||||||
signerObjId: field?.signer?.objectId || "",
|
|
||||||
signerPtr: signer
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
...placeholder,
|
|
||||||
email: field.email,
|
|
||||||
signerObjId: field?.signer?.objectId || "",
|
|
||||||
signerPtr: signer
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
// If no matching field is found, keep the placeholder as is
|
// Map through the copied Placeholders array to update email values
|
||||||
return placeholder;
|
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||||
});
|
// Find the field in the current form that matches the placeholder Id
|
||||||
|
const field = form.fields.find(
|
||||||
|
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||||
|
);
|
||||||
|
// If a matching field is found, update the email value in the placeholder
|
||||||
|
const signer = field?.signer?.objectId ? field.signer : "";
|
||||||
|
if (field) {
|
||||||
|
if (signer) {
|
||||||
|
return {
|
||||||
|
...placeholder,
|
||||||
|
signerObjId: field?.signer?.objectId || "",
|
||||||
|
signerPtr: signer
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...placeholder,
|
||||||
|
email: field.email,
|
||||||
|
signerObjId: field?.signer?.objectId || "",
|
||||||
|
signerPtr: signer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no matching field is found, keep the placeholder as is
|
||||||
|
return placeholder;
|
||||||
|
});
|
||||||
|
|
||||||
// Push a new document object with updated Placeholders into the Documents array
|
// Push a new document object with updated Placeholders into the Documents array
|
||||||
if (existSigner?.length > 0) {
|
if (existSigner?.length > 0) {
|
||||||
Documents.push({
|
Documents.push({
|
||||||
...props.item,
|
...props.item,
|
||||||
Placeholders: updatedPlaceholders,
|
Placeholders: updatedPlaceholders,
|
||||||
Signers: props.item.Signers
|
Signers: props.item.Signers
|
||||||
? [...props.item.Signers, ...existSigner]
|
? [...props.item.Signers, ...existSigner]
|
||||||
: [...existSigner]
|
: [...existSigner]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Documents.push({
|
Documents.push({
|
||||||
...props.item,
|
...props.item,
|
||||||
Placeholders: updatedPlaceholders,
|
Placeholders: updatedPlaceholders,
|
||||||
SignatureType: props.signatureType
|
SignatureType: props.signatureType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await batchQuery(Documents);
|
await batchQuery(Documents);
|
||||||
} else {
|
} else {
|
||||||
setIsSubmit(false);
|
setIsSubmit(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const batchQuery = async (Documents) => {
|
const batchQuery = async (Documents) => {
|
||||||
const token = {
|
const token =
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||||
};
|
|
||||||
const functionsUrl = `${localStorage.getItem(
|
const functionsUrl = `${localStorage.getItem(
|
||||||
"baseUrl"
|
"baseUrl"
|
||||||
)}functions/batchdocuments`;
|
)}functions/batchdocuments`;
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
...token
|
...token,
|
||||||
};
|
};
|
||||||
const params = { Documents: JSON.stringify(Documents) };
|
const params = { Documents: JSON.stringify(Documents) };
|
||||||
try {
|
try {
|
||||||
@@ -275,7 +277,8 @@ const BulkSendUi = (props) => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -31,6 +31,8 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
|||||||
initializeHead();
|
initializeHead();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
async function initializeHead() {
|
async function initializeHead() {
|
||||||
const applogo = await getAppLogo();
|
const applogo = await getAppLogo();
|
||||||
if (applogo?.logo) {
|
if (applogo?.logo) {
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
function Title({ title, drive }) {
|
function Title({ title, drive }) {
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import "../../styles/opensigndrive.css";
|
import "../../styles/opensigndrive.css";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
import { ContextMenu } from "radix-ui";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import Table from "react-bootstrap/Table";
|
import Table from "react-bootstrap/Table";
|
||||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
import { HoverCard } from "radix-ui";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import FolderModal from "../shared/fields/FolderModal";
|
import FolderModal from "../shared/fields/FolderModal";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
+7
-4
@@ -1,7 +1,10 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
function DefaultSignature(props) {
|
function DefaultSignature(props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
|
||||||
|
const myInitial = useSelector((state) => state.widget.myInitial)
|
||||||
const tabName = ["my-signature", "my-initials"];
|
const tabName = ["my-signature", "my-initials"];
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
const confirmToaddDefaultSign = (type) => {
|
const confirmToaddDefaultSign = (type) => {
|
||||||
@@ -69,15 +72,15 @@ function DefaultSignature(props) {
|
|||||||
<img
|
<img
|
||||||
alt="signature"
|
alt="signature"
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
src={props?.defaultSignImg}
|
src={defaultSignImg}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
activeTab === 1 &&
|
activeTab === 1 &&
|
||||||
(props?.myInitial ? (
|
(myInitial ? (
|
||||||
<img
|
<img
|
||||||
alt="signature"
|
alt="signature"
|
||||||
className="w-full h-full object-contain"
|
className="w-full h-full object-contain"
|
||||||
src={props?.myInitial}
|
src={myInitial}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center h-full">
|
<div className="flex justify-center items-center h-full">
|
||||||
@@ -97,7 +100,7 @@ function DefaultSignature(props) {
|
|||||||
disabled={
|
disabled={
|
||||||
activeTab === 0 && !props?.isDefault
|
activeTab === 0 && !props?.isDefault
|
||||||
? true
|
? true
|
||||||
: activeTab === 1 && !props.myInitial
|
: activeTab === 1 && !myInitial
|
||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
+5
-17
@@ -12,7 +12,7 @@ function DropdownWidgetOption(props) {
|
|||||||
]);
|
]);
|
||||||
const [minCount, setMinCount] = useState(0);
|
const [minCount, setMinCount] = useState(0);
|
||||||
const [maxCount, setMaxCount] = useState(0);
|
const [maxCount, setMaxCount] = useState(0);
|
||||||
const [dropdownName, setDropdownName] = useState(props.type);
|
const [dropdownName, setDropdownName] = useState();
|
||||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||||
const [isHideLabel, setIsHideLabel] = useState(false);
|
const [isHideLabel, setIsHideLabel] = useState(false);
|
||||||
const [status, setStatus] = useState("required");
|
const [status, setStatus] = useState("required");
|
||||||
@@ -22,7 +22,7 @@ function DropdownWidgetOption(props) {
|
|||||||
|
|
||||||
const resetState = () => {
|
const resetState = () => {
|
||||||
setDropdownOptionList(["option-1", "option-2"]);
|
setDropdownOptionList(["option-1", "option-2"]);
|
||||||
setDropdownName(props.type);
|
setDropdownName( props.currWidgetsDetails?.options?.name || props.type);
|
||||||
setIsReadOnly(false);
|
setIsReadOnly(false);
|
||||||
setIsHideLabel(false);
|
setIsHideLabel(false);
|
||||||
setMinCount(0);
|
setMinCount(0);
|
||||||
@@ -30,11 +30,10 @@ function DropdownWidgetOption(props) {
|
|||||||
setDefaultCheckbox([]);
|
setDefaultCheckbox([]);
|
||||||
setDefaultValue("");
|
setDefaultValue("");
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
props.currWidgetsDetails?.options?.name &&
|
props.currWidgetsDetails?.options?.name &&
|
||||||
props.currWidgetsDetails?.options?.values
|
props.currWidgetsDetails?.options?.values?.length > 0
|
||||||
) {
|
) {
|
||||||
setDropdownName(props.currWidgetsDetails?.options?.name);
|
setDropdownName(props.currWidgetsDetails?.options?.name);
|
||||||
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
|
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
|
||||||
@@ -116,16 +115,7 @@ function DropdownWidgetOption(props) {
|
|||||||
defaultData,
|
defaultData,
|
||||||
isHideLabel
|
isHideLabel
|
||||||
);
|
);
|
||||||
// props.setShowDropdown(false);
|
resetState()
|
||||||
setDropdownOptionList(["option-1", "option-2"]);
|
|
||||||
setDropdownName(props.type);
|
|
||||||
// props.setCurrWidgetsDetails({});
|
|
||||||
setIsReadOnly(false);
|
|
||||||
setIsHideLabel(false);
|
|
||||||
setMinCount(0);
|
|
||||||
setMaxCount(0);
|
|
||||||
setDefaultCheckbox([]);
|
|
||||||
setDefaultValue("");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -137,7 +127,6 @@ function DropdownWidgetOption(props) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
|
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
|
||||||
<div className="h-full p-[15px] text-base-content">
|
<div className="h-full p-[15px] text-base-content">
|
||||||
@@ -156,7 +145,6 @@ function DropdownWidgetOption(props) {
|
|||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
required
|
required
|
||||||
defaultValue={dropdownName}
|
|
||||||
value={dropdownName}
|
value={dropdownName}
|
||||||
onChange={(e) => setDropdownName(e.target.value)}
|
onChange={(e) => setDropdownName(e.target.value)}
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
@@ -166,7 +154,7 @@ function DropdownWidgetOption(props) {
|
|||||||
{t("options")}
|
{t("options")}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{dropdownOptionList.map((option, index) => (
|
{dropdownOptionList?.map((option, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex flex-row mb-[5px] items-center"
|
className="flex flex-row mb-[5px] items-center"
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { getFileName } from "../../constant/Utils";
|
|
||||||
import {
|
|
||||||
maxDescriptionLength,
|
|
||||||
maxNoteLength,
|
|
||||||
maxTitleLength
|
|
||||||
} from "../../constant/const";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Tooltip } from "react-tooltip";
|
|
||||||
import SignersInput from "../shared/fields/SignersInput";
|
|
||||||
|
|
||||||
const EditTemplate = ({ template, onSuccess }) => {
|
|
||||||
const appName = "OpenSign™";
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [formData, setFormData] = useState({
|
|
||||||
Name: template?.Name || "",
|
|
||||||
Note: template?.Note || "",
|
|
||||||
Description: template?.Description || "",
|
|
||||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
|
||||||
AutomaticReminders: template?.AutomaticReminders || false,
|
|
||||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
|
||||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
|
||||||
IsTourEnabled: template?.IsTourEnabled
|
|
||||||
? `${template?.IsTourEnabled}`
|
|
||||||
: "false",
|
|
||||||
NotifyOnSignatures:
|
|
||||||
template?.NotifyOnSignatures !== undefined
|
|
||||||
? template?.NotifyOnSignatures
|
|
||||||
: false,
|
|
||||||
Bcc: template?.Bcc,
|
|
||||||
RedirectUrl: template?.RedirectUrl || "",
|
|
||||||
AllowModifications: template?.AllowModifications || false,
|
|
||||||
TimeToCompleteDays: template?.TimeToCompleteDays || 15
|
|
||||||
});
|
|
||||||
|
|
||||||
// `isValidURL` is used to check valid webhook url
|
|
||||||
function isValidURL(value) {
|
|
||||||
try {
|
|
||||||
const url = new URL(value);
|
|
||||||
return url.protocol === "https:" || url.protocol === "http:";
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStrInput = (e) => {
|
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define a function to handle form submission
|
|
||||||
const handleSubmit = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (formData.RedirectUrl && !isValidURL(formData?.RedirectUrl)) {
|
|
||||||
alert(t("invalid-redirect-url"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (formData?.Name?.length > maxTitleLength) {
|
|
||||||
alert(t("title-length-alert"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (formData?.Note?.length > maxNoteLength) {
|
|
||||||
alert(t("note-length-alert"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (formData?.Description?.length > maxDescriptionLength) {
|
|
||||||
alert(t("description-length-alert"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
|
||||||
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
|
||||||
const AutoReminder = formData?.AutomaticReminders || false;
|
|
||||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
|
||||||
const allowModify = formData?.AllowModifications || false;
|
|
||||||
let reminderDate = {};
|
|
||||||
const remindOnceInEvery = formData?.RemindOnceInEvery;
|
|
||||||
const TimeToCompleteDays = parseInt(formData?.TimeToCompleteDays);
|
|
||||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
|
||||||
if (AutoReminder && reminderCount > 15) {
|
|
||||||
alert(t("only-15-reminder-allowed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (AutoReminder) {
|
|
||||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
|
||||||
const ReminderDate = new Date(template?.createdAt);
|
|
||||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
|
||||||
reminderDate = { NextReminderDate: ReminderDate };
|
|
||||||
}
|
|
||||||
const data = {
|
|
||||||
...formData,
|
|
||||||
SendinOrder: isChecked,
|
|
||||||
IsEnableOTP: IsEnableOTP,
|
|
||||||
IsTourEnabled: isTourEnabled,
|
|
||||||
AllowModifications: allowModify,
|
|
||||||
...reminderDate
|
|
||||||
};
|
|
||||||
onSuccess(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
|
||||||
const handleNotifySignChange = (value) => {
|
|
||||||
setFormData((obj) => ({ ...obj, NotifyOnSignatures: value }));
|
|
||||||
};
|
|
||||||
const handleBcc = (data) => {
|
|
||||||
if (data && data.length > 0) {
|
|
||||||
const trimEmail = data.map((item) => ({
|
|
||||||
objectId: item?.value,
|
|
||||||
Name: item?.label,
|
|
||||||
Email: item?.email
|
|
||||||
}));
|
|
||||||
setFormData((prev) => ({ ...prev, Bcc: trimEmail }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
|
||||||
<div className="text-base-content">
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className="mb-[0.35rem]">
|
|
||||||
<label htmlFor="name" className="text-[13px]">
|
|
||||||
{t("report-heading.File")}
|
|
||||||
</label>
|
|
||||||
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
|
|
||||||
{getFileName(template.URL)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.35rem]">
|
|
||||||
<label htmlFor="name" className="text-[13px]">
|
|
||||||
{t("Title")}
|
|
||||||
<span className="text-[13px] text-[red]"> *</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="Name"
|
|
||||||
value={formData.Name}
|
|
||||||
onChange={(e) => handleStrInput(e)}
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
required
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.35rem]">
|
|
||||||
<label htmlFor="Note" className="text-[13px]">
|
|
||||||
{t("report-heading.Note")}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="Note"
|
|
||||||
id="Note"
|
|
||||||
value={formData.Note}
|
|
||||||
onChange={(e) => handleStrInput(e)}
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.35rem]">
|
|
||||||
<label htmlFor="Description" className="text-[13px]">
|
|
||||||
{t("description")}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="Description"
|
|
||||||
id="Description"
|
|
||||||
value={formData.Description}
|
|
||||||
onChange={(e) => handleStrInput(e)}
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.35rem]">
|
|
||||||
<label className="text-[13px]">{t("send-in-order")}</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={"true"}
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
name="SendinOrder"
|
|
||||||
checked={formData.SendinOrder === "true"}
|
|
||||||
onChange={handleStrInput}
|
|
||||||
/>
|
|
||||||
<div className="text-[12px]">{t("yes")}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={"false"}
|
|
||||||
name="SendinOrder"
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
checked={formData.SendinOrder === "false"}
|
|
||||||
onChange={handleStrInput}
|
|
||||||
/>
|
|
||||||
<div className="text-[12px]">{t("no")}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-3">
|
|
||||||
<label className="block">
|
|
||||||
<span>
|
|
||||||
{t("enable-tour")}
|
|
||||||
<a data-tooltip-id="istourenabled-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>{" "}
|
|
||||||
</span>
|
|
||||||
<Tooltip id="istourenabled-tooltip" className="z-50">
|
|
||||||
<div className="max-w-[200px] md:max-w-[450px]">
|
|
||||||
<p className="font-bold">{t("enable-tour")}</p>
|
|
||||||
<div className="p-[5px]">
|
|
||||||
<ol className="list-disc">
|
|
||||||
<li>
|
|
||||||
<span className="font-bold">{t("yes")}: </span>
|
|
||||||
<span>{t("istourenabled-help.p1")}</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<span className="font-bold">{t("no")}: </span>
|
|
||||||
<span>{t("istourenabled-help.p2")}</span>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<p>{t("istourenabled-help.p3", { appName: appName })}</p>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={"true"}
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
name="IsTourEnabled"
|
|
||||||
checked={formData.IsTourEnabled === "true"}
|
|
||||||
onChange={handleStrInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("yes")}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={"false"}
|
|
||||||
name="IsTourEnabled"
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
checked={formData.IsTourEnabled === "false"}
|
|
||||||
onChange={handleStrInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("no")}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-3">
|
|
||||||
<label>
|
|
||||||
{t("notify-on-signatures")}
|
|
||||||
<a data-tooltip-id="nos-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="nos-tooltip" className="z-[999]">
|
|
||||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
|
||||||
<p className="font-bold">{t("notify-on-signatures")}</p>
|
|
||||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
|
||||||
<p>{t("notify-on-signatures-help.note")}</p>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
|
||||||
<input
|
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
|
||||||
type="radio"
|
|
||||||
onChange={() => handleNotifySignChange(true)}
|
|
||||||
checked={formData.NotifyOnSignatures === true}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("yes")}</div>
|
|
||||||
</div>
|
|
||||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
|
||||||
<input
|
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
|
||||||
type="radio"
|
|
||||||
onChange={() => handleNotifySignChange(false)}
|
|
||||||
checked={formData.NotifyOnSignatures === false}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("no")}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-3">
|
|
||||||
<SignersInput
|
|
||||||
label={t("Bcc")}
|
|
||||||
initialData={template?.Bcc}
|
|
||||||
onChange={handleBcc}
|
|
||||||
helptextZindex={50}
|
|
||||||
helpText={t("bcc-help")}
|
|
||||||
isCaptureAllData
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-2">
|
|
||||||
<label className="block">Redirect Url</label>
|
|
||||||
<input
|
|
||||||
name="RedirectUrl"
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
value={formData.RedirectUrl}
|
|
||||||
onChange={handleStrInput}
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-2">
|
|
||||||
<label className="block">
|
|
||||||
{t("time-to-complete")}
|
|
||||||
<span className="text-red-500 text-[13px]">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="TimeToCompleteDays"
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
value={formData.TimeToCompleteDays}
|
|
||||||
onChange={(e) => handleStrInput(e)}
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-[1rem] flex justify-start">
|
|
||||||
<button type="submit" className="op-btn op-btn-primary">
|
|
||||||
{t("submit")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EditTemplate;
|
|
||||||
@@ -0,0 +1,538 @@
|
|||||||
|
import React, {
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
base64ToArrayBuffer,
|
||||||
|
convertBase64ToFile,
|
||||||
|
generatePdfName,
|
||||||
|
getFileName
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import {
|
||||||
|
maxDescriptionLength,
|
||||||
|
maxNoteLength,
|
||||||
|
maxTitleLength
|
||||||
|
} from "../../constant/const";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
import SignersInput from "../shared/fields/SignersInput";
|
||||||
|
import { PDFDocument } from "pdf-lib";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import { SaveFileSize } from "../../constant/saveFileSize";
|
||||||
|
|
||||||
|
const EditTemplate = ({
|
||||||
|
title,
|
||||||
|
handleClose,
|
||||||
|
pdfbase64,
|
||||||
|
template,
|
||||||
|
onSuccess,
|
||||||
|
setPdfArrayBuffer,
|
||||||
|
setPdfBase64Url,
|
||||||
|
}) => {
|
||||||
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const inputFileRef = useRef(null);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
Name: template?.Name || "",
|
||||||
|
Note: template?.Note || "",
|
||||||
|
Description: template?.Description || "",
|
||||||
|
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||||
|
AutomaticReminders: template?.AutomaticReminders || false,
|
||||||
|
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
||||||
|
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
||||||
|
IsTourEnabled: template?.IsTourEnabled
|
||||||
|
? `${template?.IsTourEnabled}`
|
||||||
|
: "false",
|
||||||
|
NotifyOnSignatures:
|
||||||
|
template?.NotifyOnSignatures !== undefined
|
||||||
|
? template?.NotifyOnSignatures
|
||||||
|
: false,
|
||||||
|
Bcc: template?.Bcc,
|
||||||
|
RedirectUrl: template?.RedirectUrl || "",
|
||||||
|
AllowModifications: template?.AllowModifications || false,
|
||||||
|
TimeToCompleteDays: template?.TimeToCompleteDays || 15
|
||||||
|
});
|
||||||
|
const [isUpdate, setIsUpdate] = useState(false);
|
||||||
|
const [showConfirm, setShowConfirm] = useState(false);
|
||||||
|
const [uploadPdf, setUploadPdf] = useState({
|
||||||
|
name: "",
|
||||||
|
base64: "",
|
||||||
|
url: ""
|
||||||
|
});
|
||||||
|
const handleDrop = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const file = e.dataTransfer.files[0];
|
||||||
|
handleFile(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFile = (file) => {
|
||||||
|
if (file && file.type === "application/pdf") {
|
||||||
|
handleReplaceFileValdition(file);
|
||||||
|
// You can handle the file here
|
||||||
|
} else {
|
||||||
|
alert("Only pdf files are allowed.");
|
||||||
|
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleDragOver = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
// `isValidURL` is used to check valid webhook url
|
||||||
|
function isValidURL(value) {
|
||||||
|
try {
|
||||||
|
const url = new URL(value);
|
||||||
|
return url.protocol === "https:" || url.protocol === "http:";
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStrInput = (e) => {
|
||||||
|
setIsUpdate(true);
|
||||||
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
const getPdfMetadataHash = async (pdfBytes) => {
|
||||||
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
||||||
|
const pages = pdfDoc.getPages();
|
||||||
|
const metaString = pages
|
||||||
|
.map((page, index) => {
|
||||||
|
const { width, height } = page.getSize();
|
||||||
|
return `${index + 1}:${Math.round(width)}x${Math.round(height)}`;
|
||||||
|
})
|
||||||
|
.join("|");
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const data = encoder.encode(metaString);
|
||||||
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
||||||
|
return Array.from(new Uint8Array(hashBuffer))
|
||||||
|
.map((b) => b.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileInput = async (e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
handleReplaceFileValdition(file);
|
||||||
|
};
|
||||||
|
const handleReplaceFileValdition = async (file) => {
|
||||||
|
try {
|
||||||
|
const basePdfBytes = base64ToArrayBuffer(pdfbase64);
|
||||||
|
const expectedHash = await getPdfMetadataHash(basePdfBytes);
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
fileReader.onload = async (event) => {
|
||||||
|
const uploadedPdfBytes = event.target.result;
|
||||||
|
const uploadedHash = await getPdfMetadataHash(uploadedPdfBytes);
|
||||||
|
|
||||||
|
if (expectedHash === uploadedHash) {
|
||||||
|
const arrayBuffer = uploadedPdfBytes;
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
const binaryString = Array.from(uint8Array)
|
||||||
|
.map((b) => String.fromCharCode(b))
|
||||||
|
.join("");
|
||||||
|
const base64 = btoa(binaryString);
|
||||||
|
const pdfName = generatePdfName(16);
|
||||||
|
setIsUpdate(true);
|
||||||
|
setUploadPdf((prev) => ({ ...prev, name: pdfName, base64: base64 }));
|
||||||
|
// alert("✅ PDFs match (based on page number, width, height)");
|
||||||
|
} else {
|
||||||
|
alert("❌ PDF do NOT match based on page number, width, height");
|
||||||
|
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsArrayBuffer(file);
|
||||||
|
} catch (err) {
|
||||||
|
alert("Error: " + err.message);
|
||||||
|
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Define a function to handle form submission
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (formData.RedirectUrl && !isValidURL(formData?.RedirectUrl)) {
|
||||||
|
alert(t("invalid-redirect-url"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (formData?.Name?.length > maxTitleLength) {
|
||||||
|
alert(t("title-length-alert"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (formData?.Note?.length > maxNoteLength) {
|
||||||
|
alert(t("note-length-alert"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (formData?.Description?.length > maxDescriptionLength) {
|
||||||
|
alert(t("description-length-alert"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pdfUrl;
|
||||||
|
if (uploadPdf?.base64) {
|
||||||
|
pdfUrl = await convertBase64ToFile(
|
||||||
|
uploadPdf.name,
|
||||||
|
uploadPdf.base64,
|
||||||
|
);
|
||||||
|
setUploadPdf((prev) => ({ ...prev, url: pdfUrl }));
|
||||||
|
const pdfBuffer = base64ToArrayBuffer(uploadPdf.base64);
|
||||||
|
setPdfArrayBuffer && setPdfArrayBuffer(pdfBuffer);
|
||||||
|
setPdfBase64Url && setPdfBase64Url(uploadPdf.base64);
|
||||||
|
const tenantId =
|
||||||
|
localStorage.getItem("TenantId") ||
|
||||||
|
template?.ExtUserPtr?.TenantId?.objectId;
|
||||||
|
const buffer = atob(uploadPdf.base64);
|
||||||
|
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||||
|
}
|
||||||
|
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||||
|
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
||||||
|
const AutoReminder = formData?.AutomaticReminders || false;
|
||||||
|
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||||
|
const allowModify = formData?.AllowModifications || false;
|
||||||
|
let reminderDate = {};
|
||||||
|
const remindOnceInEvery = formData?.RemindOnceInEvery;
|
||||||
|
const TimeToCompleteDays = parseInt(formData?.TimeToCompleteDays);
|
||||||
|
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||||
|
if (AutoReminder && reminderCount > 15) {
|
||||||
|
alert(t("only-15-reminder-allowed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||||
|
const ReminderDate = new Date(template?.createdAt);
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
reminderDate = { NextReminderDate: ReminderDate };
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
...formData,
|
||||||
|
...(pdfUrl ? { URL: pdfUrl } : {}),
|
||||||
|
SendinOrder: isChecked,
|
||||||
|
IsEnableOTP: IsEnableOTP,
|
||||||
|
IsTourEnabled: isTourEnabled,
|
||||||
|
AllowModifications: allowModify,
|
||||||
|
...reminderDate
|
||||||
|
};
|
||||||
|
onSuccess(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
||||||
|
const handleNotifySignChange = (value) => {
|
||||||
|
setIsUpdate(true);
|
||||||
|
setFormData((obj) => ({ ...obj, NotifyOnSignatures: value }));
|
||||||
|
};
|
||||||
|
const handleBcc = (data) => {
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const trimEmail = data.map((item) => ({
|
||||||
|
objectId: item?.value,
|
||||||
|
Name: item?.label,
|
||||||
|
Email: item?.email
|
||||||
|
}));
|
||||||
|
setIsUpdate(true);
|
||||||
|
setFormData((prev) => ({ ...prev, Bcc: trimEmail }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditTemplateClose = () => {
|
||||||
|
if (isUpdate) {
|
||||||
|
setShowConfirm(true);
|
||||||
|
} else {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const discardChanges = () => {
|
||||||
|
setShowConfirm(false);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<ModalUi
|
||||||
|
isOpen
|
||||||
|
title={isUpdate ? `${title} (unsaved)` : title}
|
||||||
|
handleClose={handleEditTemplateClose}
|
||||||
|
>
|
||||||
|
<ModalUi isOpen={showConfirm} showClose={false}>
|
||||||
|
<div className="p-[20px]">
|
||||||
|
<p className="text-base font-normal text-base-content py-[5px] md:py-[6px] px-[5px]">
|
||||||
|
{t("unsaved-changes-discard-them?")}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center mt-2.5 gap-2 md:gap-3 text-white">
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-primary px-6"
|
||||||
|
onClick={discardChanges}
|
||||||
|
>
|
||||||
|
{t("yes-discard")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary px-4 md:px-6"
|
||||||
|
onClick={() => setShowConfirm(false)}
|
||||||
|
>
|
||||||
|
{t("cancel")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||||
|
<div className="text-base-content">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-[0.35rem]">
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
|
{t("report-heading.File")}
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className="border-[1.5px] border-dashed border-gray-300 rounded-lg px-4 py-6 text-center text-gray-500 bg-white cursor-pointer hover:border-base-content transition"
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onClick={() => inputFileRef?.current?.click()}
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
htmlFor="fileUpload"
|
||||||
|
className="cursor-pointer text-center mb-0"
|
||||||
|
>
|
||||||
|
Browse or drag & drop a new file to replace the existng one
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={inputFileRef}
|
||||||
|
type="file"
|
||||||
|
className="hidden"
|
||||||
|
accept="application/pdf"
|
||||||
|
onChange={(e) => handleFileInput(e)}
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
/>
|
||||||
|
{uploadPdf?.name && (
|
||||||
|
<div
|
||||||
|
onClick={() => inputFileRef?.current?.click()}
|
||||||
|
className="mt-2 cursor-pointer op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs"
|
||||||
|
>
|
||||||
|
selected:{" "}
|
||||||
|
{uploadPdf?.url
|
||||||
|
? `${uploadPdf?.name}.pdf`
|
||||||
|
: getFileName(template.URL)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mb-[0.35rem]">
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
|
{t("Title")}
|
||||||
|
<span className="text-[13px] text-[red]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Name"
|
||||||
|
value={formData.Name}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-[0.35rem]">
|
||||||
|
<label htmlFor="Note" className="text-[13px]">
|
||||||
|
{t("report-heading.Note")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Note"
|
||||||
|
id="Note"
|
||||||
|
value={formData.Note}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-[0.35rem]">
|
||||||
|
<label htmlFor="Description" className="text-[13px]">
|
||||||
|
{t("description")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Description"
|
||||||
|
id="Description"
|
||||||
|
value={formData.Description}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-[0.35rem]">
|
||||||
|
<label className="text-[13px]">{t("send-in-order")}</label>
|
||||||
|
<div className="flex flex-col md:flex-row md:gap-4">
|
||||||
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"true"}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
name="SendinOrder"
|
||||||
|
checked={formData.SendinOrder === "true"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-[12px]">{t("yes")}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"false"}
|
||||||
|
name="SendinOrder"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
checked={formData.SendinOrder === "false"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-[12px]">{t("no")}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-3">
|
||||||
|
<label className="block">
|
||||||
|
<span>
|
||||||
|
{t("enable-tour")}
|
||||||
|
<a data-tooltip-id="istourenabled-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>{" "}
|
||||||
|
</span>
|
||||||
|
<Tooltip id="istourenabled-tooltip" className="z-50">
|
||||||
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
|
<p className="font-bold">{t("enable-tour")}</p>
|
||||||
|
<div className="p-[5px]">
|
||||||
|
<ol className="list-disc">
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">{t("yes")}: </span>
|
||||||
|
<span>{t("istourenabled-help.p1")}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">{t("no")}: </span>
|
||||||
|
<span>{t("istourenabled-help.p2")}</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<p>{t("istourenabled-help.p3", { appName: appName })}</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-col md:flex-row md:gap-4">
|
||||||
|
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"true"}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
name="IsTourEnabled"
|
||||||
|
checked={formData.IsTourEnabled === "true"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-center">{t("yes")}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"false"}
|
||||||
|
name="IsTourEnabled"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
checked={formData.IsTourEnabled === "false"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-center">{t("no")}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-3">
|
||||||
|
<label>
|
||||||
|
{t("notify-on-signatures")}
|
||||||
|
<a data-tooltip-id="nos-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="nos-tooltip" className="z-[999]">
|
||||||
|
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||||
|
<p className="font-bold">{t("notify-on-signatures")}</p>
|
||||||
|
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||||
|
<p>{t("notify-on-signatures-help.note")}</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-col md:flex-row md:gap-4">
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
`flex items-center gap-2 ml-2 mb-1`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
|
type="radio"
|
||||||
|
onChange={() => handleNotifySignChange(true)}
|
||||||
|
checked={formData.NotifyOnSignatures === true}
|
||||||
|
/>
|
||||||
|
<div className="text-center">{t("yes")}</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
`flex items-center gap-2 ml-2 mb-1`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
|
type="radio"
|
||||||
|
onChange={() => handleNotifySignChange(false)}
|
||||||
|
checked={formData.NotifyOnSignatures === false}
|
||||||
|
/>
|
||||||
|
<div className="text-center">{t("no")}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-3">
|
||||||
|
<SignersInput
|
||||||
|
label={t("Bcc")}
|
||||||
|
initialData={template?.Bcc}
|
||||||
|
onChange={handleBcc}
|
||||||
|
helptextZindex={50}
|
||||||
|
helpText={t("bcc-help")}
|
||||||
|
isCaptureAllData
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">Redirect Url</label>
|
||||||
|
<input
|
||||||
|
name="RedirectUrl"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
value={formData.RedirectUrl}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
{t("time-to-complete")}
|
||||||
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="TimeToCompleteDays"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
value={formData.TimeToCompleteDays}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-[1rem] flex justify-start">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
{t("submit")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditTemplate;
|
||||||
+8
-3
@@ -1,6 +1,10 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { handleToPrint } from "../../constant/Utils";
|
import {
|
||||||
import { emailRegex } from "../../constant/const";
|
handleToPrint,
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import {
|
||||||
|
emailRegex,
|
||||||
|
} from "../../constant/const";
|
||||||
import Loader from "../../primitives/Loader";
|
import Loader from "../../primitives/Loader";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -37,7 +41,8 @@ function EmailComponent({
|
|||||||
setEmailList([]);
|
setEmailList([]);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setIsEmail(false);
|
setIsEmail(false);
|
||||||
setIsAlert({
|
setIsAlert({
|
||||||
+21
-14
@@ -9,7 +9,7 @@ import {
|
|||||||
handleToPrint
|
handleToPrint
|
||||||
} from "../../constant/Utils";
|
} from "../../constant/Utils";
|
||||||
import "../../styles/signature.css";
|
import "../../styles/signature.css";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import { DropdownMenu } from "radix-ui";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import Loader from "../../primitives/Loader";
|
import Loader from "../../primitives/Loader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -28,8 +28,12 @@ function Header(props) {
|
|||||||
const enabledBackBtn = props?.disabledBackBtn === true ? false : true;
|
const enabledBackBtn = props?.disabledBackBtn === true ? false : true;
|
||||||
//function for show decline alert
|
//function for show decline alert
|
||||||
const handleDeclinePdfAlert = async () => {
|
const handleDeclinePdfAlert = async () => {
|
||||||
const currentDecline = { currnt: "Sure", isDeclined: true };
|
if (props?.handleDecline) {
|
||||||
props?.setIsDecline(currentDecline);
|
props.handleDecline();
|
||||||
|
} else {
|
||||||
|
const currentDecline = { currnt: "Sure", isDeclined: true };
|
||||||
|
props?.setIsDecline(currentDecline);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const handleDetelePage = async () => {
|
const handleDetelePage = async () => {
|
||||||
props?.setIsUploadPdf && props?.setIsUploadPdf(true);
|
props?.setIsUploadPdf && props?.setIsUploadPdf(true);
|
||||||
@@ -102,7 +106,6 @@ function Header(props) {
|
|||||||
console.error("Error merging PDF:", error);
|
console.error("Error merging PDF:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex py-[5px]">
|
<div className="flex py-[5px]">
|
||||||
{isMobile && props?.isShowHeader ? (
|
{isMobile && props?.isShowHeader ? (
|
||||||
@@ -273,6 +276,20 @@ function Header(props) {
|
|||||||
className="bg-white shadow-md rounded-md px-3 py-2"
|
className="bg-white shadow-md rounded-md px-3 py-2"
|
||||||
sideOffset={5}
|
sideOffset={5}
|
||||||
>
|
>
|
||||||
|
{props?.setIsEditTemplate && (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="DropdownMenuItem"
|
||||||
|
onClick={() => props?.setIsEditTemplate(true)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<i
|
||||||
|
className="fa-light fa-gear mr-[3px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-[500]">{t("Edit")}</span>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)}
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
className="DropdownMenuItem"
|
className="DropdownMenuItem"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -502,16 +519,6 @@ function Header(props) {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex" data-tut="reactourFifth">
|
<div className="flex" data-tut="reactourFifth">
|
||||||
{(!props?.templateId && !props?.isSelfSign) ||
|
|
||||||
(!props.isGuestSignFlow && (
|
|
||||||
<button
|
|
||||||
onClick={() => window.history.go(-2)}
|
|
||||||
type="button"
|
|
||||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
|
||||||
>
|
|
||||||
{t("back")}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
{props?.currentSigner && (
|
{props?.currentSigner && (
|
||||||
<>
|
<>
|
||||||
{props?.templateId && (
|
{props?.templateId && (
|
||||||
+103
-232
@@ -3,6 +3,7 @@ import BorderResize from "./BorderResize";
|
|||||||
import PlaceholderBorder from "./PlaceholderBorder";
|
import PlaceholderBorder from "./PlaceholderBorder";
|
||||||
import { Rnd } from "react-rnd";
|
import { Rnd } from "react-rnd";
|
||||||
import {
|
import {
|
||||||
|
changeDateToMomentFormat,
|
||||||
defaultWidthHeight,
|
defaultWidthHeight,
|
||||||
fontColorArr,
|
fontColorArr,
|
||||||
fontsizeArr,
|
fontsizeArr,
|
||||||
@@ -20,6 +21,8 @@ import moment from "moment";
|
|||||||
import "../../styles/opensigndrive.css";
|
import "../../styles/opensigndrive.css";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setIsShowModal } from "../../redux/reducers/widgetSlice";
|
||||||
|
|
||||||
const selectFormat = (data) => {
|
const selectFormat = (data) => {
|
||||||
switch (data) {
|
switch (data) {
|
||||||
@@ -52,32 +55,6 @@ const selectFormat = (data) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeDateToMomentFormat = (format) => {
|
|
||||||
switch (format) {
|
|
||||||
case "MM/dd/yyyy":
|
|
||||||
return "L";
|
|
||||||
case "dd-MM-yyyy":
|
|
||||||
return "DD-MM-YYYY";
|
|
||||||
case "dd/MM/yyyy":
|
|
||||||
return "DD/MM/YYYY";
|
|
||||||
case "MMMM dd, yyyy":
|
|
||||||
return "LL";
|
|
||||||
case "dd MMM, yyyy":
|
|
||||||
return "DD MMM, YYYY";
|
|
||||||
case "yyyy-MM-dd":
|
|
||||||
return "YYYY-MM-DD";
|
|
||||||
case "MM-dd-yyyy":
|
|
||||||
return "MM-DD-YYYY";
|
|
||||||
case "MM.dd.yyyy":
|
|
||||||
return "MM.DD.YYYY";
|
|
||||||
case "MMM dd, yyyy":
|
|
||||||
return "MMM DD, YYYY";
|
|
||||||
case "dd MMMM, yyyy":
|
|
||||||
return "DD MMMM, YYYY";
|
|
||||||
default:
|
|
||||||
return "L";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//function to get default format
|
//function to get default format
|
||||||
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||||
|
|
||||||
@@ -97,27 +74,25 @@ const getDefaultDate = (dateStr, format) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function Placeholder(props) {
|
function Placeholder(props) {
|
||||||
//'isTouchDevice' is used to detect whether a device has a touchscreen or is mouse-based
|
|
||||||
const isTouchDevice = navigator.maxTouchPoints > 0;
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const widgetData =
|
||||||
|
props.pos?.options?.defaultValue || props.pos?.options?.response;
|
||||||
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
|
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
|
||||||
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
|
||||||
const [isDateModal, setIsDateModal] = useState(false);
|
const [isDateModal, setIsDateModal] = useState(false);
|
||||||
const [containerScale, setContainerScale] = useState();
|
const [containerScale, setContainerScale] = useState();
|
||||||
const holdTimeout = useRef(null);
|
const holdTimeout = useRef(null);
|
||||||
const startTime = useRef(null); // Track when the user starts holdings
|
const startTime = useRef(null); // Track when the user starts holdings
|
||||||
const [isDisableDragging, setIsDisableDragging] = useState(true);
|
|
||||||
const [selectDate, setSelectDate] = useState({});
|
const [selectDate, setSelectDate] = useState({});
|
||||||
const [dateFormat, setDateFormat] = useState([]);
|
const [dateFormat, setDateFormat] = useState([]);
|
||||||
const [clickonWidget, setClickonWidget] = useState({});
|
const [clickonWidget, setClickonWidget] = useState({});
|
||||||
const [startDate, setStartDate] = useState(
|
const startDate = props?.pos?.options?.response
|
||||||
props?.pos?.options?.response
|
? getDefaultDate(
|
||||||
? getDefaultDate(
|
props?.pos?.options?.response,
|
||||||
props?.pos?.options?.response,
|
props.pos?.options?.validation?.format
|
||||||
props.pos?.options?.validation?.format
|
)
|
||||||
)
|
: new Date();
|
||||||
: new Date()
|
|
||||||
);
|
|
||||||
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
|
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
|
||||||
width: null,
|
width: null,
|
||||||
height: null
|
height: null
|
||||||
@@ -142,33 +117,28 @@ function Placeholder(props) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateWidth = () => {
|
if (props?.pos?.type === "date") {
|
||||||
|
const isDateChange = true;
|
||||||
|
const dateObj = {
|
||||||
|
date: startDate,
|
||||||
|
format: getDefaultFormat(props.pos?.options?.validation?.format)
|
||||||
|
};
|
||||||
|
handleSaveDate(dateObj, isDateChange); //function to save date and format in local array
|
||||||
|
}
|
||||||
|
}, [widgetData]);
|
||||||
|
|
||||||
|
const handleGetDaynamicWH = () => {
|
||||||
|
if (
|
||||||
|
props?.pos?.type === "checkbox" ||
|
||||||
|
props?.pos?.type === radioButtonWidget
|
||||||
|
) {
|
||||||
const rndElement = document.getElementById(props.pos.key);
|
const rndElement = document.getElementById(props.pos.key);
|
||||||
if (rndElement) {
|
if (rndElement) {
|
||||||
const { width, height } = rndElement.getBoundingClientRect();
|
const { width, height } = rndElement.getBoundingClientRect();
|
||||||
setGetCheckboxRenderWidth({ width: width, height: height });
|
setGetCheckboxRenderWidth({ width: width, height: height });
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
// Delay to ensure rendering is complete
|
|
||||||
const timer = setTimeout(updateWidth, 0);
|
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
}, [props.pos]);
|
|
||||||
useEffect(() => {
|
|
||||||
const onOutsideClick = () => {
|
|
||||||
if (!isDraggingEnabled) {
|
|
||||||
setDraggingEnabled(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("click", onOutsideClick);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
// Cleanup the event listener when the component unmounts
|
|
||||||
document.removeEventListener("click", onOutsideClick);
|
|
||||||
};
|
|
||||||
}, [isDraggingEnabled]);
|
|
||||||
//function change format array list with selected date and format
|
//function change format array list with selected date and format
|
||||||
const changeDateFormat = () => {
|
const changeDateFormat = () => {
|
||||||
const updateDate = [];
|
const updateDate = [];
|
||||||
@@ -199,61 +169,40 @@ function Placeholder(props) {
|
|||||||
}, [selectDate]);
|
}, [selectDate]);
|
||||||
|
|
||||||
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
|
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
|
||||||
const handleWidgetIdandPopup = () => {
|
const handleWidgetIdandPopup = async () => {
|
||||||
if (props.setSelectWidgetId) {
|
//'props.isOpenSignPad' variable is used to check flow to open signature pad for finish document
|
||||||
props.setSelectWidgetId(props.pos.key);
|
if (
|
||||||
}
|
props.isOpenSignPad &&
|
||||||
|
!props.isDragging &&
|
||||||
const widgetTypeExist = [
|
!props.pos.options?.isReadOnly &&
|
||||||
textInputWidget,
|
props.pos.type !== textWidget
|
||||||
"checkbox",
|
) {
|
||||||
"name",
|
|
||||||
"company",
|
|
||||||
"job title",
|
|
||||||
"date",
|
|
||||||
"email",
|
|
||||||
textWidget
|
|
||||||
].includes(props.pos.type);
|
|
||||||
|
|
||||||
if (widgetTypeExist) {
|
|
||||||
setDraggingEnabled(false);
|
|
||||||
}
|
|
||||||
if (props.isOpenSignPad && !props.isDragging) {
|
|
||||||
if (props?.ispublicTemplate) {
|
if (props?.ispublicTemplate) {
|
||||||
props.handleUserDetails();
|
props.handleUserDetails();
|
||||||
} else {
|
} else {
|
||||||
if (props?.isNeedSign) {
|
if (props?.isNeedSign) {
|
||||||
//funcion is used to height widgets on top if two widgets on overlap
|
//funcion is used to highlight widgets on top when click any widget if two widgets on overlap
|
||||||
const getCurrentSignerPos = props.xyPosition.find(
|
const getCurrentSignerPos = props.xyPosition.find(
|
||||||
(x) => x.Id === props.uniqueId
|
(x) => x.Id === props.uniqueId
|
||||||
);
|
);
|
||||||
const updateZindex = handleHeighlightWidget(
|
const updateZindex = handleHeighlightWidget(
|
||||||
getCurrentSignerPos,
|
getCurrentSignerPos,
|
||||||
props.pos.key,
|
props.pos.key,
|
||||||
props.pageNumber
|
props.pageNumber
|
||||||
);
|
);
|
||||||
const updatesignerPos = props.xyPosition.map((x) =>
|
const updatesignerPos = props.xyPosition.map((x) =>
|
||||||
x.Id === props.uniqueId ? { ...x, placeHolder: updateZindex } : x
|
x.Id === props.uniqueId ? { ...x, placeHolder: updateZindex } : x
|
||||||
);
|
);
|
||||||
props.setXyPosition(updatesignerPos);
|
props.setXyPosition(updatesignerPos);
|
||||||
}
|
}
|
||||||
if (
|
dispatch(setIsShowModal({ [props.pos.key]: true }));
|
||||||
["signature", "stamp", "image", "initials"].includes(props.pos.type)
|
|
||||||
) {
|
|
||||||
props.setIsSignPad(true);
|
|
||||||
props.setSignKey(props.pos.key);
|
|
||||||
props.setIsStamp(props.pos.isStamp);
|
|
||||||
}
|
|
||||||
if (props.pos.type === "initials") {
|
|
||||||
props.setIsInitial(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
props.isPlaceholder &&
|
props.isPlaceholder &&
|
||||||
!props.isDragging &&
|
!props.isDragging &&
|
||||||
props.pos.type !== textWidget
|
props.pos.type !== textWidget
|
||||||
) {
|
) {
|
||||||
if (props.pos.key === props.selectWidgetId) {
|
if (props.pos.key === props?.currWidgetsDetails?.key) {
|
||||||
props.handleLinkUser(props.data.Id);
|
props.handleLinkUser(props.data.Id);
|
||||||
props.setUniqueId(props.data.Id);
|
props.setUniqueId(props.data.Id);
|
||||||
const checkIndex = props.xyPosition.findIndex(
|
const checkIndex = props.xyPosition.findIndex(
|
||||||
@@ -261,31 +210,17 @@ function Placeholder(props) {
|
|||||||
);
|
);
|
||||||
props.setIsSelectId(checkIndex || 0);
|
props.setIsSelectId(checkIndex || 0);
|
||||||
}
|
}
|
||||||
} else if (!props.pos.type) {
|
//handle prefill 'text widget' click then save previos in tem variable after save or close button again assign current selected userId
|
||||||
if (
|
} else if (props.pos.type === textWidget) {
|
||||||
!props.pos.type &&
|
dispatch(setIsShowModal({ [props.pos.key]: true }));
|
||||||
props.isNeedSign &&
|
props.setTempSignerId(props?.uniqueId);
|
||||||
props.data.signerObjId === props.signerObjId
|
props.setUniqueId(props?.data?.Id);
|
||||||
) {
|
|
||||||
props.setIsSignPad(true);
|
|
||||||
props.setSignKey(props.pos.key);
|
|
||||||
props.setIsStamp(props.pos.isStamp);
|
|
||||||
} else if (
|
|
||||||
(props.isNeedSign && props.pos.type === "signature") ||
|
|
||||||
props.pos.type === "stamp"
|
|
||||||
) {
|
|
||||||
props.setIsSignPad(true);
|
|
||||||
props.setSignKey(props.pos.key);
|
|
||||||
props.setIsStamp(props.pos.isStamp);
|
|
||||||
} else if (props.isNeedSign && props.pos.type === "dropdown") {
|
|
||||||
props.setSignKey(props.pos.key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const widgetClickHandler = () => {
|
const widgetClickHandler = () => {
|
||||||
//The else condition opens the signature pad if it's a request signature flow and the user clicking is identified as a signer.
|
|
||||||
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
||||||
|
//condition to check in request signing flow user click on agree or not
|
||||||
if (props?.data?.signerObjId === props?.signerObjId && !props.isDragging) {
|
if (props?.data?.signerObjId === props?.signerObjId && !props.isDragging) {
|
||||||
if (!props.isAgree && !props.isSelfSign) {
|
if (!props.isAgree && !props.isSelfSign) {
|
||||||
props.setIsAgreeTour && props.setIsAgreeTour(true);
|
props.setIsAgreeTour && props.setIsAgreeTour(true);
|
||||||
@@ -297,7 +232,10 @@ function Placeholder(props) {
|
|||||||
handleWidgetIdandPopup();
|
handleWidgetIdandPopup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnClickPlaceholder = () => {
|
const handleOnClickPlaceholder = () => {
|
||||||
|
//'props.isDragging' variable is used to checking if user take any widget and try to drag then in that case
|
||||||
|
//onclick event call. so to prevent onclick and do not open unecessary modal open.
|
||||||
//condition only for request signing flow and self signing flow then apply one click copy sign url of previous drawn signature
|
//condition only for request signing flow and self signing flow then apply one click copy sign url of previous drawn signature
|
||||||
if (props.isApplyAll) {
|
if (props.isApplyAll) {
|
||||||
props.setRequestSignTour && props.setRequestSignTour(true);
|
props.setRequestSignTour && props.setRequestSignTour(true);
|
||||||
@@ -370,8 +308,8 @@ function Placeholder(props) {
|
|||||||
} else {
|
} else {
|
||||||
//The else condition is used to handle the case when the user clicks on a widget and open signature pad to draw sign
|
//The else condition is used to handle the case when the user clicks on a widget and open signature pad to draw sign
|
||||||
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
||||||
props.setWidgetType(props.pos.type);
|
|
||||||
handleWidgetIdandPopup();
|
handleWidgetIdandPopup();
|
||||||
|
handleGetDaynamicWH();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
|
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
|
||||||
@@ -414,8 +352,6 @@ function Placeholder(props) {
|
|||||||
props.setTempSignerId(props.uniqueId);
|
props.setTempSignerId(props.uniqueId);
|
||||||
props.setUniqueId(props?.data?.Id);
|
props.setUniqueId(props?.data?.Id);
|
||||||
}
|
}
|
||||||
props.setSignKey(props.pos.key);
|
|
||||||
props.setWidgetType(props.pos.type);
|
|
||||||
props.setCurrWidgetsDetails(props.pos);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
};
|
};
|
||||||
//function to set required state value onclick on widget's copy icon
|
//function to set required state value onclick on widget's copy icon
|
||||||
@@ -448,7 +384,7 @@ function Placeholder(props) {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
props.setIsPageCopy(true);
|
props.setIsPageCopy(true);
|
||||||
props.setSignKey(props.pos.key);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
} else {
|
} else {
|
||||||
//function to create new widget next to just widget
|
//function to create new widget next to just widget
|
||||||
handleCopyNextToWidget(
|
handleCopyNextToWidget(
|
||||||
@@ -462,18 +398,6 @@ function Placeholder(props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//function to save date and format after seleted new date in response field and after finish document it should be emebed new selected date instead of current date
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.pos.type === "date") {
|
|
||||||
const isDateChange = true;
|
|
||||||
const dateObj = {
|
|
||||||
date: startDate,
|
|
||||||
format: getDefaultFormat(props.pos?.options?.validation?.format)
|
|
||||||
};
|
|
||||||
handleSaveDate(dateObj, isDateChange); //function to save date and format in local array
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [startDate]);
|
|
||||||
//function to save date and format on local array onchange date and onclick format
|
//function to save date and format on local array onchange date and onclick format
|
||||||
const handleSaveDate = (data, isDateChange) => {
|
const handleSaveDate = (data, isDateChange) => {
|
||||||
let updateDate = data.date;
|
let updateDate = data.date;
|
||||||
@@ -499,8 +423,6 @@ function Placeholder(props) {
|
|||||||
props.data && props.data.Id,
|
props.data && props.data.Id,
|
||||||
false,
|
false,
|
||||||
data?.format,
|
data?.format,
|
||||||
null,
|
|
||||||
null,
|
|
||||||
props.fontSize || props.pos?.options?.fontSize || 12,
|
props.fontSize || props.pos?.options?.fontSize || 12,
|
||||||
props.fontColor || props.pos?.options?.fontColor || "black"
|
props.fontColor || props.pos?.options?.fontColor || "black"
|
||||||
);
|
);
|
||||||
@@ -609,7 +531,7 @@ function Placeholder(props) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setClickonWidget(props.pos);
|
setClickonWidget(props.pos);
|
||||||
if (props.data) {
|
if (props.data) {
|
||||||
props.setSignKey(props.pos.key);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
props.setUniqueId(props.data.Id);
|
props.setUniqueId(props.data.Id);
|
||||||
const checkIndex = props.xyPosition.findIndex(
|
const checkIndex = props.xyPosition.findIndex(
|
||||||
(data) => data.Id === props.data.Id
|
(data) => data.Id === props.data.Id
|
||||||
@@ -622,7 +544,7 @@ function Placeholder(props) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsDateModal(!isDateModal);
|
setIsDateModal(!isDateModal);
|
||||||
if (props.data) {
|
if (props.data) {
|
||||||
props.setSignKey(props.pos.key);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
props.setUniqueId(props.data.Id);
|
props.setUniqueId(props.data.Id);
|
||||||
const checkIndex = props.xyPosition.findIndex(
|
const checkIndex = props.xyPosition.findIndex(
|
||||||
(data) => data.Id === props.data.Id
|
(data) => data.Id === props.data.Id
|
||||||
@@ -659,7 +581,6 @@ function Placeholder(props) {
|
|||||||
//condition for signyour-self flow
|
//condition for signyour-self flow
|
||||||
else {
|
else {
|
||||||
props.handleDeleteSign(props.pos.key);
|
props.handleDeleteSign(props.pos.key);
|
||||||
props.setIsStamp(false);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
//for mobile and tablet touch event
|
//for mobile and tablet touch event
|
||||||
@@ -672,7 +593,6 @@ function Placeholder(props) {
|
|||||||
//condition for signyour-self flow
|
//condition for signyour-self flow
|
||||||
else {
|
else {
|
||||||
props.handleDeleteSign(props.pos.key);
|
props.handleDeleteSign(props.pos.key);
|
||||||
props.setIsStamp(false);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
|
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
|
||||||
@@ -767,43 +687,7 @@ function Placeholder(props) {
|
|||||||
return "all-scroll";
|
return "all-scroll";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleDragging = () => {
|
|
||||||
//condition for request signing flow
|
|
||||||
if (props.isNeedSign) {
|
|
||||||
//enable dragging functionality only if isAlllowModify true on tab that widget and hold 1sec
|
|
||||||
if (
|
|
||||||
props.isAlllowModify &&
|
|
||||||
props?.assignedWidgetId.includes(props.pos.key) &&
|
|
||||||
props.data?.signerObjId === props.signerObjId
|
|
||||||
) {
|
|
||||||
//if 'isTouchDevice' then handle dragging functionality conditionaly
|
|
||||||
if (isTouchDevice) {
|
|
||||||
return isDisableDragging;
|
|
||||||
} else {
|
|
||||||
//no need to handle dragging functionality it auto enable and working or mouse click devices
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
//condition when 'isAlllowModify' is true and user add new widgets then handle dragging functionality like signyourself flow
|
|
||||||
props.isAlllowModify &&
|
|
||||||
!props?.assignedWidgetId.includes(props.pos.key) &&
|
|
||||||
props.data?.signerObjId === props.signerObjId
|
|
||||||
) {
|
|
||||||
return !isDraggingEnabled;
|
|
||||||
} else {
|
|
||||||
//if 'isAlllowModify' is false then disbale dragging functionality
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} //dragging enable in placeholder,template and not text widget flow
|
|
||||||
else if (props.isPlaceholder && ![textWidget].includes(props.pos.type)) {
|
|
||||||
return false;
|
|
||||||
} //dragging depend on 'isDraggingEnabled' variable in self sign and signyourself flow
|
|
||||||
else if (isTouchDevice) {
|
|
||||||
return !isDraggingEnabled;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//function to handle widget background color
|
//function to handle widget background color
|
||||||
const handleBackground = () => {
|
const handleBackground = () => {
|
||||||
if (props.data) {
|
if (props.data) {
|
||||||
@@ -835,42 +719,38 @@ function Placeholder(props) {
|
|||||||
}
|
}
|
||||||
if (!props.isNeedSign || props.isAlllowModify) handleOnClickPlaceholder();
|
if (!props.isNeedSign || props.isAlllowModify) handleOnClickPlaceholder();
|
||||||
};
|
};
|
||||||
const handleTouchStart = () => {
|
|
||||||
clearTimeout(holdTimeout.current); // Ensure no previous timeouts are running
|
|
||||||
startTime.current = Date.now(); // Store touch start time
|
|
||||||
|
|
||||||
holdTimeout.current = setTimeout(() => {
|
|
||||||
//handlle vibration and tab any widget and hold for 1 sec then show border outside widget and then user can able to drag
|
|
||||||
if (isDisableDragging) {
|
|
||||||
if (
|
|
||||||
props.isNeedSign &&
|
|
||||||
props.isAlllowModify &&
|
|
||||||
props?.assignedWidgetId.includes(props.pos.key)
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
navigator.vibrate(200); // Vibrate for 200ms
|
|
||||||
} catch (e) {
|
|
||||||
console.log("error in navigator.vibrate", e);
|
|
||||||
}
|
|
||||||
setIsDisableDragging(false);
|
|
||||||
props.setSelectWidgetId(props.pos.key);
|
|
||||||
} else if (!props.isNeedSign) {
|
|
||||||
setIsDisableDragging(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000); // Hold for 1 second before vibrating
|
|
||||||
};
|
|
||||||
const fontSize = calculateFont(props.pos.options?.fontSize);
|
const fontSize = calculateFont(props.pos.options?.fontSize);
|
||||||
const fontColor = props.pos.options?.fontColor || "black";
|
const fontColor = props.pos.options?.fontColor || "black";
|
||||||
|
|
||||||
|
const handleDragging = () => {
|
||||||
|
//condition for request signing flow
|
||||||
|
if (props.isNeedSign) {
|
||||||
|
//enable dragging functionality only if isAlllowModify true on tab that widget and hold 1sec
|
||||||
|
if (
|
||||||
|
props.isAlllowModify &&
|
||||||
|
!props?.assignedWidgetId.includes(props.pos.key) &&
|
||||||
|
props.data?.signerObjId === props.signerObjId
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
//if 'isAlllowModify' is false then disbale dragging functionality
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Check if a text widget (prefill type) exists. Once the user enters a value and clicks outside or the widget becomes non-selectable, it should appear as plain text (just like embedded text in a document). When the user clicks on the text again, it should become editable. */}
|
{/* Check if a text widget (prefill type) exists. Once the user enters a value and clicks outside or the widget becomes non-selectable, it should appear as plain text (just like embedded text in a document). When the user clicks on the text again, it should become editable. */}
|
||||||
{props.pos?.options?.response &&
|
{props.pos?.options?.response &&
|
||||||
props.pos.key !== props.selectWidgetId &&
|
props.pos.key !== props?.currWidgetsDetails?.key &&
|
||||||
props.pos.type === textWidget ? (
|
props.pos.type === textWidget ? (
|
||||||
<span
|
<span
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.setSelectWidgetId && props.setSelectWidgetId(props.pos.key);
|
props.setCurrWidgetsDetails &&
|
||||||
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
fontFamily: "Arial, sans-serif",
|
fontFamily: "Arial, sans-serif",
|
||||||
@@ -919,7 +799,7 @@ function Placeholder(props) {
|
|||||||
: false
|
: false
|
||||||
: props.pos.type !== radioButtonWidget &&
|
: props.pos.type !== radioButtonWidget &&
|
||||||
props.pos.type !== "checkbox" &&
|
props.pos.type !== "checkbox" &&
|
||||||
props.pos.key === props.selectWidgetId &&
|
props.pos.key === props?.currWidgetsDetails?.key &&
|
||||||
true,
|
true,
|
||||||
bottomLeft: false,
|
bottomLeft: false,
|
||||||
topLeft: false
|
topLeft: false
|
||||||
@@ -932,7 +812,7 @@ function Placeholder(props) {
|
|||||||
cursor: getCursor(),
|
cursor: getCursor(),
|
||||||
zIndex:
|
zIndex:
|
||||||
props.pos.type === "date"
|
props.pos.type === "date"
|
||||||
? props.pos.key === props.selectWidgetId
|
? props.pos.key === props?.currWidgetsDetails?.key
|
||||||
? 99 + 1
|
? 99 + 1
|
||||||
: 99
|
: 99
|
||||||
: props?.pos?.zIndex
|
: props?.pos?.zIndex
|
||||||
@@ -943,7 +823,7 @@ function Placeholder(props) {
|
|||||||
background: handleBackground()
|
background: handleBackground()
|
||||||
}}
|
}}
|
||||||
onDrag={() => {
|
onDrag={() => {
|
||||||
setDraggingEnabled(true);
|
handleGetDaynamicWH();
|
||||||
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||||
}}
|
}}
|
||||||
size={{
|
size={{
|
||||||
@@ -961,7 +841,6 @@ function Placeholder(props) {
|
|||||||
minHeight={calculateFont(props.pos.options?.fontSize, true)}
|
minHeight={calculateFont(props.pos.options?.fontSize, true)}
|
||||||
maxHeight="auto"
|
maxHeight="auto"
|
||||||
onResizeStart={() => {
|
onResizeStart={() => {
|
||||||
setDraggingEnabled(true);
|
|
||||||
props.setIsResize && props.setIsResize(true);
|
props.setIsResize && props.setIsResize(true);
|
||||||
}}
|
}}
|
||||||
onResizeStop={(e, direction, ref) => {
|
onResizeStop={(e, direction, ref) => {
|
||||||
@@ -981,9 +860,7 @@ function Placeholder(props) {
|
|||||||
props.isResize
|
props.isResize
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
disableDragging={handleDragging()}
|
|
||||||
onDragStop={(event, dragElement) => {
|
onDragStop={(event, dragElement) => {
|
||||||
setIsDisableDragging(true);
|
|
||||||
props.handleStop &&
|
props.handleStop &&
|
||||||
props.handleStop(
|
props.handleStop(
|
||||||
event,
|
event,
|
||||||
@@ -1002,11 +879,9 @@ function Placeholder(props) {
|
|||||||
h: ref.offsetHeight / (props.scale * containerScale)
|
h: ref.offsetHeight / (props.scale * containerScale)
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
// onClick={() =>
|
disableDragging={handleDragging()}
|
||||||
// !props.isResize && !isMobile && handleOnClickPlaceholder()
|
|
||||||
// }
|
|
||||||
>
|
>
|
||||||
{props.pos.key === props.selectWidgetId &&
|
{props.pos.key === props?.currWidgetsDetails?.key &&
|
||||||
((props.isShowBorder &&
|
((props.isShowBorder &&
|
||||||
![radioButtonWidget, "checkbox"].includes(props.pos.type)) ||
|
![radioButtonWidget, "checkbox"].includes(props.pos.type)) ||
|
||||||
(props?.isAlllowModify &&
|
(props?.isAlllowModify &&
|
||||||
@@ -1033,22 +908,20 @@ function Placeholder(props) {
|
|||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
![radioButtonWidget, "checkbox"].includes(props.pos.type) &&
|
![radioButtonWidget, "checkbox"].includes(props.pos.type) &&
|
||||||
props.pos.key === props.selectWidgetId && <BorderResize />
|
props.pos.key === props?.currWidgetsDetails?.key && <BorderResize />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 1- Show a border if props.pos.key === props.selectWidgetId, indicating the current user's selected widget.
|
{/* 1- Show a border if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
|
||||||
2- If props.isShowBorder is true, display borders for all widgets.
|
2- If props.isShowBorder is true, display borders for all widgets.
|
||||||
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show borders:
|
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show borders:
|
||||||
1- When isAlllowModify is true, show borders.
|
1- When isAlllowModify is true, show borders.
|
||||||
2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
|
2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
|
||||||
*/}
|
*/}
|
||||||
{props.pos.key === props.selectWidgetId &&
|
{props.pos.key === props?.currWidgetsDetails?.key &&
|
||||||
(props.isShowBorder ||
|
(props.isShowBorder ||
|
||||||
!isDisableDragging ||
|
|
||||||
(props?.isAlllowModify &&
|
(props?.isAlllowModify &&
|
||||||
!props?.assignedWidgetId.includes(props.pos.key))) && (
|
!props?.assignedWidgetId.includes(props.pos.key))) && (
|
||||||
<PlaceholderBorder
|
<PlaceholderBorder
|
||||||
setDraggingEnabled={setDraggingEnabled}
|
|
||||||
pos={props.pos}
|
pos={props.pos}
|
||||||
isPlaceholder={props.isPlaceholder}
|
isPlaceholder={props.isPlaceholder}
|
||||||
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||||
@@ -1074,18 +947,18 @@ function Placeholder(props) {
|
|||||||
: props.posHeight(props.pos, props.isSignYourself),
|
: props.posHeight(props.pos, props.isSignYourself),
|
||||||
zIndex: "10"
|
zIndex: "10"
|
||||||
}}
|
}}
|
||||||
onTouchEnd={() => handleTouchEnd()}
|
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||||
onClick={() => handleOnClickPlaceholder()}
|
onClick={() => handleOnClickPlaceholder()}
|
||||||
onTouchStart={() => handleTouchStart()}
|
|
||||||
>
|
>
|
||||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
{props.pos.key === props?.currWidgetsDetails?.key && (
|
||||||
|
<PlaceholderIcon />
|
||||||
|
)}
|
||||||
<PlaceholderType
|
<PlaceholderType
|
||||||
pos={props.pos}
|
pos={props.pos}
|
||||||
xyPosition={props.xyPosition}
|
xyPosition={props.xyPosition}
|
||||||
index={props.index}
|
index={props.index}
|
||||||
setXyPosition={props.setXyPosition}
|
setXyPosition={props.setXyPosition}
|
||||||
data={props.data}
|
data={props.data}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
isShowDropdown={props?.isShowDropdown}
|
isShowDropdown={props?.isShowDropdown}
|
||||||
isPlaceholder={props.isPlaceholder}
|
isPlaceholder={props.isPlaceholder}
|
||||||
isSignYourself={props.isSignYourself}
|
isSignYourself={props.isSignYourself}
|
||||||
@@ -1096,8 +969,6 @@ function Placeholder(props) {
|
|||||||
isNeedSign={props.isNeedSign}
|
isNeedSign={props.isNeedSign}
|
||||||
setSelectDate={setSelectDate}
|
setSelectDate={setSelectDate}
|
||||||
selectDate={selectDate}
|
selectDate={selectDate}
|
||||||
setValidateAlert={props.setValidateAlert}
|
|
||||||
setStartDate={setStartDate}
|
|
||||||
startDate={startDate}
|
startDate={startDate}
|
||||||
handleSaveDate={handleSaveDate}
|
handleSaveDate={handleSaveDate}
|
||||||
xPos={props.xPos}
|
xPos={props.xPos}
|
||||||
-4
@@ -43,10 +43,6 @@ function PlaceholderBorder(props) {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(false)}
|
|
||||||
onTouchEnd={() =>
|
|
||||||
props.pos.type === textWidget && props?.setDraggingEnabled(false)
|
|
||||||
}
|
|
||||||
className="absolute inline-block w-[14px] h-[14px] border-[0.2px] overflow-hidden border-dashed"
|
className="absolute inline-block w-[14px] h-[14px] border-[0.2px] overflow-hidden border-dashed"
|
||||||
style={{
|
style={{
|
||||||
borderColor: themeColor,
|
borderColor: themeColor,
|
||||||
@@ -1,903 +0,0 @@
|
|||||||
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
|
||||||
import {
|
|
||||||
getMonth,
|
|
||||||
getYear,
|
|
||||||
onChangeHeightOfTextArea,
|
|
||||||
onChangeInput,
|
|
||||||
radioButtonWidget,
|
|
||||||
range,
|
|
||||||
textInputWidget,
|
|
||||||
textWidget
|
|
||||||
} from "../../constant/Utils";
|
|
||||||
import DatePicker from "react-datepicker";
|
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
|
||||||
import "../../styles/signature.css";
|
|
||||||
import RegexParser from "regex-parser";
|
|
||||||
import { emailRegex } from "../../constant/const";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
const textWidgetCls =
|
|
||||||
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
|
|
||||||
const selectWidgetCls =
|
|
||||||
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
|
|
||||||
const widgetCls =
|
|
||||||
"select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center";
|
|
||||||
function PlaceholderType(props) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const type = props?.pos?.type;
|
|
||||||
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
|
|
||||||
const [selectOption, setSelectOption] = useState("");
|
|
||||||
const [validatePlaceholder, setValidatePlaceholder] = useState("");
|
|
||||||
const inputRef = useRef(null);
|
|
||||||
const [textValue, setTextValue] = useState();
|
|
||||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
|
||||||
const [hint, setHint] = useState("");
|
|
||||||
const years = range(1950, getYear(new Date()) + 16, 1);
|
|
||||||
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
|
||||||
const fontColor = props.pos.options?.fontColor || "black";
|
|
||||||
const months = [
|
|
||||||
"January",
|
|
||||||
"February",
|
|
||||||
"March",
|
|
||||||
"April",
|
|
||||||
"May",
|
|
||||||
"June",
|
|
||||||
"July",
|
|
||||||
"August",
|
|
||||||
"September",
|
|
||||||
"October",
|
|
||||||
"November",
|
|
||||||
"December"
|
|
||||||
];
|
|
||||||
const textWidgetStyle = {
|
|
||||||
fontSize: fontSize,
|
|
||||||
color: fontColor,
|
|
||||||
fontFamily: "Arial, sans-serif",
|
|
||||||
overflow: "hidden",
|
|
||||||
textAlign: "start",
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
height: "100%"
|
|
||||||
};
|
|
||||||
const validateExpression = (regexValidation) => {
|
|
||||||
if (textValue && regexValidation) {
|
|
||||||
let regexObject = regexValidation;
|
|
||||||
if (props.pos?.options?.validation?.type === "regex") {
|
|
||||||
regexObject = RegexParser(regexValidation);
|
|
||||||
}
|
|
||||||
// new RegExp(regexValidation);
|
|
||||||
let isValidate = regexObject.test(textValue);
|
|
||||||
if (!isValidate) {
|
|
||||||
props?.setValidateAlert(true);
|
|
||||||
inputRef.current.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputBlur = () => {
|
|
||||||
const validateType = props.pos?.options?.validation?.type;
|
|
||||||
let regexValidation;
|
|
||||||
if (validateType && validateType !== "text") {
|
|
||||||
switch (validateType) {
|
|
||||||
case "email":
|
|
||||||
regexValidation = emailRegex;
|
|
||||||
validateExpression(regexValidation);
|
|
||||||
break;
|
|
||||||
case "number":
|
|
||||||
regexValidation = /^[0-9\s]*$/;
|
|
||||||
validateExpression(regexValidation);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
regexValidation = props.pos?.options?.validation?.pattern || "";
|
|
||||||
validateExpression(regexValidation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTextValid = (e) => {
|
|
||||||
const textInput = e.target.value;
|
|
||||||
setTextValue(textInput);
|
|
||||||
};
|
|
||||||
function checkRegularExpress(validateType) {
|
|
||||||
switch (validateType) {
|
|
||||||
case "email":
|
|
||||||
setValidatePlaceholder("demo@gmail.com");
|
|
||||||
break;
|
|
||||||
case "number":
|
|
||||||
setValidatePlaceholder("12345");
|
|
||||||
break;
|
|
||||||
case "text":
|
|
||||||
setValidatePlaceholder("please enter text");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
setValidatePlaceholder("please enter value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (type && type === "checkbox" && props.isNeedSign) {
|
|
||||||
const isDefaultValue = props.pos.options?.defaultValue;
|
|
||||||
if (isDefaultValue) {
|
|
||||||
setSelectedCheckbox(isDefaultValue);
|
|
||||||
}
|
|
||||||
} else if (props.pos?.options?.hint) {
|
|
||||||
setValidatePlaceholder(props.pos?.options.hint);
|
|
||||||
} else if (props.pos?.options?.validation?.type) {
|
|
||||||
checkRegularExpress(props.pos?.options?.validation?.type);
|
|
||||||
}
|
|
||||||
setTextValue(
|
|
||||||
props.pos?.options?.response
|
|
||||||
? props.pos?.options?.response
|
|
||||||
: props.pos?.options?.defaultValue
|
|
||||||
? props.pos?.options?.defaultValue
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
setSelectOption(
|
|
||||||
props.pos?.options?.response
|
|
||||||
? props.pos?.options?.response
|
|
||||||
: props.pos?.options?.defaultValue
|
|
||||||
? props.pos?.options?.defaultValue
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
["name", "email", "job title", "company"].includes(props.pos?.type) &&
|
|
||||||
props.isNeedSign &&
|
|
||||||
props.data?.signerObjId === props?.signerObjId
|
|
||||||
) {
|
|
||||||
const defaultData = props.pos?.options?.defaultValue;
|
|
||||||
if (defaultData) {
|
|
||||||
setTextValue(defaultData);
|
|
||||||
}
|
|
||||||
if (props.pos?.options?.hint) {
|
|
||||||
setHint(props.pos?.options.hint);
|
|
||||||
} else {
|
|
||||||
setHint(props.pos?.type);
|
|
||||||
}
|
|
||||||
} else if ([textInputWidget].includes(props.pos?.type)) {
|
|
||||||
const defaultData = props.pos?.options?.defaultValue;
|
|
||||||
if (defaultData) {
|
|
||||||
setTextValue(defaultData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [props.pos?.options?.defaultValue]);
|
|
||||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: fontSize,
|
|
||||||
color: fontColor,
|
|
||||||
fontFamily: "Arial, sans-serif"
|
|
||||||
}}
|
|
||||||
className={`${selectWidgetCls} overflow-hidden`}
|
|
||||||
onClick={onClick}
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
{value}
|
|
||||||
<i className="fa-light fa-calendar ml-[5px]"></i>
|
|
||||||
</div>
|
|
||||||
));
|
|
||||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
["name", "email", "job title", "company"].includes(type) &&
|
|
||||||
props.isNeedSign &&
|
|
||||||
props.data?.signerObjId === props.signerObjId
|
|
||||||
) {
|
|
||||||
const isDefault = true;
|
|
||||||
const senderUser = localStorage.getItem(`Extand_Class`);
|
|
||||||
const jsonSender = JSON.parse(senderUser);
|
|
||||||
onChangeInput(
|
|
||||||
jsonSender && jsonSender[0],
|
|
||||||
null,
|
|
||||||
props.xyPosition,
|
|
||||||
null,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data.Id,
|
|
||||||
isDefault
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [type]);
|
|
||||||
//function for show checked checkbox
|
|
||||||
const selectCheckbox = (ind) => {
|
|
||||||
const res = props.pos.options?.response;
|
|
||||||
const defaultCheck = props.pos.options?.defaultValue;
|
|
||||||
if (res && res?.length > 0) {
|
|
||||||
const isSelectIndex = res.indexOf(ind);
|
|
||||||
if (isSelectIndex > -1) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
} else if (defaultCheck) {
|
|
||||||
const isSelectIndex = defaultCheck.indexOf(ind);
|
|
||||||
if (isSelectIndex > -1) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRadioCheck = (data) => {
|
|
||||||
const defaultData = props.pos.options?.defaultValue;
|
|
||||||
if (textValue === data) {
|
|
||||||
return true;
|
|
||||||
} else if (defaultData === data) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//function for set checked and unchecked value of checkbox
|
|
||||||
const handleCheckboxValue = (isChecked, ind) => {
|
|
||||||
let updateSelectedCheckbox = [],
|
|
||||||
checkedList;
|
|
||||||
let isDefaultValue, isDefaultEmpty;
|
|
||||||
if (type === "checkbox") {
|
|
||||||
updateSelectedCheckbox = selectedCheckbox ? selectedCheckbox : [];
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
updateSelectedCheckbox.push(ind);
|
|
||||||
setSelectedCheckbox(updateSelectedCheckbox);
|
|
||||||
} else {
|
|
||||||
checkedList = selectedCheckbox.filter((data) => data !== ind);
|
|
||||||
setSelectedCheckbox(checkedList);
|
|
||||||
}
|
|
||||||
if (props.isNeedSign) {
|
|
||||||
isDefaultValue = props.pos.options?.defaultValue;
|
|
||||||
}
|
|
||||||
if (isDefaultValue && isDefaultValue.length > 0) {
|
|
||||||
isDefaultEmpty = true;
|
|
||||||
}
|
|
||||||
onChangeInput(
|
|
||||||
checkedList ? checkedList : updateSelectedCheckbox,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data.Id,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
isDefaultEmpty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//function to handle select radio widget and set value seletced by user
|
|
||||||
const handleCheckRadio = (isChecked, data) => {
|
|
||||||
let isDefaultValue,
|
|
||||||
isDefaultEmpty,
|
|
||||||
isRadio = true;
|
|
||||||
if (props.isNeedSign) {
|
|
||||||
isDefaultValue = props.pos.options?.defaultValue;
|
|
||||||
}
|
|
||||||
if (isDefaultValue) {
|
|
||||||
isDefaultEmpty = true;
|
|
||||||
}
|
|
||||||
if (isChecked) {
|
|
||||||
setTextValue(data);
|
|
||||||
} else {
|
|
||||||
setTextValue("");
|
|
||||||
}
|
|
||||||
onChangeInput(
|
|
||||||
data,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data.Id,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
isDefaultEmpty,
|
|
||||||
isRadio
|
|
||||||
);
|
|
||||||
};
|
|
||||||
//function to set onchange date
|
|
||||||
const handleOnDateChange = (date) => {
|
|
||||||
props.setStartDate(date);
|
|
||||||
};
|
|
||||||
//handle height on enter press in text area
|
|
||||||
const handleEnterPress = (e) => {
|
|
||||||
const height = 18;
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
//function to save height of text area
|
|
||||||
onChangeHeightOfTextArea(
|
|
||||||
height,
|
|
||||||
props.pos.type,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
switch (type) {
|
|
||||||
case "signature":
|
|
||||||
return props.pos.SignUrl ? (
|
|
||||||
<img
|
|
||||||
alt="signature"
|
|
||||||
draggable="false"
|
|
||||||
src={props.pos.SignUrl}
|
|
||||||
className="w-full h-full select-none-cls "
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className={widgetCls}>
|
|
||||||
{props.pos.type && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: props.pos
|
|
||||||
? props.calculateFontsize(props.pos)
|
|
||||||
: "11px"
|
|
||||||
}}
|
|
||||||
className="font-medium"
|
|
||||||
>
|
|
||||||
{props.isNeedSign
|
|
||||||
? props.pos?.options?.hint || widgetTypeTranslation
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "stamp":
|
|
||||||
return props.pos.SignUrl ? (
|
|
||||||
<img
|
|
||||||
alt="stamp"
|
|
||||||
draggable="false"
|
|
||||||
src={props.pos.SignUrl}
|
|
||||||
className="w-full h-full select-none-cls"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className={widgetCls}>
|
|
||||||
{props.pos.type && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: props.pos
|
|
||||||
? props.calculateFontsize(props.pos)
|
|
||||||
: "11px"
|
|
||||||
}}
|
|
||||||
className="font-medium"
|
|
||||||
>
|
|
||||||
{props.isNeedSign
|
|
||||||
? props.pos?.options?.hint || widgetTypeTranslation
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "checkbox":
|
|
||||||
return (
|
|
||||||
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
|
||||||
{props.pos.options?.values?.map((data, ind) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={ind}
|
|
||||||
className="select-none-cls flex items-center text-center gap-0.5"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id={`checkbox-${props.pos.key + ind}`}
|
|
||||||
style={{ width: fontSize, height: fontSize }}
|
|
||||||
className={`${
|
|
||||||
ind === 0 ? "mt-0" : "mt-[5px]"
|
|
||||||
} flex justify-center op-checkbox rounded-[1px] `}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
disabled={
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
}
|
|
||||||
type="checkbox"
|
|
||||||
checked={selectCheckbox(ind)}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.checked) {
|
|
||||||
if (!props.isPlaceholder) {
|
|
||||||
const maxRequired =
|
|
||||||
props.pos.options?.validation?.maxRequiredCount;
|
|
||||||
const maxCountInt =
|
|
||||||
maxRequired && parseInt(maxRequired);
|
|
||||||
|
|
||||||
if (maxCountInt > 0) {
|
|
||||||
if (
|
|
||||||
selectedCheckbox &&
|
|
||||||
selectedCheckbox?.length <= maxCountInt - 1
|
|
||||||
) {
|
|
||||||
handleCheckboxValue(e.target.checked, ind);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleCheckboxValue(e.target.checked, ind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleCheckboxValue(e.target.checked, ind);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!props.pos.options?.isHideLabel && (
|
|
||||||
<label
|
|
||||||
htmlFor={`checkbox-${props.pos.key + ind}`}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
className="text-xs mb-0 text-center"
|
|
||||||
>
|
|
||||||
{data}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case textInputWidget:
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<textarea
|
|
||||||
ref={inputRef}
|
|
||||||
placeholder={validatePlaceholder || t("widgets-name.text")}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={handleEnterPress}
|
|
||||||
value={textValue}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTextValue(e.target.value);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
? " bg-black/68 select-none "
|
|
||||||
: "" + textWidgetCls
|
|
||||||
}`}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
disabled={
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
}
|
|
||||||
cols="50"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div style={textWidgetStyle} className="select-none-cls">
|
|
||||||
<span>{textValue || widgetTypeTranslation}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "dropdown":
|
|
||||||
return props.data?.signerObjId === props.signerObjId ? (
|
|
||||||
<select
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
className={`${
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
? " disabled:bg-inherit select-none "
|
|
||||||
: "" + `${selectWidgetCls} text-[12px] bg-inherit`
|
|
||||||
}`}
|
|
||||||
id="myDropdown"
|
|
||||||
value={selectOption}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSelectOption(e.target.value);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
disabled={
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{/* Default/Title option */}
|
|
||||||
<option
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
value=""
|
|
||||||
disabled
|
|
||||||
hidden
|
|
||||||
>
|
|
||||||
{props?.pos?.options?.name}
|
|
||||||
</option>
|
|
||||||
|
|
||||||
{props.pos?.options?.values?.map((data, ind) => {
|
|
||||||
return (
|
|
||||||
<option
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
key={ind}
|
|
||||||
value={data}
|
|
||||||
>
|
|
||||||
{data}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={textWidgetStyle}
|
|
||||||
className="select-none-cls flex justify-between items-center"
|
|
||||||
>
|
|
||||||
{props.pos?.options?.name
|
|
||||||
? props.pos.options.name
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
<i className="fa-light fa-circle-chevron-down mr-1 "></i>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "initials":
|
|
||||||
return props.pos.SignUrl ? (
|
|
||||||
<img
|
|
||||||
alt="initials"
|
|
||||||
draggable="false"
|
|
||||||
src={props.pos.SignUrl}
|
|
||||||
className="w-full h-full select-none-cls"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className={widgetCls}>
|
|
||||||
{props.pos.type && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: props.pos
|
|
||||||
? props.calculateFontsize(props.pos)
|
|
||||||
: "11px"
|
|
||||||
}}
|
|
||||||
className="font-medium text-center"
|
|
||||||
>
|
|
||||||
{props.isNeedSign
|
|
||||||
? props.pos?.options?.hint || widgetTypeTranslation
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "name":
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<textarea
|
|
||||||
ref={inputRef}
|
|
||||||
placeholder={hint || widgetTypeTranslation}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={handleEnterPress}
|
|
||||||
value={textValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
const isDefault = false;
|
|
||||||
handleTextValid(e);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
isDefault
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={textWidgetCls}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
cols="50"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="flex h-full select-none-cls" style={textWidgetStyle}>
|
|
||||||
<span>{widgetTypeTranslation}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "company":
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<textarea
|
|
||||||
ref={inputRef}
|
|
||||||
placeholder={hint || widgetTypeTranslation}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={handleEnterPress}
|
|
||||||
value={textValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
handleTextValid(e);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={textWidgetCls}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
cols="50"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div style={textWidgetStyle} className="select-none-cls">
|
|
||||||
<span>{widgetTypeTranslation}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "job title":
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<textarea
|
|
||||||
ref={inputRef}
|
|
||||||
placeholder={hint || widgetTypeTranslation}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={handleEnterPress}
|
|
||||||
value={textValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
handleTextValid(e);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={textWidgetCls}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
cols="50"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div style={textWidgetStyle} className="select-none-cls">
|
|
||||||
<span>{widgetTypeTranslation}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "date":
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<DatePicker
|
|
||||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
|
||||||
<div className="flex justify-start ml-2 ">
|
|
||||||
<select
|
|
||||||
className="bg-transparent outline-none"
|
|
||||||
value={months[getMonth(date)]}
|
|
||||||
onChange={({ target: { value } }) =>
|
|
||||||
changeMonth(months.indexOf(value))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{months.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<select
|
|
||||||
className="bg-transparent outline-none"
|
|
||||||
value={getYear(date)}
|
|
||||||
onChange={({ target: { value } }) => changeYear(value)}
|
|
||||||
>
|
|
||||||
{years.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
disabled={
|
|
||||||
props.isPlaceholder ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId !== props.signerObjId)
|
|
||||||
}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
closeOnScroll={true}
|
|
||||||
className={`${selectWidgetCls} outline-[#007bff]`}
|
|
||||||
selected={props?.startDate}
|
|
||||||
onChange={(date) => handleOnDateChange(date)}
|
|
||||||
popperPlacement="top-end"
|
|
||||||
customInput={<ExampleCustomInput />}
|
|
||||||
dateFormat={
|
|
||||||
props.selectDate
|
|
||||||
? props.selectDate?.format
|
|
||||||
: props.pos?.options?.validation?.format
|
|
||||||
? props.pos?.options?.validation?.format
|
|
||||||
: "MM/dd/yyyy"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={textWidgetStyle}
|
|
||||||
className="select-none-cls overflow-hidden"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{props.selectDate
|
|
||||||
? props.selectDate?.format
|
|
||||||
: props.pos?.options?.validation?.format
|
|
||||||
? props.pos?.options?.validation?.format
|
|
||||||
: "MM/dd/yyyy"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "image":
|
|
||||||
return props.pos.SignUrl ? (
|
|
||||||
<img
|
|
||||||
alt="image"
|
|
||||||
draggable="false"
|
|
||||||
src={props.pos.SignUrl}
|
|
||||||
className="w-full h-full select-none-cls"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className={widgetCls}>
|
|
||||||
{props.pos.type && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: props.pos
|
|
||||||
? props.calculateFontsize(props.pos)
|
|
||||||
: "11px"
|
|
||||||
}}
|
|
||||||
className="font-medium text-center"
|
|
||||||
>
|
|
||||||
{props.isNeedSign
|
|
||||||
? props.pos?.options?.hint || widgetTypeTranslation
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "email":
|
|
||||||
return props.isSignYourself ||
|
|
||||||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
|
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
|
||||||
<textarea
|
|
||||||
ref={inputRef}
|
|
||||||
placeholder={hint || widgetTypeTranslation}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
// Prevent new line on Enter key press
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={textValue}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
|
||||||
handleTextValid(e);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={textWidgetCls}
|
|
||||||
style={{
|
|
||||||
fontSize: fontSize,
|
|
||||||
color: fontColor,
|
|
||||||
fontFamily: "Arial, sans-serif"
|
|
||||||
}}
|
|
||||||
cols="1"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div style={textWidgetStyle} className="select-none-cls">
|
|
||||||
<span>{widgetTypeTranslation}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case radioButtonWidget:
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{props.pos.options?.values.map((data, ind) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={ind}
|
|
||||||
className="select-none-cls flex items-center text-center gap-0.5"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id={`radio-${props.pos.key + ind}`}
|
|
||||||
style={{
|
|
||||||
width: fontSize,
|
|
||||||
height: fontSize,
|
|
||||||
marginTop: ind > 0 ? "10px" : "0px"
|
|
||||||
}}
|
|
||||||
className={`flex justify-center op-radio`}
|
|
||||||
type="radio"
|
|
||||||
disabled={
|
|
||||||
props.isNeedSign &&
|
|
||||||
(props.pos.options?.isReadOnly ||
|
|
||||||
props.data?.signerObjId !== props.signerObjId)
|
|
||||||
}
|
|
||||||
checked={handleRadioCheck(data)}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (!props.isPlaceholder) {
|
|
||||||
handleCheckRadio(e.target.checked, data);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!props.pos.options?.isHideLabel && (
|
|
||||||
<label
|
|
||||||
htmlFor={`radio-${props.pos.key + ind}`}
|
|
||||||
style={{ fontSize: fontSize, color: fontColor }}
|
|
||||||
className="text-xs mb-0"
|
|
||||||
>
|
|
||||||
{data}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case textWidget:
|
|
||||||
return (
|
|
||||||
<textarea
|
|
||||||
placeholder={t("widgets-name.text")}
|
|
||||||
rows={1}
|
|
||||||
onKeyDown={handleEnterPress}
|
|
||||||
value={textValue}
|
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTextValue(e.target.value);
|
|
||||||
onChangeInput(
|
|
||||||
e.target.value,
|
|
||||||
props.pos.key,
|
|
||||||
props.xyPosition,
|
|
||||||
props.index,
|
|
||||||
props.setXyPosition,
|
|
||||||
props.data && props.data?.Id,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
className={textWidgetCls}
|
|
||||||
style={{
|
|
||||||
fontFamily: "Arial, sans-serif",
|
|
||||||
fontSize: fontSize,
|
|
||||||
color: fontColor
|
|
||||||
}}
|
|
||||||
cols="50"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return props.pos.SignUrl ? (
|
|
||||||
<div className="pointer-events-none">
|
|
||||||
<img
|
|
||||||
alt="image"
|
|
||||||
draggable="false"
|
|
||||||
src={props.pos.SignUrl}
|
|
||||||
className="w-full h-full "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={widgetCls}>
|
|
||||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
|
||||||
{props.pos.type && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: props.pos
|
|
||||||
? props.calculateFontsize(props.pos)
|
|
||||||
: "11px"
|
|
||||||
}}
|
|
||||||
className="font-medium"
|
|
||||||
>
|
|
||||||
{props.isNeedSign
|
|
||||||
? props.pos?.options?.hint || widgetTypeTranslation
|
|
||||||
: widgetTypeTranslation}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PlaceholderType;
|
|
||||||
@@ -0,0 +1,528 @@
|
|||||||
|
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||||
|
import {
|
||||||
|
getMonth,
|
||||||
|
getYear,
|
||||||
|
radioButtonWidget,
|
||||||
|
textInputWidget,
|
||||||
|
textWidget,
|
||||||
|
months,
|
||||||
|
years,
|
||||||
|
selectCheckbox,
|
||||||
|
checkRegularExpress
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import DatePicker from "react-datepicker";
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
import "../../styles/signature.css";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
const textWidgetCls =
|
||||||
|
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
|
||||||
|
const selectWidgetCls =
|
||||||
|
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
|
||||||
|
const widgetCls =
|
||||||
|
"select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center";
|
||||||
|
function PlaceholderType(props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const type = props?.pos?.type;
|
||||||
|
const iswidgetEnable =
|
||||||
|
props.isSignYourself ||
|
||||||
|
((props.isSelfSign || props.isNeedSign) &&
|
||||||
|
props.data?.signerObjId === props.signerObjId);
|
||||||
|
const widgetData =
|
||||||
|
props.pos?.options?.defaultValue || props.pos?.options?.response;
|
||||||
|
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
const [widgetValue, setwidgetValue] = useState();
|
||||||
|
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||||
|
const [hint, setHint] = useState("");
|
||||||
|
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||||
|
const fontColor = props.pos.options?.fontColor || "black";
|
||||||
|
const textWidgetStyle = {
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
fontFamily: "Arial, sans-serif",
|
||||||
|
overflow: "hidden",
|
||||||
|
textAlign: "start",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
height: "100%"
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type !== "date") {
|
||||||
|
if (type && type === "checkbox") {
|
||||||
|
setSelectedCheckbox(
|
||||||
|
props?.pos?.options?.response ||
|
||||||
|
props?.pos?.options?.defaultValue ||
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (widgetData) {
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
setHint(props.pos?.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [props.pos]);
|
||||||
|
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
fontFamily: "Arial, sans-serif"
|
||||||
|
}}
|
||||||
|
className={`${selectWidgetCls} overflow-hidden`}
|
||||||
|
onClick={onClick}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
<i className="fa-light fa-calendar ml-[5px]"></i>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||||
|
|
||||||
|
const handleRadioCheck = (data) => {
|
||||||
|
const defaultData = props.pos.options?.defaultValue;
|
||||||
|
if (widgetValue === data) {
|
||||||
|
return true;
|
||||||
|
} else if (defaultData === data) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle height on enter press in text area
|
||||||
|
// const handleEnterPress = (e) => {
|
||||||
|
// const height = 18;
|
||||||
|
// if (e.key === "Enter") {
|
||||||
|
// //function to save height of text area
|
||||||
|
// onChangeHeightOfTextArea(
|
||||||
|
// height,
|
||||||
|
// props.pos.type,
|
||||||
|
// props.pos.key,
|
||||||
|
// props.xyPosition,
|
||||||
|
// props.index,
|
||||||
|
// props.setXyPosition,
|
||||||
|
// props.data && props.data?.Id
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
switch (type) {
|
||||||
|
case "signature":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="signature"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
className="w-full h-full select-none-cls "
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={widgetCls}>
|
||||||
|
{props.pos.type && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: props.pos
|
||||||
|
? props.calculateFontsize(props.pos)
|
||||||
|
: "11px"
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
{hint || widgetTypeTranslation}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "stamp":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="stamp"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
className="w-full h-full select-none-cls"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={widgetCls}>
|
||||||
|
{props.pos.type && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: props.pos
|
||||||
|
? props.calculateFontsize(props.pos)
|
||||||
|
: "11px"
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
{hint || widgetTypeTranslation}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "checkbox":
|
||||||
|
return (
|
||||||
|
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
||||||
|
{props.pos.options?.values?.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={ind}
|
||||||
|
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id={`checkbox-${props.pos.key + ind}`}
|
||||||
|
style={{ width: fontSize, height: fontSize }}
|
||||||
|
className={`${
|
||||||
|
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||||
|
} flex justify-center op-checkbox rounded-[1px] `}
|
||||||
|
disabled={
|
||||||
|
props.isNeedSign &&
|
||||||
|
(props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
type="checkbox"
|
||||||
|
readOnly
|
||||||
|
checked={!!selectCheckbox(ind, selectedCheckbox)}
|
||||||
|
/>
|
||||||
|
{!props.pos.options?.isHideLabel && (
|
||||||
|
<label
|
||||||
|
htmlFor={`checkbox-${props.pos.key + ind}`}
|
||||||
|
style={{ fontSize: fontSize, color: fontColor }}
|
||||||
|
className="text-xs mb-0 text-center"
|
||||||
|
>
|
||||||
|
{data}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case textInputWidget:
|
||||||
|
return props.isSignYourself || iswidgetEnable ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={hint || t("widgets-name.text")}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={`${
|
||||||
|
props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId
|
||||||
|
? "select-none"
|
||||||
|
: textWidgetCls
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
background: props.data?.blockColor,
|
||||||
|
pointerEvents: "none"
|
||||||
|
}}
|
||||||
|
readOnly
|
||||||
|
disabled={
|
||||||
|
props.isNeedSign &&
|
||||||
|
(props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={textWidgetStyle} className="select-none-cls">
|
||||||
|
<span>{hint || widgetTypeTranslation}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "dropdown":
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={textWidgetStyle}
|
||||||
|
className="select-none-cls flex justify-between items-center"
|
||||||
|
>
|
||||||
|
{widgetData || hint || widgetTypeTranslation}
|
||||||
|
<i className="fa-light fa-circle-chevron-down mr-1 "></i>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "initials":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="initials"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
className="w-full h-full select-none-cls"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={widgetCls}>
|
||||||
|
{props.pos.type && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: props.pos
|
||||||
|
? props.calculateFontsize(props.pos)
|
||||||
|
: "11px"
|
||||||
|
}}
|
||||||
|
className="font-medium text-center"
|
||||||
|
>
|
||||||
|
{hint || widgetTypeTranslation}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "name":
|
||||||
|
return iswidgetEnable ? (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={hint || widgetTypeTranslation}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={textWidgetCls}
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
pointerEvents: "none"
|
||||||
|
}}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full select-none-cls" style={textWidgetStyle}>
|
||||||
|
<span> {props.pos?.options?.hint || widgetTypeTranslation}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "company":
|
||||||
|
return iswidgetEnable ? (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={hint || widgetTypeTranslation}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={textWidgetCls}
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
pointerEvents: "none"
|
||||||
|
}}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={textWidgetStyle} className="select-none-cls">
|
||||||
|
<span>{hint || widgetTypeTranslation}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "job title":
|
||||||
|
return iswidgetEnable ? (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={hint || widgetTypeTranslation}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={textWidgetCls}
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
pointerEvents: "none"
|
||||||
|
}}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={textWidgetStyle} className="select-none-cls">
|
||||||
|
<span>{hint || widgetTypeTranslation}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "date":
|
||||||
|
return iswidgetEnable ? (
|
||||||
|
<DatePicker
|
||||||
|
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||||
|
<div className="flex justify-start ml-2 ">
|
||||||
|
<select
|
||||||
|
className="bg-transparent outline-none"
|
||||||
|
value={months[getMonth(date)]}
|
||||||
|
onChange={({ target: { value } }) =>
|
||||||
|
changeMonth(months.indexOf(value))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{months.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className="bg-transparent outline-none"
|
||||||
|
value={getYear(date)}
|
||||||
|
onChange={({ target: { value } }) => changeYear(value)}
|
||||||
|
>
|
||||||
|
{years.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
disabled={true}
|
||||||
|
closeOnScroll={true}
|
||||||
|
className={`${selectWidgetCls} outline-[#007bff]`}
|
||||||
|
selected={props?.startDate}
|
||||||
|
popperPlacement="top-end"
|
||||||
|
customInput={<ExampleCustomInput />}
|
||||||
|
dateFormat={
|
||||||
|
props.selectDate
|
||||||
|
? props.selectDate?.format
|
||||||
|
: props.pos?.options?.validation?.format
|
||||||
|
? props.pos?.options?.validation?.format
|
||||||
|
: "MM/dd/yyyy"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={textWidgetStyle}
|
||||||
|
className="select-none-cls overflow-hidden"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{props.selectDate
|
||||||
|
? props.selectDate?.format
|
||||||
|
: props.pos?.options?.validation?.format
|
||||||
|
? props.pos?.options?.validation?.format
|
||||||
|
: "MM/dd/yyyy"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "image":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="image"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
className="w-full h-full select-none-cls"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={widgetCls}>
|
||||||
|
{props.pos.type && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: props.pos
|
||||||
|
? props.calculateFontsize(props.pos)
|
||||||
|
: "11px"
|
||||||
|
}}
|
||||||
|
className="font-medium text-center"
|
||||||
|
>
|
||||||
|
{hint || widgetTypeTranslation}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "email":
|
||||||
|
return iswidgetEnable ? (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={hint || widgetTypeTranslation}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={textWidgetCls}
|
||||||
|
style={{
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor,
|
||||||
|
fontFamily: "Arial, sans-serif",
|
||||||
|
pointerEvents: "none"
|
||||||
|
}}
|
||||||
|
disabled
|
||||||
|
cols="1"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={textWidgetStyle} className="select-none-cls">
|
||||||
|
<span>{hint || widgetTypeTranslation}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case radioButtonWidget:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.pos.options?.values.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={ind}
|
||||||
|
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
readOnly
|
||||||
|
id={`radio-${props.pos.key + ind}`}
|
||||||
|
style={{
|
||||||
|
width: fontSize,
|
||||||
|
height: fontSize,
|
||||||
|
marginTop: ind > 0 ? "10px" : "0px"
|
||||||
|
}}
|
||||||
|
className={`op-radio rounded-full border- border-black appearance-none bg-white inline-block align-middle relative ${
|
||||||
|
handleRadioCheck(data) ? "checked-radio" : ""
|
||||||
|
}`}
|
||||||
|
type="radio"
|
||||||
|
disabled={
|
||||||
|
props.isNeedSign &&
|
||||||
|
(props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
checked={handleRadioCheck(data)}
|
||||||
|
/>
|
||||||
|
{!props.pos.options?.isHideLabel && (
|
||||||
|
<label
|
||||||
|
htmlFor={`radio-${props.pos.key + ind}`}
|
||||||
|
style={{ fontSize: fontSize, color: fontColor }}
|
||||||
|
className="text-xs mb-0"
|
||||||
|
>
|
||||||
|
{data}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case textWidget:
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
readOnly
|
||||||
|
placeholder={t("widgets-name.text")}
|
||||||
|
rows={1}
|
||||||
|
value={widgetValue}
|
||||||
|
className={textWidgetCls}
|
||||||
|
style={{
|
||||||
|
fontFamily: "Arial, sans-serif",
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor
|
||||||
|
}}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<div className="pointer-events-none">
|
||||||
|
<img
|
||||||
|
alt="image"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
className="w-full h-full "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={widgetCls}>
|
||||||
|
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||||
|
{props.pos.type && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: props.pos
|
||||||
|
? props.calculateFontsize(props.pos)
|
||||||
|
: "11px"
|
||||||
|
}}
|
||||||
|
className="font-medium"
|
||||||
|
>
|
||||||
|
{hint || widgetTypeTranslation}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlaceholderType;
|
||||||
+19
-46
@@ -95,15 +95,12 @@ function RenderPdf(props) {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={key}>
|
<React.Fragment key={key}>
|
||||||
{placeData.pageNumber === props.pageNumber &&
|
{placeData.pageNumber === props.pageNumber &&
|
||||||
placeData.pos.map((pos) => {
|
placeData.pos.map((pos, ind) => {
|
||||||
return (
|
return (
|
||||||
pos && (
|
pos && (
|
||||||
<React.Fragment key={pos.key}>
|
<React.Fragment key={ind}>
|
||||||
<Placeholder
|
<Placeholder
|
||||||
pos={pos}
|
pos={pos}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
setIsSignPad={props.setIsSignPad}
|
|
||||||
setIsStamp={props.setIsStamp}
|
|
||||||
handleSignYourselfImageResize={handleImageResize}
|
handleSignYourselfImageResize={handleImageResize}
|
||||||
index={props.pageNumber}
|
index={props.pageNumber}
|
||||||
xyPosition={props.signerPos}
|
xyPosition={props.signerPos}
|
||||||
@@ -121,11 +118,7 @@ function RenderPdf(props) {
|
|||||||
posHeight={posHeight}
|
posHeight={posHeight}
|
||||||
isDragging={props.isDragging}
|
isDragging={props.isDragging}
|
||||||
pdfDetails={props.pdfDetails}
|
pdfDetails={props.pdfDetails}
|
||||||
setIsInitial={props.setIsInitial}
|
|
||||||
setValidateAlert={props.setValidateAlert}
|
|
||||||
unSignedWidgetId={props.unSignedWidgetId}
|
unSignedWidgetId={props.unSignedWidgetId}
|
||||||
setSelectWidgetId={props.setSelectWidgetId}
|
|
||||||
selectWidgetId={props.selectWidgetId}
|
|
||||||
setCurrWidgetsDetails={props.setCurrWidgetsDetails}
|
setCurrWidgetsDetails={props.setCurrWidgetsDetails}
|
||||||
uniqueId={props.uniqueId}
|
uniqueId={props.uniqueId}
|
||||||
scale={props.scale}
|
scale={props.scale}
|
||||||
@@ -139,7 +132,6 @@ function RenderPdf(props) {
|
|||||||
isAgree={props.isAgree}
|
isAgree={props.isAgree}
|
||||||
handleTabDrag={props.handleTabDrag}
|
handleTabDrag={props.handleTabDrag}
|
||||||
handleStop={props.handleStop}
|
handleStop={props.handleStop}
|
||||||
setWidgetType={props.setWidgetType}
|
|
||||||
setUniqueId={props.setUniqueId}
|
setUniqueId={props.setUniqueId}
|
||||||
setIsSelectId={props.setIsSelectId}
|
setIsSelectId={props.setIsSelectId}
|
||||||
handleDeleteSign={props.handleDeleteSign}
|
handleDeleteSign={props.handleDeleteSign}
|
||||||
@@ -156,6 +148,8 @@ function RenderPdf(props) {
|
|||||||
setFontColor={props.setFontColor}
|
setFontColor={props.setFontColor}
|
||||||
setRequestSignTour={props.setRequestSignTour}
|
setRequestSignTour={props.setRequestSignTour}
|
||||||
calculateFontsize={calculateFontsize}
|
calculateFontsize={calculateFontsize}
|
||||||
|
currWidgetsDetails={props?.currWidgetsDetails}
|
||||||
|
setTempSignerId={props.setTempSignerId}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
@@ -241,11 +235,9 @@ function RenderPdf(props) {
|
|||||||
<Placeholder
|
<Placeholder
|
||||||
pos={pos}
|
pos={pos}
|
||||||
setIsPageCopy={props.setIsPageCopy}
|
setIsPageCopy={props.setIsPageCopy}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
handleDeleteSign={
|
handleDeleteSign={
|
||||||
props.handleDeleteSign
|
props.handleDeleteSign
|
||||||
}
|
}
|
||||||
setIsStamp={props.setIsStamp}
|
|
||||||
handleTabDrag={props.handleTabDrag}
|
handleTabDrag={props.handleTabDrag}
|
||||||
handleStop={props.handleStop}
|
handleStop={props.handleStop}
|
||||||
handleSignYourselfImageResize={
|
handleSignYourselfImageResize={
|
||||||
@@ -270,18 +262,11 @@ function RenderPdf(props) {
|
|||||||
posHeight={posHeight}
|
posHeight={posHeight}
|
||||||
isDragging={props.isDragging}
|
isDragging={props.isDragging}
|
||||||
setIsValidate={props.setIsValidate}
|
setIsValidate={props.setIsValidate}
|
||||||
setWidgetType={props.setWidgetType}
|
|
||||||
setIsRadio={props.setIsRadio}
|
setIsRadio={props.setIsRadio}
|
||||||
setIsCheckbox={props.setIsCheckbox}
|
setIsCheckbox={props.setIsCheckbox}
|
||||||
setCurrWidgetsDetails={
|
setCurrWidgetsDetails={
|
||||||
props.setCurrWidgetsDetails
|
props.setCurrWidgetsDetails
|
||||||
}
|
}
|
||||||
setSelectWidgetId={
|
|
||||||
props.setSelectWidgetId
|
|
||||||
}
|
|
||||||
selectWidgetId={
|
|
||||||
props.selectWidgetId
|
|
||||||
}
|
|
||||||
handleNameModal={
|
handleNameModal={
|
||||||
props.handleNameModal
|
props.handleNameModal
|
||||||
}
|
}
|
||||||
@@ -309,6 +294,9 @@ function RenderPdf(props) {
|
|||||||
calculateFontsize={
|
calculateFontsize={
|
||||||
calculateFontsize
|
calculateFontsize
|
||||||
}
|
}
|
||||||
|
currWidgetsDetails={
|
||||||
|
props?.currWidgetsDetails
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@@ -331,9 +319,7 @@ function RenderPdf(props) {
|
|||||||
key={id}
|
key={id}
|
||||||
pos={pos}
|
pos={pos}
|
||||||
setIsPageCopy={props.setIsPageCopy}
|
setIsPageCopy={props.setIsPageCopy}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
handleDeleteSign={props.handleDeleteSign}
|
handleDeleteSign={props.handleDeleteSign}
|
||||||
setIsStamp={props.setIsStamp}
|
|
||||||
handleTabDrag={props.handleTabDrag}
|
handleTabDrag={props.handleTabDrag}
|
||||||
handleStop={props.handleStop}
|
handleStop={props.handleStop}
|
||||||
handleSignYourselfImageResize={
|
handleSignYourselfImageResize={
|
||||||
@@ -343,19 +329,13 @@ function RenderPdf(props) {
|
|||||||
xyPosition={props.xyPosition}
|
xyPosition={props.xyPosition}
|
||||||
setXyPosition={props.setXyPosition}
|
setXyPosition={props.setXyPosition}
|
||||||
containerWH={props.containerWH}
|
containerWH={props.containerWH}
|
||||||
setIsSignPad={props.setIsSignPad}
|
|
||||||
isShowBorder={true}
|
isShowBorder={true}
|
||||||
isSignYourself={true}
|
isSignYourself={true}
|
||||||
posWidth={posWidth}
|
posWidth={posWidth}
|
||||||
posHeight={posHeight}
|
posHeight={posHeight}
|
||||||
pdfDetails={props.pdfDetails[0]}
|
pdfDetails={props.pdfDetails[0]}
|
||||||
isDragging={props.isDragging}
|
isDragging={props.isDragging}
|
||||||
setIsInitial={props.setIsInitial}
|
|
||||||
setWidgetType={props.setWidgetType}
|
|
||||||
setSelectWidgetId={props.setSelectWidgetId}
|
|
||||||
selectWidgetId={props.selectWidgetId}
|
|
||||||
setIsCheckbox={props.setIsCheckbox}
|
setIsCheckbox={props.setIsCheckbox}
|
||||||
setValidateAlert={props.setValidateAlert}
|
|
||||||
setCurrWidgetsDetails={
|
setCurrWidgetsDetails={
|
||||||
props.setCurrWidgetsDetails
|
props.setCurrWidgetsDetails
|
||||||
}
|
}
|
||||||
@@ -374,6 +354,9 @@ function RenderPdf(props) {
|
|||||||
isFreeResize={false}
|
isFreeResize={false}
|
||||||
isOpenSignPad={true}
|
isOpenSignPad={true}
|
||||||
calculateFontsize={calculateFontsize}
|
calculateFontsize={calculateFontsize}
|
||||||
|
currWidgetsDetails={
|
||||||
|
props?.currWidgetsDetails
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -388,7 +371,7 @@ function RenderPdf(props) {
|
|||||||
loading={t("loading-doc")}
|
loading={t("loading-doc")}
|
||||||
onLoadSuccess={props.pageDetails}
|
onLoadSuccess={props.pageDetails}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.setSelectWidgetId && props.setSelectWidgetId("")
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({})
|
||||||
}
|
}
|
||||||
file={pdfDataBase64}
|
file={pdfDataBase64}
|
||||||
>
|
>
|
||||||
@@ -403,6 +386,7 @@ function RenderPdf(props) {
|
|||||||
onGetAnnotationsError={(error) => {
|
onGetAnnotationsError={(error) => {
|
||||||
console.log("annotation error", error);
|
console.log("annotation error", error);
|
||||||
}}
|
}}
|
||||||
|
className="select-none touch-callout-none"
|
||||||
/>
|
/>
|
||||||
</Document>
|
</Document>
|
||||||
</div>
|
</div>
|
||||||
@@ -453,11 +437,9 @@ function RenderPdf(props) {
|
|||||||
<Placeholder
|
<Placeholder
|
||||||
pos={pos}
|
pos={pos}
|
||||||
setIsPageCopy={props.setIsPageCopy}
|
setIsPageCopy={props.setIsPageCopy}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
handleDeleteSign={
|
handleDeleteSign={
|
||||||
props.handleDeleteSign
|
props.handleDeleteSign
|
||||||
}
|
}
|
||||||
setIsStamp={props.setIsStamp}
|
|
||||||
handleTabDrag={props.handleTabDrag}
|
handleTabDrag={props.handleTabDrag}
|
||||||
handleStop={props.handleStop}
|
handleStop={props.handleStop}
|
||||||
handleSignYourselfImageResize={
|
handleSignYourselfImageResize={
|
||||||
@@ -482,18 +464,11 @@ function RenderPdf(props) {
|
|||||||
posHeight={posHeight}
|
posHeight={posHeight}
|
||||||
isDragging={props.isDragging}
|
isDragging={props.isDragging}
|
||||||
setIsValidate={props.setIsValidate}
|
setIsValidate={props.setIsValidate}
|
||||||
setWidgetType={props.setWidgetType}
|
|
||||||
setIsRadio={props.setIsRadio}
|
setIsRadio={props.setIsRadio}
|
||||||
setIsCheckbox={props.setIsCheckbox}
|
setIsCheckbox={props.setIsCheckbox}
|
||||||
setCurrWidgetsDetails={
|
setCurrWidgetsDetails={
|
||||||
props.setCurrWidgetsDetails
|
props.setCurrWidgetsDetails
|
||||||
}
|
}
|
||||||
setSelectWidgetId={
|
|
||||||
props.setSelectWidgetId
|
|
||||||
}
|
|
||||||
selectWidgetId={
|
|
||||||
props.selectWidgetId
|
|
||||||
}
|
|
||||||
handleNameModal={
|
handleNameModal={
|
||||||
props.handleNameModal
|
props.handleNameModal
|
||||||
}
|
}
|
||||||
@@ -521,6 +496,9 @@ function RenderPdf(props) {
|
|||||||
calculateFontsize={
|
calculateFontsize={
|
||||||
calculateFontsize
|
calculateFontsize
|
||||||
}
|
}
|
||||||
|
currWidgetsDetails={
|
||||||
|
props?.currWidgetsDetails
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@@ -543,9 +521,7 @@ function RenderPdf(props) {
|
|||||||
<Placeholder
|
<Placeholder
|
||||||
pos={pos}
|
pos={pos}
|
||||||
setIsPageCopy={props.setIsPageCopy}
|
setIsPageCopy={props.setIsPageCopy}
|
||||||
setSignKey={props.setSignKey}
|
|
||||||
handleDeleteSign={props.handleDeleteSign}
|
handleDeleteSign={props.handleDeleteSign}
|
||||||
setIsStamp={props.setIsStamp}
|
|
||||||
handleTabDrag={props.handleTabDrag}
|
handleTabDrag={props.handleTabDrag}
|
||||||
handleStop={(event, dragElement) =>
|
handleStop={(event, dragElement) =>
|
||||||
props.handleStop(
|
props.handleStop(
|
||||||
@@ -560,19 +536,13 @@ function RenderPdf(props) {
|
|||||||
index={props.index}
|
index={props.index}
|
||||||
xyPosition={props.xyPosition}
|
xyPosition={props.xyPosition}
|
||||||
setXyPosition={props.setXyPosition}
|
setXyPosition={props.setXyPosition}
|
||||||
setIsSignPad={props.setIsSignPad}
|
|
||||||
isShowBorder={true}
|
isShowBorder={true}
|
||||||
isSignYourself={true}
|
isSignYourself={true}
|
||||||
posWidth={posWidth}
|
posWidth={posWidth}
|
||||||
posHeight={posHeight}
|
posHeight={posHeight}
|
||||||
pdfDetails={props.pdfDetails[0]}
|
pdfDetails={props.pdfDetails[0]}
|
||||||
isDragging={props.isDragging}
|
isDragging={props.isDragging}
|
||||||
setIsInitial={props.setIsInitial}
|
|
||||||
setWidgetType={props.setWidgetType}
|
|
||||||
setSelectWidgetId={props.setSelectWidgetId}
|
|
||||||
selectWidgetId={props.selectWidgetId}
|
|
||||||
setIsCheckbox={props.setIsCheckbox}
|
setIsCheckbox={props.setIsCheckbox}
|
||||||
setValidateAlert={props.setValidateAlert}
|
|
||||||
setCurrWidgetsDetails={
|
setCurrWidgetsDetails={
|
||||||
props.setCurrWidgetsDetails
|
props.setCurrWidgetsDetails
|
||||||
}
|
}
|
||||||
@@ -592,6 +562,9 @@ function RenderPdf(props) {
|
|||||||
isFreeResize={false}
|
isFreeResize={false}
|
||||||
isOpenSignPad={true}
|
isOpenSignPad={true}
|
||||||
calculateFontsize={calculateFontsize}
|
calculateFontsize={calculateFontsize}
|
||||||
|
currWidgetsDetails={
|
||||||
|
props?.currWidgetsDetails
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@@ -607,7 +580,7 @@ function RenderPdf(props) {
|
|||||||
loading={t("loading-doc")}
|
loading={t("loading-doc")}
|
||||||
onLoadSuccess={props.pageDetails}
|
onLoadSuccess={props.pageDetails}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.setSelectWidgetId && props.setSelectWidgetId("")
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({})
|
||||||
}
|
}
|
||||||
file={pdfDataBase64}
|
file={pdfDataBase64}
|
||||||
>
|
>
|
||||||
@@ -1,814 +0,0 @@
|
|||||||
import React, { useRef, useState, useEffect } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import SignatureCanvas from "react-signature-canvas";
|
|
||||||
import Parse from "parse";
|
|
||||||
import {
|
|
||||||
generateTitleFromFilename,
|
|
||||||
getBase64FromUrl,
|
|
||||||
getSecureUrl
|
|
||||||
} from "../../constant/Utils";
|
|
||||||
import sanitizeFileName from "../../primitives/sanitizeFileName";
|
|
||||||
import { SaveFileSize } from "../../constant/saveFileSize";
|
|
||||||
import Loader from "../../primitives/Loader";
|
|
||||||
|
|
||||||
function SignPad(props) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [penColor, setPenColor] = useState("blue");
|
|
||||||
const allColor = ["blue", "red", "black"];
|
|
||||||
const canvasRef = useRef(null);
|
|
||||||
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
|
||||||
const [isTab, setIsTab] = useState("");
|
|
||||||
const [isSignImg, setIsSignImg] = useState("");
|
|
||||||
const [textWidth, setTextWidth] = useState(0);
|
|
||||||
const [textHeight, setTextHeight] = useState(0);
|
|
||||||
const [signatureType, setSignatureType] = useState("");
|
|
||||||
const [isSignTypes, setIsSignTypes] = useState(true);
|
|
||||||
const [typedSignature, setTypedSignature] = useState("");
|
|
||||||
const fontOptions = [
|
|
||||||
{ value: "Fasthand" },
|
|
||||||
{ value: "Dancing Script" },
|
|
||||||
{ value: "Cedarville Cursive" },
|
|
||||||
{ value: "Delicious Handrawn" }
|
|
||||||
// Add more font options as needed
|
|
||||||
];
|
|
||||||
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
|
|
||||||
const [isSavedSign, setIsSavedSign] = useState(false);
|
|
||||||
const [isLoader, setIsLoader] = useState(false);
|
|
||||||
const accesstoken = localStorage.getItem("accesstoken") || "";
|
|
||||||
const senderUser = localStorage.getItem(
|
|
||||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
|
||||||
);
|
|
||||||
const jsonSender = senderUser && JSON.parse(senderUser);
|
|
||||||
const currentUserName = jsonSender && jsonSender?.name;
|
|
||||||
useEffect(() => {
|
|
||||||
handleTab();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [props.signatureTypes]);
|
|
||||||
function handleTab() {
|
|
||||||
const signtypes = props?.signatureTypes || [];
|
|
||||||
const defaultIndex = signtypes?.findIndex(
|
|
||||||
(x) =>
|
|
||||||
x.name === "default" &&
|
|
||||||
x.enabled === true &&
|
|
||||||
props.defaultSign &&
|
|
||||||
(props?.currWidgetsDetails?.type || props.widgetType) !== "image" &&
|
|
||||||
(props?.currWidgetsDetails?.type || props.widgetType) !== "stamp"
|
|
||||||
);
|
|
||||||
const getIndex =
|
|
||||||
defaultIndex !== -1 // Check if the default index exists
|
|
||||||
? defaultIndex // If found, use it
|
|
||||||
: signtypes?.findIndex((x) => x.enabled === true);
|
|
||||||
|
|
||||||
if (getIndex !== -1) {
|
|
||||||
setIsSignTypes(true);
|
|
||||||
const tab = props?.signatureTypes[getIndex].name;
|
|
||||||
if (tab === "draw") {
|
|
||||||
setIsTab("draw");
|
|
||||||
setSignatureType("draw");
|
|
||||||
} else if (tab === "upload") {
|
|
||||||
props?.setIsImageSelect(true);
|
|
||||||
setIsTab("uploadImage");
|
|
||||||
} else if (tab === "typed") {
|
|
||||||
setIsTab("type");
|
|
||||||
} else if (tab === "default") {
|
|
||||||
setIsDefaultSign(true);
|
|
||||||
setIsTab("mysignature");
|
|
||||||
} else {
|
|
||||||
setIsTab(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setIsSignTypes(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isTabEnabled(tabName) {
|
|
||||||
const isEnabled = props?.signatureTypes.find(
|
|
||||||
(x) => x.name === tabName
|
|
||||||
)?.enabled;
|
|
||||||
return isEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
//function for clear signature image
|
|
||||||
const handleClear = () => {
|
|
||||||
if (isTab === "draw") {
|
|
||||||
if (canvasRef.current) {
|
|
||||||
canvasRef.current.clear();
|
|
||||||
} else if (props?.isStamp) {
|
|
||||||
props?.setImage("");
|
|
||||||
}
|
|
||||||
setIsSignImg("");
|
|
||||||
} else if (isTab === "uploadImage") {
|
|
||||||
props?.setImage("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//function for set signature url
|
|
||||||
const handleSignatureChange = (data) => {
|
|
||||||
props?.setSignature(data);
|
|
||||||
setIsSignImg(data);
|
|
||||||
};
|
|
||||||
function base64StringtoFile(base64String, filename) {
|
|
||||||
let arr = base64String.split(","),
|
|
||||||
// type of uploaded image
|
|
||||||
mime = arr[0].match(/:(.*?);/)[1],
|
|
||||||
// decode base64
|
|
||||||
bstr = atob(arr[1]),
|
|
||||||
n = bstr.length,
|
|
||||||
u8arr = new Uint8Array(n);
|
|
||||||
while (n--) {
|
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
|
||||||
}
|
|
||||||
const ext = mime.split("/").pop();
|
|
||||||
const name = `${filename}.${ext}`;
|
|
||||||
return new File([u8arr], name, { type: mime });
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadFile = async (file) => {
|
|
||||||
try {
|
|
||||||
const parseFile = new Parse.File(file.name, file);
|
|
||||||
const response = await parseFile.save();
|
|
||||||
if (response?.url()) {
|
|
||||||
const fileRes = await getSecureUrl(response.url());
|
|
||||||
if (fileRes.url) {
|
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
|
||||||
SaveFileSize(file.size, fileRes.url, tenantId);
|
|
||||||
return fileRes?.url;
|
|
||||||
} else {
|
|
||||||
alert(`${t("something-went-wrong-mssg")}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert(`${t("something-went-wrong-mssg")}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("sign upload err", err);
|
|
||||||
alert(`${err.message}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// `handlesavesign` is used to save signaute, initials, stamp as a default
|
|
||||||
const handleSaveSign = async () => {
|
|
||||||
if (isSignImg || props?.image?.src) {
|
|
||||||
setIsLoader(true);
|
|
||||||
try {
|
|
||||||
const User = Parse?.User?.current();
|
|
||||||
const sanitizename = generateTitleFromFilename(User?.get("name"));
|
|
||||||
const replaceSpace = sanitizeFileName(sanitizename);
|
|
||||||
let file;
|
|
||||||
if (isSignImg) {
|
|
||||||
file = base64StringtoFile(isSignImg, `${replaceSpace}__sign`);
|
|
||||||
} else {
|
|
||||||
file = base64StringtoFile(props?.image?.src, `${replaceSpace}__sign`);
|
|
||||||
}
|
|
||||||
const imageUrl = await uploadFile(file);
|
|
||||||
const userId = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "_User",
|
|
||||||
objectId: User?.id
|
|
||||||
};
|
|
||||||
if (imageUrl) {
|
|
||||||
// below code is used to save or update default signaute, initials, stamp
|
|
||||||
try {
|
|
||||||
const signCls = new Parse.Object("contracts_Signature");
|
|
||||||
if (props?.saveSignCheckbox?.signId) {
|
|
||||||
signCls.id = props.saveSignCheckbox.signId;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
props.currWidgetsDetails?.type === "initials" ||
|
|
||||||
props?.widgetType === "initials"
|
|
||||||
) {
|
|
||||||
signCls.set("Initials", imageUrl);
|
|
||||||
} else if (
|
|
||||||
props.currWidgetsDetails?.type === "signature" ||
|
|
||||||
props?.widgetType === "signature"
|
|
||||||
) {
|
|
||||||
signCls.set("ImageURL", imageUrl);
|
|
||||||
}
|
|
||||||
signCls.set("UserId", userId);
|
|
||||||
const signRes = await signCls.save();
|
|
||||||
if (signRes) {
|
|
||||||
props.saveSignCheckbox.signId;
|
|
||||||
props.setSaveSignCheckbox((prev) => ({
|
|
||||||
...prev,
|
|
||||||
signId: signRes?.id
|
|
||||||
}));
|
|
||||||
const _signRes = JSON.parse(JSON.stringify(signRes));
|
|
||||||
if (
|
|
||||||
props.currWidgetsDetails?.type === "signature" ||
|
|
||||||
props?.widgetType === "signature"
|
|
||||||
) {
|
|
||||||
const defaultSign = await getBase64FromUrl(
|
|
||||||
_signRes?.ImageURL,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
props.setDefaultSignImg(defaultSign);
|
|
||||||
} else if (
|
|
||||||
props.currWidgetsDetails?.type === "initials" ||
|
|
||||||
props?.widgetType === "initials"
|
|
||||||
) {
|
|
||||||
const defaultInitials = await getBase64FromUrl(
|
|
||||||
_signRes?.Initials,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
props.setMyInitial(defaultInitials);
|
|
||||||
}
|
|
||||||
alert(t("saved-successfully"));
|
|
||||||
}
|
|
||||||
return signRes;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
alert(`${err.message}`);
|
|
||||||
} finally {
|
|
||||||
setIsLoader(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("Err while saving signature", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveBtn = async () => {
|
|
||||||
if (accesstoken && isSavedSign) {
|
|
||||||
await handleSaveSign();
|
|
||||||
resetToDefault();
|
|
||||||
} else {
|
|
||||||
resetToDefault();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const resetToDefault = () => {
|
|
||||||
props?.setCurrWidgetsDetails({});
|
|
||||||
if (!props?.image) {
|
|
||||||
if (isTab === "mysignature") {
|
|
||||||
setIsSignImg("");
|
|
||||||
if (props?.isInitial) {
|
|
||||||
props?.onSaveSign(signatureType, "initials");
|
|
||||||
} else {
|
|
||||||
props?.onSaveSign(null, "default");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isTab === "type") {
|
|
||||||
setIsSignImg("");
|
|
||||||
props?.onSaveSign(
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
!props?.isInitial && textWidth > 150 ? 150 : textWidth,
|
|
||||||
!props?.isInitial && textHeight > 35 ? 35 : textHeight,
|
|
||||||
typedSignature
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setIsSignImg("");
|
|
||||||
canvasRef.current.clear();
|
|
||||||
props?.onSaveSign(signatureType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setPenColor("blue");
|
|
||||||
} else {
|
|
||||||
setIsSignImg("");
|
|
||||||
props?.onSaveImage(signatureType);
|
|
||||||
}
|
|
||||||
props?.setIsSignPad(false);
|
|
||||||
props?.setIsInitial && props?.setIsInitial(false);
|
|
||||||
props?.setIsImageSelect(false);
|
|
||||||
setIsDefaultSign(false);
|
|
||||||
props?.setImage();
|
|
||||||
handleTab();
|
|
||||||
props?.setIsStamp(false);
|
|
||||||
};
|
|
||||||
//save button component
|
|
||||||
const SaveBtn = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{(isTab === "draw" || isTab === "uploadImage") && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="op-btn op-btn-ghost mr-1 mt-[2px]"
|
|
||||||
onClick={() => handleClear()}
|
|
||||||
>
|
|
||||||
{t("clear")}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => handleSaveBtn()}
|
|
||||||
type="button"
|
|
||||||
className={`${
|
|
||||||
isSignImg ||
|
|
||||||
props?.image ||
|
|
||||||
isDefaultSign ||
|
|
||||||
textWidth ||
|
|
||||||
props.isAutoSign
|
|
||||||
? ""
|
|
||||||
: "pointer-events-none"
|
|
||||||
} op-btn op-btn-primary shadow-lg`}
|
|
||||||
disabled={
|
|
||||||
(isTab === "draw" && isSignImg) ||
|
|
||||||
(isTab === "image" && props?.image) ||
|
|
||||||
(isTab === "mysignature" && isDefaultSign) ||
|
|
||||||
(isTab === "type" && typedSignature) ||
|
|
||||||
props.isAutoSign
|
|
||||||
? false
|
|
||||||
: props?.image
|
|
||||||
? false
|
|
||||||
: true
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("save")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const autoSignAll = () => {
|
|
||||||
return (
|
|
||||||
<label className="cursor-pointer flex items-center mb-[6px] text-center text-[11px] md:text-base">
|
|
||||||
<input
|
|
||||||
className="mr-2 md:mr-3 op-checkbox op-checkbox-xs md:op-checkbox-sm"
|
|
||||||
type="checkbox"
|
|
||||||
value={props.isAutoSign}
|
|
||||||
onChange={(e) => {
|
|
||||||
props.setIsAutoSign(e.target.checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{t("auto-sign-mssg")}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
|
|
||||||
useEffect(() => {
|
|
||||||
if (props?.currWidgetsDetails && canvasRef.current && props.isSignPad) {
|
|
||||||
const isWidgetType = props?.currWidgetsDetails?.type;
|
|
||||||
const signatureType = props?.currWidgetsDetails?.signatureType;
|
|
||||||
const url = props?.currWidgetsDetails?.SignUrl;
|
|
||||||
//checking widget type and draw type signature url
|
|
||||||
if (props?.isInitial) {
|
|
||||||
if (isWidgetType === "initials" && signatureType === "draw" && url) {
|
|
||||||
canvasRef.current.fromDataURL(url);
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
isWidgetType === "signature" &&
|
|
||||||
signatureType === "draw" &&
|
|
||||||
url
|
|
||||||
) {
|
|
||||||
canvasRef.current.fromDataURL(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const trimmedName = currentUserName && currentUserName?.trim();
|
|
||||||
const firstCharacter = trimmedName?.charAt(0);
|
|
||||||
const userName = props?.isInitial ? firstCharacter : currentUserName;
|
|
||||||
const signatureValue = props?.currWidgetsDetails?.typeSignature;
|
|
||||||
setTypedSignature(signatureValue || userName || "");
|
|
||||||
setFontSelect("Fasthand");
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [props.isSignPad]);
|
|
||||||
useEffect(() => {
|
|
||||||
const loadFont = async () => {
|
|
||||||
try {
|
|
||||||
await document.fonts.load(`20px ${fontSelect}`);
|
|
||||||
const selectFontSTyle = fontOptions.find(
|
|
||||||
(font) => font.value === fontSelect
|
|
||||||
);
|
|
||||||
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error loading font:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadFont();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [fontSelect]);
|
|
||||||
useEffect(() => {
|
|
||||||
// Load the default signature after the component mounts
|
|
||||||
if (canvasRef.current) {
|
|
||||||
canvasRef.current.fromDataURL(isSignImg);
|
|
||||||
}
|
|
||||||
if (isTab === "type") {
|
|
||||||
const trimmedName = typedSignature
|
|
||||||
? typedSignature?.trim()
|
|
||||||
: currentUserName?.trim();
|
|
||||||
const firstCharacter = trimmedName?.charAt(0);
|
|
||||||
const userName = props?.isInitial ? firstCharacter : typedSignature;
|
|
||||||
const signatureValue = props?.currWidgetsDetails?.typeSignature;
|
|
||||||
setTypedSignature(signatureValue || userName || "");
|
|
||||||
convertToImg(fontSelect, userName);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isTab]);
|
|
||||||
//function for convert input text value in image
|
|
||||||
const convertToImg = async (fontStyle, text, color) => {
|
|
||||||
//get text content to convert in image
|
|
||||||
const textContent = text;
|
|
||||||
const fontfamily = fontStyle
|
|
||||||
? fontStyle
|
|
||||||
: fontSelect
|
|
||||||
? fontSelect
|
|
||||||
: "Fasthand";
|
|
||||||
const fontSizeValue = "40px";
|
|
||||||
//creating span for getting text content width
|
|
||||||
const span = document.createElement("span");
|
|
||||||
span.textContent = textContent;
|
|
||||||
span.style.font = `${fontSizeValue} ${fontfamily}`; // here put your text size and font family
|
|
||||||
span.style.color = color ? color : penColor;
|
|
||||||
span.style.display = "hidden";
|
|
||||||
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
|
||||||
|
|
||||||
//create canvas to render text in canvas and convert in image
|
|
||||||
const canvasElement = document.createElement("canvas");
|
|
||||||
// Draw the text content on the canvas
|
|
||||||
const ctx = canvasElement.getContext("2d");
|
|
||||||
const pixelRatio = window.devicePixelRatio || 1;
|
|
||||||
const addExtraWidth = props?.isInitial ? 10 : 50;
|
|
||||||
const width = span.offsetWidth + addExtraWidth;
|
|
||||||
const height = span.offsetHeight;
|
|
||||||
setTextWidth(width);
|
|
||||||
setTextHeight(height);
|
|
||||||
const font = span.style["font"];
|
|
||||||
// Set the canvas dimensions to match the span
|
|
||||||
canvasElement.width = width * pixelRatio;
|
|
||||||
canvasElement.height = height * pixelRatio;
|
|
||||||
|
|
||||||
// You can customize text styles if needed
|
|
||||||
ctx.font = font;
|
|
||||||
ctx.fillStyle = color ? color : penColor; // Set the text color
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = "middle";
|
|
||||||
ctx.scale(pixelRatio, pixelRatio);
|
|
||||||
// Draw the content of the span onto the canvas
|
|
||||||
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
|
|
||||||
//remove span tag
|
|
||||||
document.body.removeChild(span);
|
|
||||||
// Convert the canvas to image data
|
|
||||||
const dataUrl = canvasElement.toDataURL("image/png");
|
|
||||||
props?.setSignature(dataUrl);
|
|
||||||
};
|
|
||||||
const PenColorComponent = (props) => {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-row items-center m-[5px] gap-2">
|
|
||||||
{allColor.map((data, key) => {
|
|
||||||
return (
|
|
||||||
<i
|
|
||||||
key={key}
|
|
||||||
onClick={() => {
|
|
||||||
props?.convertToImg &&
|
|
||||||
props?.convertToImg(fontSelect, typedSignature, data);
|
|
||||||
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`}
|
|
||||||
></i>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// `handleCancelBtn` function trigger when user click on cross button
|
|
||||||
const handleCancelBtn = () => {
|
|
||||||
setPenColor("blue");
|
|
||||||
props?.setIsSignPad(false);
|
|
||||||
props?.setIsInitial && props?.setIsInitial(false);
|
|
||||||
props?.setIsImageSelect(false);
|
|
||||||
setIsDefaultSign(false);
|
|
||||||
props?.setImage();
|
|
||||||
handleTab();
|
|
||||||
props?.setIsStamp(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const savesigncheckbox = (
|
|
||||||
<label className="cursor-pointer flex items-center mb-0 text-center text-[11px] md:text-base">
|
|
||||||
<input
|
|
||||||
className="mr-2 md:mr-3 op-checkbox op-checkbox-xs md:op-checkbox-sm"
|
|
||||||
type="checkbox"
|
|
||||||
checked={isSavedSign}
|
|
||||||
onChange={(e) => setIsSavedSign(e.target.checked)}
|
|
||||||
/>
|
|
||||||
Save {props?.currWidgetsDetails?.type || props?.widgetType}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{props?.isSignPad && (
|
|
||||||
<div className="op-modal op-modal-open">
|
|
||||||
<div className="op-modal-box px-[13px] pt-2 pb-0">
|
|
||||||
{isLoader && (
|
|
||||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isSignTypes ? (
|
|
||||||
<>
|
|
||||||
<div className="flex justify-between text-base-content items-center">
|
|
||||||
<div className="text-[1.2rem]">
|
|
||||||
<div className="flex flex-row justify-between mt-[3px]">
|
|
||||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
|
||||||
{props?.isStamp ? (
|
|
||||||
<span className="text-base-content font-bold text-lg">
|
|
||||||
{props?.widgetType === "image" ||
|
|
||||||
props?.currWidgetsDetails?.type === "image"
|
|
||||||
? t("upload-image")
|
|
||||||
: t("upload-stamp-image")}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{!props?.isInitial &&
|
|
||||||
props?.defaultSign &&
|
|
||||||
isTabEnabled("default") ? (
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setIsDefaultSign(true);
|
|
||||||
props?.setIsImageSelect(true);
|
|
||||||
setIsTab("mysignature");
|
|
||||||
setSignatureType("");
|
|
||||||
props?.setImage();
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
isTab === "mysignature"
|
|
||||||
? "op-link-primary"
|
|
||||||
: "no-underline"
|
|
||||||
} op-link underline-offset-8 ml-[2px]`}
|
|
||||||
>
|
|
||||||
{t("my-signature")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
props?.isInitial &&
|
|
||||||
props?.myInitial &&
|
|
||||||
isTabEnabled("default") && (
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setIsDefaultSign(true);
|
|
||||||
props?.setIsImageSelect(true);
|
|
||||||
setIsTab("mysignature");
|
|
||||||
setSignatureType("");
|
|
||||||
props?.setImage();
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
isTab === "mysignature"
|
|
||||||
? "op-link-primary"
|
|
||||||
: "no-underline"
|
|
||||||
} op-link underline-offset-8 ml-[2px]`}
|
|
||||||
>
|
|
||||||
{t("my-initials")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
{isTabEnabled("draw") && (
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setIsDefaultSign(false);
|
|
||||||
props?.setIsImageSelect(false);
|
|
||||||
setIsTab("draw");
|
|
||||||
props?.setImage();
|
|
||||||
if (isSignImg) {
|
|
||||||
props?.setSignature(isSignImg);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
isTab === "draw"
|
|
||||||
? "op-link-primary"
|
|
||||||
: "no-underline"
|
|
||||||
} op-link underline-offset-8 ml-[2px]`}
|
|
||||||
>
|
|
||||||
{t("draw")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isTabEnabled("upload") && (
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setIsDefaultSign(false);
|
|
||||||
props?.setIsImageSelect(true);
|
|
||||||
setIsTab("uploadImage");
|
|
||||||
setSignatureType("");
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
isTab === "uploadImage"
|
|
||||||
? "op-link-primary"
|
|
||||||
: "no-underline"
|
|
||||||
} op-link underline-offset-8 ml-[2px]`}
|
|
||||||
>
|
|
||||||
{t("upload-image")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isTabEnabled("typed") && (
|
|
||||||
<div>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setIsDefaultSign(false);
|
|
||||||
props?.setIsImageSelect(false);
|
|
||||||
setIsTab("type");
|
|
||||||
setSignatureType("");
|
|
||||||
props?.setImage();
|
|
||||||
}}
|
|
||||||
className={`${
|
|
||||||
isTab === "type"
|
|
||||||
? "op-link-primary"
|
|
||||||
: "no-underline"
|
|
||||||
} op-link underline-offset-8 ml-[2px]`}
|
|
||||||
>
|
|
||||||
{t("type")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="text-[1.5rem] cursor-pointer"
|
|
||||||
onClick={handleCancelBtn}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="p-[20px] h-full">
|
|
||||||
{isDefaultSign ? (
|
|
||||||
<>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<div
|
|
||||||
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="stamp img"
|
|
||||||
className="w-full h-full object-contain bg-white"
|
|
||||||
draggable="false"
|
|
||||||
src={
|
|
||||||
props?.isInitial
|
|
||||||
? props?.myInitial
|
|
||||||
: props?.defaultSign
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{props.setIsAutoSign && autoSignAll()}
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<SaveBtn />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : props?.isImageSelect || props?.isStamp ? (
|
|
||||||
!props?.image ? (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<div
|
|
||||||
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
|
|
||||||
onClick={() => props?.imageRef.current.click()}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
onChange={props?.onImageChange}
|
|
||||||
className="filetype"
|
|
||||||
accept="image/png,image/jpeg"
|
|
||||||
ref={props?.imageRef}
|
|
||||||
hidden
|
|
||||||
/>
|
|
||||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
|
||||||
<div className="text-[10px]">{t("upload")}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<div
|
|
||||||
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] mb-[6px] overflow-hidden`}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="print img"
|
|
||||||
ref={props?.imageRef}
|
|
||||||
src={props?.image.src}
|
|
||||||
draggable="false"
|
|
||||||
className="object-contain h-full w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{props.setIsAutoSign && autoSignAll()}
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<SaveBtn />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
) : isTab === "type" ? (
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<span className="mr-[5px] text-[12px]">
|
|
||||||
{props?.isInitial
|
|
||||||
? t("initial-teb")
|
|
||||||
: t("signature-tab")}
|
|
||||||
:
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
maxLength={props?.isInitial ? 3 : 30}
|
|
||||||
style={{ fontFamily: fontSelect, color: penColor }}
|
|
||||||
type="text"
|
|
||||||
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
|
||||||
placeholder={
|
|
||||||
props?.isInitial
|
|
||||||
? t("initial-type")
|
|
||||||
: t("signature-type")
|
|
||||||
}
|
|
||||||
value={typedSignature}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTypedSignature(e.target.value);
|
|
||||||
convertToImg(fontSelect, e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
|
||||||
{fontOptions.map((font, ind) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={ind}
|
|
||||||
style={{
|
|
||||||
cursor: "pointer",
|
|
||||||
fontFamily: font.value,
|
|
||||||
backgroundColor:
|
|
||||||
fontSelect === font.value &&
|
|
||||||
"rgb(206 225 247)"
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setFontSelect(font.value);
|
|
||||||
convertToImg(font.value, typedSignature);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="py-[5px] px-[10px] text-[20px]"
|
|
||||||
style={{ color: penColor }}
|
|
||||||
>
|
|
||||||
{typedSignature
|
|
||||||
? typedSignature
|
|
||||||
: "Your signature"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-between mt-[10px]">
|
|
||||||
{props.setIsAutoSign && autoSignAll()}
|
|
||||||
<div className="flex flex-row justify-between mt-[10px]">
|
|
||||||
<PenColorComponent convertToImg={convertToImg} />
|
|
||||||
<SaveBtn />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<SignatureCanvas
|
|
||||||
ref={canvasRef}
|
|
||||||
penColor={penColor}
|
|
||||||
canvasProps={{
|
|
||||||
className: `${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-[#007bff]`
|
|
||||||
}}
|
|
||||||
onEnd={() =>
|
|
||||||
handleSignatureChange(
|
|
||||||
canvasRef.current?.toDataURL()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
dotSize={1}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col justify-between mt-[10px]">
|
|
||||||
{props.setIsAutoSign && autoSignAll()}
|
|
||||||
{accesstoken &&
|
|
||||||
props?.saveSignCheckbox?.isVisible &&
|
|
||||||
savesigncheckbox}
|
|
||||||
<div className="flex flex-row justify-between mt-[10px]">
|
|
||||||
<PenColorComponent />
|
|
||||||
<SaveBtn />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<div className="relative flex flex-row items-center justify-between">
|
|
||||||
<div className="text-base-content font-bold text-lg">
|
|
||||||
Signature
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="text-[1.5rem] cursor-pointer"
|
|
||||||
onClick={handleCancelBtn}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx-3 mb-6 mt-3">
|
|
||||||
<p>{t("at-least-one-signature-type")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SignPad;
|
|
||||||
+17
-5
@@ -25,17 +25,28 @@ const WidgetNameModal = (props) => {
|
|||||||
const statusArr = ["Required", "Optional"];
|
const statusArr = ["Required", "Optional"];
|
||||||
const [signatureType, setSignatureType] = useState([]);
|
const [signatureType, setSignatureType] = useState([]);
|
||||||
|
|
||||||
|
const handleHint = () => {
|
||||||
|
const type = props.defaultdata?.type;
|
||||||
|
|
||||||
|
if (type === "signature") {
|
||||||
|
return "Draw signature";
|
||||||
|
} else if (type === "stamp" || type === "image") {
|
||||||
|
return `Upload ${type}`;
|
||||||
|
} else if (type === "initials") {
|
||||||
|
return "Draw initial";
|
||||||
|
} else if (type === textInputWidget) {
|
||||||
|
return "Enter text";
|
||||||
|
} else {
|
||||||
|
return `Enter ${type}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.defaultdata) {
|
if (props.defaultdata) {
|
||||||
setFormdata({
|
setFormdata({
|
||||||
name: props.defaultdata?.options?.name || "",
|
name: props.defaultdata?.options?.name || "",
|
||||||
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||||
status: props.defaultdata?.options?.status || "required",
|
status: props.defaultdata?.options?.status || "required",
|
||||||
hint:
|
hint: props.defaultdata?.options?.hint || handleHint(),
|
||||||
props.defaultdata?.options?.hint ||
|
|
||||||
(props.defaultdata?.type === textInputWidget
|
|
||||||
? "Enter text"
|
|
||||||
: `Enter ${props.defaultdata?.options?.name}`),
|
|
||||||
textvalidate:
|
textvalidate:
|
||||||
props.defaultdata?.options?.validation?.type === "regex"
|
props.defaultdata?.options?.validation?.type === "regex"
|
||||||
? props.defaultdata?.options?.validation?.pattern
|
? props.defaultdata?.options?.validation?.pattern
|
||||||
@@ -117,6 +128,7 @@ const WidgetNameModal = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleCheckboxChange = (index) => {
|
const handleCheckboxChange = (index) => {
|
||||||
// Update the state with the modified array
|
// Update the state with the modified array
|
||||||
setSignatureType((prev) =>
|
setSignatureType((prev) =>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,173 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import Parse from "parse";
|
|
||||||
import Alert from "../../../primitives/Alert";
|
|
||||||
import Loader from "../../../primitives/Loader";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|
||||||
const folderPtr = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: folderCls,
|
|
||||||
objectId: parentFolderId
|
|
||||||
};
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [folderList, setFolderList] = useState([]);
|
|
||||||
const [isAlert, setIsAlert] = useState(false);
|
|
||||||
const [isLoader, setIsLoader] = useState(false);
|
|
||||||
const [selectedParent, setSelectedParent] = useState();
|
|
||||||
const [alert, setAlert] = useState({ type: "info", message: "" });
|
|
||||||
useEffect(() => {
|
|
||||||
fetchFolder();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const fetchFolder = async () => {
|
|
||||||
try {
|
|
||||||
const FolderQuery = new Parse.Query(folderCls);
|
|
||||||
if (parentFolderId) {
|
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
|
||||||
FolderQuery.notEqualTo("IsArchive", true);
|
|
||||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
|
||||||
} else {
|
|
||||||
FolderQuery.doesNotExist("Folder");
|
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
|
||||||
FolderQuery.notEqualTo("IsArchive", true);
|
|
||||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
|
||||||
if (res) {
|
|
||||||
const result = JSON.parse(JSON.stringify(res));
|
|
||||||
if (result) {
|
|
||||||
setFolderList(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Err ", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleCreateFolder = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
handleLoader(true);
|
|
||||||
if (name) {
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
const exsitQuery = new Parse.Query(folderCls);
|
|
||||||
exsitQuery.equalTo("Name", name);
|
|
||||||
exsitQuery.equalTo("Type", "Folder");
|
|
||||||
exsitQuery.notEqualTo("IsArchive", true);
|
|
||||||
if (parentFolderId) {
|
|
||||||
exsitQuery.equalTo("Folder", folderPtr);
|
|
||||||
}
|
|
||||||
const templExist = await exsitQuery.first();
|
|
||||||
if (templExist) {
|
|
||||||
setAlert({ type: "danger", message: t("folder-already-exist") });
|
|
||||||
setIsAlert(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsAlert(false);
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
const template = new Parse.Object(folderCls);
|
|
||||||
template.set("Name", name);
|
|
||||||
template.set("Type", "Folder");
|
|
||||||
|
|
||||||
if (selectedParent) {
|
|
||||||
template.set("Folder", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: folderCls,
|
|
||||||
objectId: selectedParent
|
|
||||||
});
|
|
||||||
} else if (parentFolderId) {
|
|
||||||
template.set("Folder", folderPtr);
|
|
||||||
}
|
|
||||||
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
|
||||||
const res = await template.save();
|
|
||||||
if (res) {
|
|
||||||
handleLoader(false);
|
|
||||||
setAlert({
|
|
||||||
type: "success",
|
|
||||||
message: t("folder-created-successfully")
|
|
||||||
});
|
|
||||||
setIsAlert(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsAlert(false);
|
|
||||||
}, 1000);
|
|
||||||
if (onSuccess) {
|
|
||||||
onSuccess(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleLoader(false);
|
|
||||||
setAlert({ type: "info", message: t("fill-folder-name") });
|
|
||||||
setIsAlert(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsAlert(false);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handleOptions = (e) => {
|
|
||||||
setSelectedParent(e.target.value);
|
|
||||||
};
|
|
||||||
const handleLoader = (status) => {
|
|
||||||
setIsLoader(status);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
|
|
||||||
<div id="createFolder" className="relative">
|
|
||||||
{isLoader && (
|
|
||||||
<div className="absolute h-full w-full flex justify-center items-center">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<h1 className="text-base font-semibold mt-[0.4rem]">
|
|
||||||
{t("create-folder")}
|
|
||||||
</h1>
|
|
||||||
<div className="text-xs mt-2">
|
|
||||||
<label className="block">
|
|
||||||
{t("name")}
|
|
||||||
<span className="text-red-500 text-[13px]">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs mt-2">
|
|
||||||
<label className="block">{t("parent-folder")}</label>
|
|
||||||
<select
|
|
||||||
value={selectedParent}
|
|
||||||
onChange={handleOptions}
|
|
||||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
>
|
|
||||||
<option>select</option>
|
|
||||||
{folderList.length > 0 &&
|
|
||||||
folderList.map((x) => (
|
|
||||||
<option key={x.objectId} value={x.objectId}>
|
|
||||||
{x.Name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
onClick={handleCreateFolder}
|
|
||||||
disabled={isLoader}
|
|
||||||
className="op-btn op-btn-primary op-btn-sm mt-3"
|
|
||||||
>
|
|
||||||
<i className="fa-light fa-plus"></i>
|
|
||||||
<span>{t("create")}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CreateFolder;
|
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import Alert from "../../../primitives/Alert";
|
||||||
|
import Loader from "../../../primitives/Loader";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const CreateFolder = ({ parentFolderId, onSuccess, folderCls, onBack }) => {
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: parentFolderId
|
||||||
|
};
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [alert, setAlert] = useState({ type: "info", message: "" });
|
||||||
|
const showToast = (type, msg) => {
|
||||||
|
setAlert({ type: type, message: msg });
|
||||||
|
setTimeout(() => setAlert({ type: type, message: "" }), 1000);
|
||||||
|
};
|
||||||
|
const handleCreateFolder = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
handleLoader(true);
|
||||||
|
if (name) {
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
const exsitQuery = new Parse.Query(folderCls);
|
||||||
|
exsitQuery.equalTo("Name", name);
|
||||||
|
exsitQuery.equalTo("Type", "Folder");
|
||||||
|
exsitQuery.notEqualTo("IsArchive", true);
|
||||||
|
if (parentFolderId) {
|
||||||
|
exsitQuery.equalTo("Folder", folderPtr);
|
||||||
|
}
|
||||||
|
const templExist = await exsitQuery.first();
|
||||||
|
if (templExist) {
|
||||||
|
showToast("danger", t("folder-already-exist"));
|
||||||
|
} else {
|
||||||
|
const template = new Parse.Object(folderCls);
|
||||||
|
template.set("Name", name);
|
||||||
|
template.set("Type", "Folder");
|
||||||
|
if (parentFolderId) {
|
||||||
|
template.set("Folder", folderPtr);
|
||||||
|
}
|
||||||
|
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||||
|
const res = await template.save();
|
||||||
|
if (res) {
|
||||||
|
handleLoader(false);
|
||||||
|
showToast("success", t("folder-created-successfully"));
|
||||||
|
onSuccess && onSuccess(res?.toJSON());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleLoader(false);
|
||||||
|
showToast("info", t("fill-folder-name"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleLoader = (status) => setIsLoader(status);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{alert.message && <Alert type={alert.type}>{alert.message}</Alert>}
|
||||||
|
<div id="createFolder" className="relative">
|
||||||
|
{isLoader && (
|
||||||
|
<div className="absolute h-full w-full flex justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<h1 className="text-base font-semibold mt-[0.4rem]">
|
||||||
|
{t("create-folder")}
|
||||||
|
</h1>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
{t("name")}
|
||||||
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center py-[1rem] ">
|
||||||
|
<button
|
||||||
|
onClick={handleCreateFolder}
|
||||||
|
disabled={isLoader}
|
||||||
|
className="op-btn op-btn-primary op-btn-sm"
|
||||||
|
>
|
||||||
|
<i className="fa-light fa-plus"></i>
|
||||||
|
<span>{t("create")}</span>
|
||||||
|
</button>
|
||||||
|
{onBack && (
|
||||||
|
<div
|
||||||
|
className="op-btn op-btn-seconday op-btn-sm"
|
||||||
|
title={t("back")}
|
||||||
|
onClick={() => onBack()}
|
||||||
|
>
|
||||||
|
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
|
||||||
|
<span className="text-xs">{t("back")}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateFolder;
|
||||||
+1
-1
@@ -45,7 +45,7 @@ const DateFormatSelector = (props) => {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div className="flex flex-col md:flex-row md:gap-4 mt-[0.75rem] text-[12px]">
|
<div className="flex flex-row gap-4 mt-[0.75rem] text-[12px]">
|
||||||
<div className="flex items-center gap-2 ml-2">
|
<div className="flex items-center gap-2 ml-2">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
+89
-97
@@ -105,49 +105,45 @@ const FolderModal = (props) => {
|
|||||||
// `handleCancel` is call when user click on folder name from path/tab in popup
|
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||||
const removeTabListItem = async (e, i) => {
|
const removeTabListItem = async (e, i) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// setEditable(false);
|
|
||||||
if (!isAdd) {
|
setIsLoader(true);
|
||||||
setIsLoader(true);
|
setIsAdd(false);
|
||||||
let folderPtr;
|
if (i !== undefined) {
|
||||||
if (i) {
|
setFolderList([]);
|
||||||
setFolderList([]);
|
const list = tabList.filter((folder, j) => j <= i && folder);
|
||||||
let list = tabList.filter((itm, j) => {
|
const index = list.length - 1;
|
||||||
if (j <= i) {
|
const folderPtr = {
|
||||||
return itm;
|
__type: "Pointer",
|
||||||
}
|
className: props.folderCls,
|
||||||
});
|
objectId: list[index].objectId
|
||||||
let _len = list.length - 1;
|
};
|
||||||
folderPtr = {
|
setTabList(list);
|
||||||
__type: "Pointer",
|
|
||||||
className: props.folderCls,
|
|
||||||
objectId: list[_len].objectId
|
|
||||||
};
|
|
||||||
setTabList(list);
|
|
||||||
} else {
|
|
||||||
setClickFolder({});
|
|
||||||
setFolderList([]);
|
|
||||||
setTabList([]);
|
|
||||||
}
|
|
||||||
fetchFolder(folderPtr);
|
fetchFolder(folderPtr);
|
||||||
|
} else {
|
||||||
|
setClickFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
fetchFolder();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// `handleCreate` is used to open folder creation form in popup
|
// `handleCreate` is used to open folder creation form in popup
|
||||||
const handleCreate = () => {
|
const handleCreate = () => setIsAdd(true);
|
||||||
setIsAdd(!isAdd);
|
const handleBack = () => setIsAdd(false);
|
||||||
};
|
|
||||||
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||||
const handleAddFolder = (newFolder) => {
|
const handleAddFolder = (newFolder) => {
|
||||||
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
|
props.setPdfData((prev) => [...prev, newFolder]);
|
||||||
if (clickFolder && clickFolder.ObjectId) {
|
if (clickFolder && clickFolder.ObjectId) {
|
||||||
fetchFolder({
|
fetchFolder({
|
||||||
__type: "Pointer",
|
__type: "Pointer",
|
||||||
className: props.folderCls,
|
className: props.folderCls,
|
||||||
objectId: clickFolder.ObjectId
|
objectId: newFolder.objectId // clickFolder.ObjectId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fetchFolder();
|
fetchFolder();
|
||||||
}
|
}
|
||||||
handleCreate();
|
setClickFolder({ ObjectId: newFolder.objectId, Name: newFolder.Name });
|
||||||
|
setTabList((prev) => [...prev, newFolder]);
|
||||||
|
handleBack();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="text-xs mt-2">
|
<div className="text-xs mt-2">
|
||||||
@@ -180,54 +176,57 @@ const FolderModal = (props) => {
|
|||||||
))}
|
))}
|
||||||
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 mb-3">
|
<div className={`${!isAdd ? "mb-3" : ""} mt-2`}>
|
||||||
<div className="max-h-[210px] overflow-auto">
|
{!isAdd && (
|
||||||
{!isAdd && folderList.length > 0
|
<div className="max-h-[210px] overflow-auto">
|
||||||
? folderList.map((folder) => (
|
{folderList.length > 0
|
||||||
<div
|
? folderList.map((folder) => (
|
||||||
key={folder.objectId}
|
<div
|
||||||
className={`${
|
key={folder.objectId}
|
||||||
folder.Type === "Folder"
|
className={`${
|
||||||
? "cursor-pointer"
|
folder.Type === "Folder"
|
||||||
: "cursor-default"
|
? "cursor-pointer"
|
||||||
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
: "cursor-default"
|
||||||
onClick={() =>
|
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
||||||
folder.Type === "Folder" && handleSelect(folder)
|
onClick={() =>
|
||||||
}
|
folder.Type === "Folder" && handleSelect(folder)
|
||||||
>
|
}
|
||||||
<div className="flex items-center gap-2">
|
>
|
||||||
{folder.Type === "Folder" ? (
|
<div className="flex items-center gap-2">
|
||||||
<svg
|
{folder.Type === "Folder" ? (
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<svg
|
||||||
viewBox="0 0 512 512"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
viewBox="0 0 512 512"
|
||||||
>
|
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
>
|
||||||
</svg>
|
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||||
) : (
|
</svg>
|
||||||
<svg
|
) : (
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<svg
|
||||||
viewBox="0 0 384 512"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
viewBox="0 0 384 512"
|
||||||
>
|
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
||||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
>
|
||||||
</svg>
|
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||||
)}
|
</svg>
|
||||||
<span className="font-semibold">{folder.Name}</span>
|
)}
|
||||||
|
<span className="font-semibold">{folder.Name}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))
|
||||||
))
|
: !isLoader && (
|
||||||
: !isLoader && (
|
<div className="text-base-content text-center my-2">
|
||||||
<div className="text-base-content text-center my-2">
|
{t("no-data")}
|
||||||
{t("no-data")}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
{isAdd && (
|
{isAdd && (
|
||||||
<CreateFolder
|
<CreateFolder
|
||||||
parentFolderId={clickFolder && clickFolder.ObjectId}
|
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||||
folderCls={props.folderCls}
|
folderCls={props.folderCls}
|
||||||
onSuccess={handleAddFolder}
|
onSuccess={handleAddFolder}
|
||||||
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
@@ -238,33 +237,26 @@ const FolderModal = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
{!isAdd && (
|
||||||
<div
|
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||||
className="op-btn op-btn-seconday op-btn-sm"
|
<div
|
||||||
title={t("save-here")}
|
className="op-btn op-btn-primary op-btn-sm"
|
||||||
onClick={handleCreate}
|
title={t("save-here")}
|
||||||
>
|
onClick={handleSubmit}
|
||||||
{isAdd ? (
|
>
|
||||||
<>
|
<i className="fa-light fa-save" aria-hidden="true"></i>
|
||||||
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
|
{t("save-here")}
|
||||||
<span className="text-xs">{t("back")}</span>
|
</div>
|
||||||
</>
|
<div
|
||||||
) : (
|
className="op-btn op-btn-seconday op-btn-sm"
|
||||||
<>
|
title={t("add-folder")}
|
||||||
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
onClick={handleCreate}
|
||||||
<span className="">{t("add-folder")}</span>
|
>
|
||||||
</>
|
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
||||||
)}
|
<span className="">{t("add-folder")}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
)}
|
||||||
className="op-btn op-btn-primary op-btn-sm"
|
|
||||||
title={t("save-here")}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
<i className="fa-light fa-save" aria-hidden="true"></i>
|
|
||||||
{t("save-here")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
+89
-97
@@ -125,50 +125,46 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
// `handleCancel` is call when user click on folder name from path/tab in popup
|
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||||
const removeTabListItem = async (e, i) => {
|
const removeTabListItem = async (e, i) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// setEditable(false);
|
setIsLoader(true);
|
||||||
if (!isAdd) {
|
setIsAdd(false);
|
||||||
setIsLoader(true);
|
if (i !== undefined) {
|
||||||
let folderPtr;
|
setFolderList([]);
|
||||||
if (i) {
|
const list = tabList.filter((folder, j) => j <= i && folder);
|
||||||
setFolderList([]);
|
const index = list.length - 1;
|
||||||
let list = tabList.filter((itm, j) => {
|
const folderPtr = {
|
||||||
if (j <= i) {
|
__type: "Pointer",
|
||||||
return itm;
|
className: folderCls,
|
||||||
}
|
objectId: list[index].objectId
|
||||||
});
|
};
|
||||||
let _len = list.length - 1;
|
|
||||||
folderPtr = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: folderCls,
|
|
||||||
objectId: list[_len].objectId
|
|
||||||
};
|
|
||||||
setTabList(list);
|
|
||||||
} else {
|
|
||||||
setClickFolder({});
|
|
||||||
setSelectedFolder({});
|
|
||||||
setFolderList([]);
|
|
||||||
setTabList([]);
|
|
||||||
}
|
|
||||||
fetchFolder(folderPtr);
|
fetchFolder(folderPtr);
|
||||||
|
setTabList(list);
|
||||||
|
} else {
|
||||||
|
setClickFolder({});
|
||||||
|
setSelectedFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
fetchFolder();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// `handleCreate` is used to open folder creation form in popup
|
// `handleCreate` is used to open folder creation form in popup
|
||||||
const handleCreate = () => {
|
const handleCreate = () => setIsAdd(true);
|
||||||
setIsAdd(!isAdd);
|
const handleBack = () => setIsAdd(false);
|
||||||
};
|
|
||||||
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||||
const handleAddFolder = () => {
|
const handleAddFolder = (newFolder) => {
|
||||||
setFolderList([]);
|
setFolderList([]);
|
||||||
if (clickFolder && clickFolder.ObjectId) {
|
if (clickFolder && clickFolder.ObjectId) {
|
||||||
fetchFolder({
|
fetchFolder({
|
||||||
__type: "Pointer",
|
__type: "Pointer",
|
||||||
className: folderCls,
|
className: folderCls,
|
||||||
objectId: clickFolder.ObjectId
|
objectId: newFolder.objectId // clickFolder.ObjectId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fetchFolder();
|
fetchFolder();
|
||||||
}
|
}
|
||||||
handleCreate();
|
setClickFolder({ ObjectId: newFolder.objectId, Name: newFolder.Name });
|
||||||
|
setTabList((prev) => [...prev, newFolder]);
|
||||||
|
handleBack();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="text-xs mt-2 ">
|
<div className="text-xs mt-2 ">
|
||||||
@@ -246,53 +242,56 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<div className="max-h-[210px] overflow-auto">
|
{!isAdd && (
|
||||||
{!isAdd && folderList.length > 0
|
<div className="max-h-[210px] overflow-auto">
|
||||||
? folderList.map((folder) => (
|
{folderList.length > 0
|
||||||
<div
|
? folderList.map((folder) => (
|
||||||
key={folder.objectId}
|
<div
|
||||||
className={`${
|
key={folder.objectId}
|
||||||
folder.Type === "Folder"
|
className={`${
|
||||||
? "cursor-pointer"
|
folder.Type === "Folder"
|
||||||
: "cursor-default"
|
? "cursor-pointer"
|
||||||
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
: "cursor-default"
|
||||||
onClick={() =>
|
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
||||||
folder.Type === "Folder" && handleSelect(folder)
|
onClick={() =>
|
||||||
}
|
folder.Type === "Folder" && handleSelect(folder)
|
||||||
>
|
}
|
||||||
<div className="flex items-center gap-2">
|
>
|
||||||
{folder.Type === "Folder" ? (
|
<div className="flex items-center gap-2">
|
||||||
<svg
|
{folder.Type === "Folder" ? (
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<svg
|
||||||
viewBox="0 0 512 512"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
viewBox="0 0 512 512"
|
||||||
>
|
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
>
|
||||||
</svg>
|
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||||
) : (
|
</svg>
|
||||||
<svg
|
) : (
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<svg
|
||||||
viewBox="0 0 384 512"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
viewBox="0 0 384 512"
|
||||||
>
|
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
||||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
>
|
||||||
</svg>
|
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||||
)}
|
</svg>
|
||||||
<span className="font-semibold">{folder.Name}</span>
|
)}
|
||||||
|
<span className="font-semibold">{folder.Name}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))
|
||||||
))
|
: !isLoader && (
|
||||||
: !isLoader && (
|
<div className="text-base-content text-center my-2">
|
||||||
<div className="text-base-content text-center my-2">
|
{t("no-data")}
|
||||||
{t("no-data")}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
{isAdd && (
|
{isAdd && (
|
||||||
<CreateFolder
|
<CreateFolder
|
||||||
parentFolderId={clickFolder && clickFolder.ObjectId}
|
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||||
folderCls={folderCls}
|
folderCls={folderCls}
|
||||||
onSuccess={handleAddFolder}
|
onSuccess={handleAddFolder}
|
||||||
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
@@ -303,33 +302,26 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
{!isAdd && (
|
||||||
<div
|
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||||
className="op-btn op-btn-seconday op-btn-sm"
|
<div
|
||||||
title={t("save-here")}
|
className="op-btn op-btn-primary op-btn-sm"
|
||||||
onClick={handleCreate}
|
title={t("save-here")}
|
||||||
>
|
onClick={handleSubmit}
|
||||||
{isAdd ? (
|
>
|
||||||
<>
|
<i className="fa-light fa-save" aria-hidden="true"></i>
|
||||||
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
|
{t("save-here")}
|
||||||
<span className="text-xs">{t("back")}</span>
|
</div>
|
||||||
</>
|
<div
|
||||||
) : (
|
className="op-btn op-btn-seconday op-btn-sm"
|
||||||
<>
|
title={t("add-folder")}
|
||||||
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
onClick={handleCreate}
|
||||||
<span className="">{t("add-folder")}</span>
|
>
|
||||||
</>
|
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
||||||
)}
|
<span className="">{t("add-folder")}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
)}
|
||||||
className="op-btn op-btn-primary op-btn-sm"
|
|
||||||
title={t("save-here")}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
>
|
|
||||||
<i className="fa-light fa-save" aria-hidden="true"></i>
|
|
||||||
{t("save-here")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
+4
-4
@@ -117,7 +117,9 @@ const SignersInput = (props) => {
|
|||||||
};
|
};
|
||||||
const loadOptions = async (inputValue) => {
|
const loadOptions = async (inputValue) => {
|
||||||
try {
|
try {
|
||||||
const contactRes = await findContact(inputValue);
|
const contactRes = await findContact(
|
||||||
|
inputValue,
|
||||||
|
);
|
||||||
if (contactRes) {
|
if (contactRes) {
|
||||||
const res = JSON.parse(JSON.stringify(contactRes));
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||||
@@ -152,9 +154,7 @@ const SignersInput = (props) => {
|
|||||||
{props.label ? props.label : t("signers")}
|
{props.label ? props.label : t("signers")}
|
||||||
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
<span
|
<span
|
||||||
className={`z-[${
|
className={`z-[${props?.helptextZindex ? props.helptextZindex : 30}] absolute ml-1 text-xs`}
|
||||||
props?.helptextZindex ? props.helptextZindex : 30
|
|
||||||
}] absolute ml-1 text-xs`}
|
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={`${props.label ? props.label : "signers"}-tooltip`}
|
id={`${props.label ? props.label : "signers"}-tooltip`}
|
||||||
+2
-1
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { NavLink } from "react-router";
|
import { NavLink } from "react-router";
|
||||||
|
|
||||||
const Menu = ({ item, isOpen, closeSidebar }) => {
|
const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
+2
-1
@@ -3,7 +3,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { NavLink } from "react-router";
|
import { NavLink } from "react-router";
|
||||||
|
|
||||||
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { title, icon, children } = item;
|
const { title, icon, children } = item;
|
||||||
@@ -7,7 +7,9 @@ import { appInfo } from "./appinfo";
|
|||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import printModule from "print-js";
|
import printModule from "print-js";
|
||||||
import fontkit from "@pdf-lib/fontkit";
|
import fontkit from "@pdf-lib/fontkit";
|
||||||
import { themeColor } from "./const";
|
import {
|
||||||
|
themeColor
|
||||||
|
} from "./const";
|
||||||
import { format, toZonedTime } from "date-fns-tz";
|
import { format, toZonedTime } from "date-fns-tz";
|
||||||
|
|
||||||
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||||
@@ -20,6 +22,35 @@ export const textInputWidget = "text input";
|
|||||||
export const textWidget = "text";
|
export const textWidget = "text";
|
||||||
export const radioButtonWidget = "radio button";
|
export const radioButtonWidget = "radio button";
|
||||||
|
|
||||||
|
|
||||||
|
//function for create list of year for date widget
|
||||||
|
export const range = (start, end, step) => {
|
||||||
|
const range = [];
|
||||||
|
for (let i = start; i <= end; i += step) {
|
||||||
|
range.push(i);
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
//function for get year
|
||||||
|
export const getYear = (date) => {
|
||||||
|
const newYear = new Date(date).getFullYear();
|
||||||
|
return newYear;
|
||||||
|
};
|
||||||
|
export const years = range(1950, getYear(new Date()) + 16, 1);
|
||||||
|
export const months = [
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December"
|
||||||
|
];
|
||||||
export const fileasbytes = async (filepath) => {
|
export const fileasbytes = async (filepath) => {
|
||||||
const response = await fetch(filepath); // Adjust the path accordingly
|
const response = await fetch(filepath); // Adjust the path accordingly
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
@@ -135,12 +166,15 @@ export const pdfNewWidthFun = (divRef) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//`contractUsers` function is used to get contract_User details
|
//`contractUsers` function is used to get contract_User details
|
||||||
export const contractUsers = async () => {
|
export const contractUsers = async (
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
|
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
const accesstoken = localStorage.getItem("accesstoken");
|
const accesstoken =
|
||||||
const token = { "X-Parse-Session-Token": accesstoken };
|
localStorage.getItem("accesstoken");
|
||||||
|
const token =
|
||||||
|
{ "X-Parse-Session-Token": accesstoken };
|
||||||
const headers = {
|
const headers = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -272,53 +306,100 @@ export const selectFormat = (data) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addWidgetOptions = (type, signer) => {
|
export const changeDateToMomentFormat = (format) => {
|
||||||
const defaultOpt = { name: type, status: "required" };
|
switch (format) {
|
||||||
|
case "MM/dd/yyyy":
|
||||||
|
return "L";
|
||||||
|
case "dd-MM-yyyy":
|
||||||
|
return "DD-MM-YYYY";
|
||||||
|
case "dd/MM/yyyy":
|
||||||
|
return "DD/MM/YYYY";
|
||||||
|
case "MMMM dd, yyyy":
|
||||||
|
return "LL";
|
||||||
|
case "dd MMM, yyyy":
|
||||||
|
return "DD MMM, YYYY";
|
||||||
|
case "yyyy-MM-dd":
|
||||||
|
return "YYYY-MM-DD";
|
||||||
|
case "MM-dd-yyyy":
|
||||||
|
return "MM-DD-YYYY";
|
||||||
|
case "MM.dd.yyyy":
|
||||||
|
return "MM.DD.YYYY";
|
||||||
|
case "MMM dd, yyyy":
|
||||||
|
return "MMM DD, YYYY";
|
||||||
|
case "dd MMMM, yyyy":
|
||||||
|
return "DD MMMM, YYYY";
|
||||||
|
default:
|
||||||
|
return "L";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const addWidgetOptions = (type, signer, widgetValue) => {
|
||||||
|
const status = { status: "required" };
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "signature":
|
case "signature":
|
||||||
return defaultOpt;
|
return { ...status, name: "Signature" };
|
||||||
case "stamp":
|
case "stamp":
|
||||||
return defaultOpt;
|
return { ...status, name: "Upload stamp" };
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
return {
|
return {
|
||||||
...defaultOpt,
|
...status,
|
||||||
options: { isReadOnly: false, isHideLabel: false }
|
name: "Checkbox",
|
||||||
|
isReadOnly: false,
|
||||||
|
isHideLabel: false
|
||||||
};
|
};
|
||||||
case textInputWidget:
|
case textInputWidget:
|
||||||
return { ...defaultOpt, isReadOnly: false };
|
return { ...status, name: "Text", isReadOnly: false };
|
||||||
case "initials":
|
case "initials":
|
||||||
return defaultOpt;
|
return { ...status, name: "Initials" };
|
||||||
case "name":
|
case "name":
|
||||||
return { ...defaultOpt };
|
return {
|
||||||
|
...status,
|
||||||
|
name: "Name",
|
||||||
|
defaultValue: widgetValue ? widgetValue : ""
|
||||||
|
};
|
||||||
case "company":
|
case "company":
|
||||||
return { ...defaultOpt };
|
return {
|
||||||
|
...status,
|
||||||
|
name: "Company",
|
||||||
|
defaultValue: widgetValue ? widgetValue : ""
|
||||||
|
};
|
||||||
case "job title":
|
case "job title":
|
||||||
return { ...defaultOpt };
|
return {
|
||||||
|
...status,
|
||||||
|
name: "Job title",
|
||||||
|
defaultValue: widgetValue ? widgetValue : ""
|
||||||
|
};
|
||||||
case "date": {
|
case "date": {
|
||||||
const dateFormat = signer?.DateFormat
|
const dateFormat = signer?.DateFormat
|
||||||
? selectFormat(signer?.DateFormat)
|
? selectFormat(signer?.DateFormat)
|
||||||
: "MM/dd/yyyy";
|
: "MM/dd/yyyy";
|
||||||
return {
|
return {
|
||||||
...defaultOpt,
|
...status,
|
||||||
|
name: "Date",
|
||||||
response: getDate(signer?.DateFormat),
|
response: getDate(signer?.DateFormat),
|
||||||
validation: { format: dateFormat, type: "date-format" }
|
validation: { format: dateFormat, type: "date-format" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "image":
|
case "image":
|
||||||
return defaultOpt;
|
return { ...status, name: "Upload image" };
|
||||||
case "email":
|
case "email":
|
||||||
return { ...defaultOpt, validation: { type: "email", pattern: "" } };
|
return {
|
||||||
|
...status,
|
||||||
|
name: "Email",
|
||||||
|
validation: { type: "email", pattern: "" },
|
||||||
|
defaultValue: widgetValue ? widgetValue : ""
|
||||||
|
};
|
||||||
case "dropdown":
|
case "dropdown":
|
||||||
return defaultOpt;
|
return { ...status, name: "Dropdown" };
|
||||||
case radioButtonWidget:
|
case radioButtonWidget:
|
||||||
return {
|
return {
|
||||||
...defaultOpt,
|
...status,
|
||||||
|
name: "Radio button",
|
||||||
values: [],
|
values: [],
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
isHideLabel: false
|
isHideLabel: false
|
||||||
};
|
};
|
||||||
case textWidget:
|
case textWidget:
|
||||||
return defaultOpt;
|
return { ...status, name: "Text" };
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -327,30 +408,30 @@ export const addWidgetOptions = (type, signer) => {
|
|||||||
export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
|
export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "signature":
|
case "signature":
|
||||||
return { name: "signature" };
|
return { name: "Signature" };
|
||||||
case "stamp":
|
case "stamp":
|
||||||
return { name: "stamp" };
|
return { name: "Upload stamp" };
|
||||||
case "checkbox":
|
case "checkbox":
|
||||||
return { name: "checkbox" };
|
return { name: "Checkbox" };
|
||||||
case textWidget:
|
case textWidget:
|
||||||
return { name: "text" };
|
return { name: "Text" };
|
||||||
case "initials":
|
case "initials":
|
||||||
return { name: "initials" };
|
return { name: "Initials" };
|
||||||
case "name":
|
case "name":
|
||||||
return {
|
return {
|
||||||
name: "name",
|
name: "Name",
|
||||||
defaultValue: getWidgetValue(type),
|
defaultValue: getWidgetValue(type),
|
||||||
validation: { type: "text", pattern: "" }
|
validation: { type: "text", pattern: "" }
|
||||||
};
|
};
|
||||||
case "company":
|
case "company":
|
||||||
return {
|
return {
|
||||||
name: "company",
|
name: "Company",
|
||||||
defaultValue: getWidgetValue(type),
|
defaultValue: getWidgetValue(type),
|
||||||
validation: { type: "text", pattern: "" }
|
validation: { type: "text", pattern: "" }
|
||||||
};
|
};
|
||||||
case "job title":
|
case "job title":
|
||||||
return {
|
return {
|
||||||
name: "job title",
|
name: "Job title",
|
||||||
defaultValue: getWidgetValue(type),
|
defaultValue: getWidgetValue(type),
|
||||||
validation: { type: "text", pattern: "" }
|
validation: { type: "text", pattern: "" }
|
||||||
};
|
};
|
||||||
@@ -359,16 +440,16 @@ export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
|
|||||||
? selectFormat(owner?.DateFormat)
|
? selectFormat(owner?.DateFormat)
|
||||||
: "MM/dd/yyyy";
|
: "MM/dd/yyyy";
|
||||||
return {
|
return {
|
||||||
name: "date",
|
name: "Date",
|
||||||
response: getDate(owner?.DateFormat),
|
response: getDate(owner?.DateFormat),
|
||||||
validation: { format: dateFormat, type: "date-format" }
|
validation: { format: dateFormat, type: "date-format" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "image":
|
case "image":
|
||||||
return { name: "image" };
|
return { name: "Upload image" };
|
||||||
case "email":
|
case "email":
|
||||||
return {
|
return {
|
||||||
name: "email",
|
name: "Email",
|
||||||
defaultValue: getWidgetValue(type),
|
defaultValue: getWidgetValue(type),
|
||||||
validation: { type: "email", pattern: "" }
|
validation: { type: "email", pattern: "" }
|
||||||
};
|
};
|
||||||
@@ -530,7 +611,7 @@ export const signPdfFun = async (
|
|||||||
documentId,
|
documentId,
|
||||||
signerObjectId,
|
signerObjectId,
|
||||||
objectId,
|
objectId,
|
||||||
widgets
|
widgets,
|
||||||
) => {
|
) => {
|
||||||
let isCustomCompletionMail = false;
|
let isCustomCompletionMail = false;
|
||||||
try {
|
try {
|
||||||
@@ -539,7 +620,10 @@ export const signPdfFun = async (
|
|||||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||||
return { status: "error", message: "User does not exist." };
|
return { status: "error", message: "User does not exist." };
|
||||||
} else {
|
} else {
|
||||||
if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
|
if (
|
||||||
|
tenantDetails?.CompletionBody &&
|
||||||
|
tenantDetails?.CompletionSubject
|
||||||
|
) {
|
||||||
isCustomCompletionMail = true;
|
isCustomCompletionMail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,8 +861,6 @@ export const onChangeInput = (
|
|||||||
userId,
|
userId,
|
||||||
initial,
|
initial,
|
||||||
dateFormat,
|
dateFormat,
|
||||||
isDefaultEmpty,
|
|
||||||
isRadio,
|
|
||||||
fontSize,
|
fontSize,
|
||||||
fontColor
|
fontColor
|
||||||
) => {
|
) => {
|
||||||
@@ -815,21 +897,13 @@ export const onChangeInput = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (isDefaultEmpty) {
|
|
||||||
return {
|
|
||||||
...position,
|
|
||||||
options: {
|
|
||||||
...position.options,
|
|
||||||
response: value,
|
|
||||||
defaultValue: isRadio ? "" : []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
options: {
|
options: {
|
||||||
...position.options,
|
...position.options,
|
||||||
response: value
|
response: value,
|
||||||
|
defaultValue: ""
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -876,7 +950,8 @@ export const onChangeInput = (
|
|||||||
...positionData,
|
...positionData,
|
||||||
options: {
|
options: {
|
||||||
...positionData.options,
|
...positionData.options,
|
||||||
response: value
|
response: value,
|
||||||
|
defaultValue: ""
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -983,21 +1058,21 @@ export const calculateInitialWidthHeight = (widgetData) => {
|
|||||||
document.body.removeChild(span);
|
document.body.removeChild(span);
|
||||||
return { getWidth: width, getHeight: height };
|
return { getWidth: width, getHeight: height };
|
||||||
};
|
};
|
||||||
export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
export const widgetDataValue = (type, value) => {
|
||||||
function widgetDataValue(type) {
|
switch (type) {
|
||||||
switch (type) {
|
case "name":
|
||||||
case "name":
|
return value?.Name;
|
||||||
return value?.Name;
|
case "company":
|
||||||
case "company":
|
return value?.Company;
|
||||||
return value?.Company;
|
case "job title":
|
||||||
case "job title":
|
return value?.JobTitle;
|
||||||
return value?.JobTitle;
|
case "email":
|
||||||
case "email":
|
return value?.Email;
|
||||||
return value?.Email;
|
default:
|
||||||
default:
|
return "";
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
||||||
return signerPos.map((item) => {
|
return signerPos.map((item) => {
|
||||||
if (item.placeHolder && item.placeHolder.length > 0) {
|
if (item.placeHolder && item.placeHolder.length > 0) {
|
||||||
// If there is a nested array, recursively add the field to the last object
|
// If there is a nested array, recursively add the field to the last object
|
||||||
@@ -1022,7 +1097,7 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
|||||||
// Adjust this line to add the desired field
|
// Adjust this line to add the desired field
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const widgetData = widgetDataValue(item.type);
|
const widgetData = widgetDataValue(item.type, value);
|
||||||
if (["name", "company", "job title", "email"].includes(item.type)) {
|
if (["name", "company", "job title", "email"].includes(item.type)) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
@@ -1040,7 +1115,8 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
|||||||
|
|
||||||
//function for embed document id
|
//function for embed document id
|
||||||
export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
// `fontBytes` is used to embed custom font in pdf
|
// `fontBytes` is used to embed custom font in pdf
|
||||||
const fontBytes = await fileasbytes(
|
const fontBytes = await fileasbytes(
|
||||||
"https://cdn.opensignlabs.com/webfonts/times.ttf"
|
"https://cdn.opensignlabs.com/webfonts/times.ttf"
|
||||||
@@ -1049,7 +1125,8 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
|||||||
const font = await pdfDoc.embedFont(fontBytes, { subset: true });
|
const font = await pdfDoc.embedFont(fontBytes, { subset: true });
|
||||||
for (let i = 0; i < allPages; i++) {
|
for (let i = 0; i < allPages; i++) {
|
||||||
const fontSize = 10;
|
const fontSize = 10;
|
||||||
const textContent = documentId && `${appName} DocumentId: ${documentId} `;
|
const textContent =
|
||||||
|
documentId && `${appName} DocumentId: ${documentId} `;
|
||||||
const pages = pdfDoc.getPages();
|
const pages = pdfDoc.getPages();
|
||||||
const page = pages[i];
|
const page = pages[i];
|
||||||
try {
|
try {
|
||||||
@@ -1155,7 +1232,6 @@ export function onSaveSign(
|
|||||||
return updatedArray;
|
return updatedArray;
|
||||||
} //condition when user edit signature/initial then updated signature apply all existing drawn signatures
|
} //condition when user edit signature/initial then updated signature apply all existing drawn signatures
|
||||||
else if (isApplyAll) {
|
else if (isApplyAll) {
|
||||||
// console.log("signatureImg",signatureImg)
|
|
||||||
const updatedArray = updateXYposition.map((page) => ({
|
const updatedArray = updateXYposition.map((page) => ({
|
||||||
...page,
|
...page,
|
||||||
pos: page.pos.map(
|
pos: page.pos.map(
|
||||||
@@ -1874,7 +1950,9 @@ export const placeholderHeight = (pos) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//function for getting contracts_contactbook details
|
//function for getting contracts_contactbook details
|
||||||
export const contactBook = async (objectId) => {
|
export const contactBook = async (
|
||||||
|
objectId,
|
||||||
|
) => {
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.get(
|
.get(
|
||||||
`${localStorage.getItem(
|
`${localStorage.getItem(
|
||||||
@@ -1884,7 +1962,8 @@ export const contactBook = async (objectId) => {
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
"X-Parse-Session-Token":
|
||||||
|
localStorage.getItem("accesstoken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1902,9 +1981,12 @@ export const contactBook = async (objectId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//function for getting document details from contract_Documents class
|
//function for getting document details from contract_Documents class
|
||||||
export const contractDocument = async (documentId) => {
|
export const contractDocument = async (
|
||||||
|
documentId,
|
||||||
|
) => {
|
||||||
const data = { docId: documentId };
|
const data = { docId: documentId };
|
||||||
const token = { sessionToken: localStorage.getItem("accesstoken") };
|
const token =
|
||||||
|
{ sessionToken: localStorage.getItem("accesstoken") };
|
||||||
const documentDeatils = await axios
|
const documentDeatils = await axios
|
||||||
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
|
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -1993,26 +2075,12 @@ export const addDefaultSignatureImg = (xyPosition, defaultSignImg, type) => {
|
|||||||
return xyDefaultPos;
|
return xyDefaultPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for create list of year for date widget
|
|
||||||
export const range = (start, end, step) => {
|
|
||||||
const range = [];
|
|
||||||
for (let i = start; i <= end; i += step) {
|
|
||||||
range.push(i);
|
|
||||||
}
|
|
||||||
return range;
|
|
||||||
};
|
|
||||||
//function for get month
|
//function for get month
|
||||||
export const getMonth = (date) => {
|
export const getMonth = (date) => {
|
||||||
const newMonth = new Date(date).getMonth();
|
const newMonth = new Date(date).getMonth();
|
||||||
return newMonth;
|
return newMonth;
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for get year
|
|
||||||
export const getYear = (date) => {
|
|
||||||
const newYear = new Date(date).getFullYear();
|
|
||||||
return newYear;
|
|
||||||
};
|
|
||||||
|
|
||||||
//function to create/copy widget next to already dropped widget
|
//function to create/copy widget next to already dropped widget
|
||||||
export const handleCopyNextToWidget = (
|
export const handleCopyNextToWidget = (
|
||||||
position,
|
position,
|
||||||
@@ -2088,31 +2156,36 @@ export const getFileName = (fileUrl) => {
|
|||||||
|
|
||||||
//fetch tenant app logo from `partners_Tenant` class by domain name
|
//fetch tenant app logo from `partners_Tenant` class by domain name
|
||||||
export const getAppLogo = async () => {
|
export const getAppLogo = async () => {
|
||||||
const domain = window.location.host;
|
const domain = window.location.host;
|
||||||
try {
|
try {
|
||||||
const tenant = await Parse.Cloud.run("getlogobydomain", {
|
const tenant = await Parse.Cloud.run("getlogobydomain", {
|
||||||
domain: domain
|
domain: domain
|
||||||
});
|
});
|
||||||
if (tenant) {
|
if (tenant) {
|
||||||
localStorage.setItem("appname", "OpenSign™");
|
localStorage.setItem("appname", "OpenSign™");
|
||||||
return { logo: tenant?.logo, user: tenant?.user };
|
return { logo: tenant?.logo, user: tenant?.user };
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in getlogo ", err);
|
||||||
|
if (err?.message?.includes("valid JSON")) {
|
||||||
|
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
|
||||||
|
} else {
|
||||||
|
return { logo: appInfo.applogo, user: "exist" };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.log("err in getlogo ", err);
|
|
||||||
if (err?.message?.includes("valid JSON")) {
|
|
||||||
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
|
|
||||||
} else {
|
|
||||||
return { logo: appInfo.applogo, user: "exist" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
export const getTenantDetails = async (objectId, contactId) => {
|
export const getTenantDetails = async (
|
||||||
|
objectId,
|
||||||
|
contactId
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
|
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
const accesstoken = localStorage.getItem("accesstoken");
|
const accesstoken = localStorage.getItem("accesstoken");
|
||||||
const token = { "X-Parse-Session-Token": accesstoken };
|
const token =
|
||||||
const data = { userId: objectId, contactId: contactId };
|
{ "X-Parse-Session-Token": accesstoken };
|
||||||
|
const data =
|
||||||
|
{ userId: objectId, contactId: contactId };
|
||||||
const res = await axios.post(url, data, {
|
const res = await axios.post(url, data, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -2193,7 +2266,7 @@ export const handleSendOTP = async (email) => {
|
|||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||||
};
|
};
|
||||||
const body = {
|
const body = {
|
||||||
email: email
|
email: email,
|
||||||
};
|
};
|
||||||
await axios.post(url, body, { headers: headers });
|
await axios.post(url, body, { headers: headers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2201,7 +2274,8 @@ export const handleSendOTP = async (email) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const fetchUrl = async (url, pdfName) => {
|
export const fetchUrl = async (url, pdfName) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -2215,7 +2289,11 @@ export const fetchUrl = async (url, pdfName) => {
|
|||||||
console.error("Error downloading the file:", error);
|
console.error("Error downloading the file:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const getSignedUrl = async (pdfUrl, docId, templateId) => {
|
export const getSignedUrl = async (
|
||||||
|
pdfUrl,
|
||||||
|
docId,
|
||||||
|
templateId
|
||||||
|
) => {
|
||||||
//use only axios here due to public template sign
|
//use only axios here due to public template sign
|
||||||
const axiosRes = await axios.post(
|
const axiosRes = await axios.post(
|
||||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||||
@@ -2276,7 +2354,10 @@ export const handleDownloadPdf = async (
|
|||||||
setIsDownloading && setIsDownloading("pdf");
|
setIsDownloading && setIsDownloading("pdf");
|
||||||
const docId = pdfDetails?.[0]?.objectId || "";
|
const docId = pdfDetails?.[0]?.objectId || "";
|
||||||
try {
|
try {
|
||||||
const url = await getSignedUrl(pdfUrl, docId);
|
const url = await getSignedUrl(
|
||||||
|
pdfUrl,
|
||||||
|
docId,
|
||||||
|
);
|
||||||
await fetchUrl(url, pdfName);
|
await fetchUrl(url, pdfName);
|
||||||
setIsDownloading && setIsDownloading("");
|
setIsDownloading && setIsDownloading("");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -2306,7 +2387,7 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
|
|||||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||||
{
|
{
|
||||||
url: pdfUrl,
|
url: pdfUrl,
|
||||||
docId: docId
|
docId: docId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -2350,7 +2431,8 @@ export const handleDownloadCertificate = async (
|
|||||||
setIsDownloading,
|
setIsDownloading,
|
||||||
isZip
|
isZip
|
||||||
) => {
|
) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||||
try {
|
try {
|
||||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||||
@@ -2434,13 +2516,14 @@ export const handleDownloadCertificate = async (
|
|||||||
export function escapeRegExp(string) {
|
export function escapeRegExp(string) {
|
||||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
|
||||||
}
|
}
|
||||||
export async function findContact(value) {
|
export async function findContact(
|
||||||
|
value,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const baseURL = localStorage.getItem("baseUrl");
|
const baseURL = localStorage.getItem("baseUrl");
|
||||||
const url = `${baseURL}functions/getsigners`;
|
const url = `${baseURL}functions/getsigners`;
|
||||||
const token = {
|
const token =
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||||
};
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
@@ -2652,20 +2735,23 @@ export function base64ToArrayBuffer(base64) {
|
|||||||
return bytes.buffer;
|
return bytes.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const convertBase64ToFile = async (pdfName, pdfBase64) => {
|
export const convertBase64ToFile = async (
|
||||||
|
pdfName,
|
||||||
|
pdfBase64,
|
||||||
|
) => {
|
||||||
const fileName = sanitizeFileName(pdfName) + ".pdf";
|
const fileName = sanitizeFileName(pdfName) + ".pdf";
|
||||||
try {
|
try {
|
||||||
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
|
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
|
||||||
// Save the Parse File if needed
|
// Save the Parse File if needed
|
||||||
const pdfData = await pdfFile.save();
|
const pdfData = await pdfFile.save();
|
||||||
const pdfUrl = pdfData.url();
|
const pdfUrl = pdfData.url();
|
||||||
const fileRes = await getSecureUrl(pdfUrl);
|
const fileRes = await getSecureUrl(pdfUrl);
|
||||||
if (fileRes?.url) {
|
if (fileRes?.url) {
|
||||||
return fileRes.url;
|
return fileRes.url;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error in convertbase64tofile", e);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log("error in convertbase64tofile", e);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
|
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
|
||||||
setScale(scale + 0.1 * scale);
|
setScale(scale + 0.1 * scale);
|
||||||
@@ -2955,10 +3041,13 @@ export const flattenPdf = async (pdfFile) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mailTemplate = (param) => {
|
export const mailTemplate = (param) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
|
"OpenSign™";
|
||||||
|
const logo =
|
||||||
|
`<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
|
||||||
|
|
||||||
const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
|
const opurl =
|
||||||
|
` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
|
||||||
|
|
||||||
const subject = `${param.senderName} has requested you to sign "${param.title}"`;
|
const subject = `${param.senderName} has requested you to sign "${param.title}"`;
|
||||||
const body =
|
const body =
|
||||||
@@ -3000,3 +3089,75 @@ export function formatDateTime(date, dateFormat, timeZone, is12Hour) {
|
|||||||
)
|
)
|
||||||
: formatTimeInTimezone(date, timeZone);
|
: formatTimeInTimezone(date, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateDateWidgetsRes = (documentData, signerId, journey) => {
|
||||||
|
const extUser =
|
||||||
|
localStorage.getItem("Extand_Class");
|
||||||
|
const contactUser = documentData?.Signers.find(
|
||||||
|
(data) => data.objectId === signerId
|
||||||
|
);
|
||||||
|
const placeHolders = documentData?.Placeholders;
|
||||||
|
const userDetails = extUser ? JSON.parse(extUser)[0] : contactUser;
|
||||||
|
return placeHolders?.map((item) => {
|
||||||
|
if (item?.signerObjId === signerId) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
placeHolder: item?.placeHolder?.map((ph) => ({
|
||||||
|
...ph,
|
||||||
|
pos: ph?.pos?.map((widget) => {
|
||||||
|
// only for date widgets *and* missing response
|
||||||
|
if (widget.type === "date" && !widget.options.response) {
|
||||||
|
return {
|
||||||
|
...widget,
|
||||||
|
options: {
|
||||||
|
...widget.options,
|
||||||
|
response: getDate(
|
||||||
|
changeDateToMomentFormat(widget.options.validation.format)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
["name", "email", "job title", "company"].includes(widget.type) &&
|
||||||
|
!widget.options.defaultValue &&
|
||||||
|
!widget.options.response
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...widget,
|
||||||
|
options: {
|
||||||
|
...widget.options,
|
||||||
|
response: widgetDataValue(widget.type, userDetails)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return widget;
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for show checked checkbox
|
||||||
|
export const selectCheckbox = (ind, selectedCheckbox) => {
|
||||||
|
if (selectedCheckbox && selectedCheckbox?.length > 0) {
|
||||||
|
const isCheck = selectedCheckbox?.some((data) => data === ind);
|
||||||
|
return isCheck || false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const checkRegularExpress = (validateType, setValidatePlaceholder) => {
|
||||||
|
switch (validateType) {
|
||||||
|
case "email":
|
||||||
|
setValidatePlaceholder("demo@gmail.com");
|
||||||
|
break;
|
||||||
|
case "number":
|
||||||
|
setValidatePlaceholder("12345");
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
setValidatePlaceholder("please enter text");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setValidatePlaceholder("please enter value");
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -11,9 +11,6 @@ export const appInfo = {
|
|||||||
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
|
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
|
||||||
baseUrl: serverUrl_fn(),
|
baseUrl: serverUrl_fn(),
|
||||||
defaultRole: "contracts_User",
|
defaultRole: "contracts_User",
|
||||||
fbAppId: process.env.REACT_APP_FBAPPID
|
|
||||||
? `${process.env.REACT_APP_FBAPPID}`
|
|
||||||
: "",
|
|
||||||
fev_Icon:
|
fev_Icon:
|
||||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC",
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC",
|
||||||
googleClietId: process.env.REACT_APP_GOOGLECLIENTID
|
googleClietId: process.env.REACT_APP_GOOGLECLIENTID
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
export default function linkURL(url) {
|
|
||||||
let newURL = (url && decodeURIComponent(url)) || "";
|
|
||||||
return newURL.substring(newURL.indexOf(".com/") + 5, newURL.indexOf("_"));
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,9 @@ body {
|
|||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
/* Firefox */
|
/* Firefox */
|
||||||
}
|
}
|
||||||
|
.react-datepicker-popper {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 766px) {
|
@media screen and (max-width: 766px) {
|
||||||
.reactour__close {
|
.reactour__close {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import "./index.css";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { store } from "./redux/store";
|
import { store } from "./redux/store";
|
||||||
import { CookiesProvider } from "react-cookie";
|
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
import { TouchBackend } from "react-dnd-touch-backend";
|
import { TouchBackend } from "react-dnd-touch-backend";
|
||||||
import {
|
import {
|
||||||
@@ -13,15 +12,14 @@ import {
|
|||||||
MouseTransition,
|
MouseTransition,
|
||||||
Preview
|
Preview
|
||||||
} from "react-dnd-multi-backend";
|
} from "react-dnd-multi-backend";
|
||||||
import DragElement from "./components/pdf/DragElement";
|
import DragElement from "./components/pdf/DragElement.jsx";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import "./polyfills";
|
import "./polyfills";
|
||||||
import { serverUrl_fn } from "./constant/appinfo";
|
import { serverUrl_fn } from "./constant/appinfo";
|
||||||
import "./i18n";
|
import "./i18n";
|
||||||
|
|
||||||
const appId = process.env.REACT_APP_APPID
|
const appId =
|
||||||
? process.env.REACT_APP_APPID
|
import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign";
|
||||||
: "opensign";
|
|
||||||
const serverUrl = serverUrl_fn();
|
const serverUrl = serverUrl_fn();
|
||||||
Parse.initialize(appId);
|
Parse.initialize(appId);
|
||||||
Parse.serverURL = serverUrl;
|
Parse.serverURL = serverUrl;
|
||||||
@@ -55,14 +53,13 @@ const generatePreview = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<CookiesProvider defaultSetOptions={{ path: "/" }}>
|
<Provider store={store}>
|
||||||
<Provider store={store}>
|
<DndProvider options={HTML5toTouch}>
|
||||||
<DndProvider options={HTML5toTouch}>
|
<Preview>{generatePreview}</Preview>
|
||||||
<Preview>{generatePreview}</Preview>
|
<App />
|
||||||
<App />
|
</DndProvider>
|
||||||
</DndProvider>
|
</Provider>
|
||||||
</Provider>
|
|
||||||
</CookiesProvider>
|
|
||||||
);
|
);
|
||||||
@@ -219,7 +219,6 @@ export default function reportJson(id) {
|
|||||||
btnId: "44534",
|
btnId: "44534",
|
||||||
hoverLabel: "option",
|
hoverLabel: "option",
|
||||||
btnColor: "",
|
btnColor: "",
|
||||||
restrictBtn: true,
|
|
||||||
textColor: "black",
|
textColor: "black",
|
||||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||||
action: "option",
|
action: "option",
|
||||||
@@ -228,9 +227,18 @@ export default function reportJson(id) {
|
|||||||
btnId: "0940",
|
btnId: "0940",
|
||||||
btnLabel: "Save as template",
|
btnLabel: "Save as template",
|
||||||
hoverLabel: "Save as template",
|
hoverLabel: "Save as template",
|
||||||
|
restrictBtn: true,
|
||||||
btnIcon: "fa-light fa-envelope",
|
btnIcon: "fa-light fa-envelope",
|
||||||
redirectUrl: "",
|
redirectUrl: "",
|
||||||
action: "saveastemplate"
|
action: "saveastemplate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "8440",
|
||||||
|
btnLabel: "Fix & resend",
|
||||||
|
hoverLabel: "Fix & resend",
|
||||||
|
btnIcon: "fa-light fa-paper-plane",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "recreatedocument"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -525,9 +533,9 @@ export default function reportJson(id) {
|
|||||||
btnId: "1873",
|
btnId: "1873",
|
||||||
btnLabel: "Share with team",
|
btnLabel: "Share with team",
|
||||||
hoverLabel: "Share with team",
|
hoverLabel: "Share with team",
|
||||||
btnIcon: "fa-light fa-share-nodes",
|
btnIcon: "fa-light fa-user-group",
|
||||||
redirectUrl: "",
|
redirectUrl: "",
|
||||||
action: "sharewith"
|
action: "sharewithteam"
|
||||||
});
|
});
|
||||||
return newItem;
|
return newItem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const dashboardJson = [
|
|||||||
queryType: "",
|
queryType: "",
|
||||||
class: "contracts_Document",
|
class: "contracts_Document",
|
||||||
query:
|
query:
|
||||||
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"SignedUrl":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
|
'where={"Type":null,"Signers":{"#*exists":true},"Placeholders":{"#*exists":true},"SignedUrl":{"#*exists":true},"IsCompleted":false,"IsDeclined":false,"IsArchive":null,"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&keys=Name,ExpiryDate,SignedUrl,Signers&count=1',
|
||||||
key: "count",
|
key: "count",
|
||||||
Redirect_type: "Report",
|
Redirect_type: "Report",
|
||||||
Redirect_id: "1MwEuxLEkF",
|
Redirect_id: "1MwEuxLEkF",
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import Header from "../components/Header";
|
|||||||
import Footer from "../components/Footer";
|
import Footer from "../components/Footer";
|
||||||
import Sidebar from "../components/sidebar/Sidebar";
|
import Sidebar from "../components/sidebar/Sidebar";
|
||||||
import { useWindowSize } from "../hook/useWindowSize";
|
import { useWindowSize } from "../hook/useWindowSize";
|
||||||
import Tour from "reactour";
|
import Tour from "../primitives/Tour";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import { useNavigate, useLocation, Outlet } from "react-router";
|
import { useNavigate, useLocation, Outlet } from "react-router";
|
||||||
import { useCookies } from "react-cookie";
|
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
import { showHeader } from "../redux/reducers/showHeader";
|
import { showHeader } from "../redux/reducers/showHeader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const HomeLayout = () => {
|
const HomeLayout = () => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -29,7 +29,6 @@ const HomeLayout = () => {
|
|||||||
const [isTour, setIsTour] = useState(false);
|
const [isTour, setIsTour] = useState(false);
|
||||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||||
const [tourConfigs, setTourConfigs] = useState([]);
|
const [tourConfigs, setTourConfigs] = useState([]);
|
||||||
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
|
||||||
|
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
|
||||||
@@ -53,8 +52,8 @@ const HomeLayout = () => {
|
|||||||
});
|
});
|
||||||
if (user) {
|
if (user) {
|
||||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||||
setIsUserValid(true);
|
setIsUserValid(true);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
} else {
|
} else {
|
||||||
setIsUserValid(false);
|
setIsUserValid(false);
|
||||||
}
|
}
|
||||||
@@ -63,38 +62,11 @@ const HomeLayout = () => {
|
|||||||
setIsUserValid(false);
|
setIsUserValid(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
saveCookies();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [tenantId]);
|
}, [tenantId]);
|
||||||
//function to use save data in cookies storage
|
|
||||||
const saveCookies = () => {
|
|
||||||
const main_Domain = window.location.origin;
|
|
||||||
const domainName = window.location.hostname;
|
|
||||||
// Find the index of the first dot in the string
|
|
||||||
const indexOfFirstDot = domainName.indexOf(".");
|
|
||||||
// Remove the first dot and get the substring starting from the next character
|
|
||||||
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
|
|
||||||
const serverUrl = localStorage.getItem("baseUrl");
|
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
|
||||||
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
|
|
||||||
secure: true,
|
|
||||||
domain: updateDomain
|
|
||||||
});
|
|
||||||
setCookie("main_Domain", main_Domain, {
|
|
||||||
secure: true,
|
|
||||||
domain: updateDomain
|
|
||||||
});
|
|
||||||
setCookie("server_url", serverUrl, {
|
|
||||||
secure: true,
|
|
||||||
domain: updateDomain
|
|
||||||
});
|
|
||||||
setCookie("parse_app_id", parseAppId, {
|
|
||||||
secure: true,
|
|
||||||
domain: updateDomain
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const showSidebar = () => {
|
const showSidebar = () => {
|
||||||
setIsOpen((value) => !value);
|
setIsOpen((value) => !value);
|
||||||
dispatch(showHeader(!isOpen));
|
dispatch(showHeader(!isOpen));
|
||||||
@@ -119,12 +91,7 @@ const HomeLayout = () => {
|
|||||||
// const resArr = arr;
|
// const resArr = arr;
|
||||||
const resArr = arr.map((obj, index) => {
|
const resArr = arr.map((obj, index) => {
|
||||||
if (arr.length - 1 === index) {
|
if (arr.length - 1 === index) {
|
||||||
return {
|
return { ...obj };
|
||||||
...obj
|
|
||||||
// actions: () => {
|
|
||||||
// setIsCloseBtn(true);
|
|
||||||
// },
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
@@ -136,27 +103,23 @@ const HomeLayout = () => {
|
|||||||
});
|
});
|
||||||
setTourConfigs([
|
setTourConfigs([
|
||||||
{
|
{
|
||||||
selector: '[data-tut="reactourFirst"]',
|
selector: '[data-tut="nonpresentmask"]',
|
||||||
content: t("tour-mssg.home-layout-1"),
|
content: t("tour-mssg.home-layout-1"),
|
||||||
position: "top"
|
position: "center",
|
||||||
// style: { backgroundColor: "#abd4d2" },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: '[data-tut="tourbutton"]',
|
selector: '[data-tut="tourbutton"]',
|
||||||
content: t("tour-mssg.home-layout-2"),
|
content: t("tour-mssg.home-layout-2"),
|
||||||
position: "top"
|
position: "top"
|
||||||
// style: { backgroundColor: "#abd4d2" },
|
|
||||||
},
|
},
|
||||||
...resArr,
|
...resArr,
|
||||||
{
|
{
|
||||||
selector: '[data-tut="reactourLast"]',
|
selector: '[data-tut="nonpresentmask"]',
|
||||||
content: t("tour-mssg.home-layout-3", { appName }),
|
content: t("tour-mssg.home-layout-3", { appName }),
|
||||||
position: "top"
|
position: "center",
|
||||||
// style: { backgroundColor: "#abd4d2" },
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
checkTourStatus();
|
checkTourStatus();
|
||||||
// console.log("resArr ", resArr);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const closeTour = async () => {
|
const closeTour = async () => {
|
||||||
@@ -96,7 +96,7 @@ const AddAdmin = () => {
|
|||||||
setState({ loading: true });
|
setState({ loading: true });
|
||||||
const userDetails = {
|
const userDetails = {
|
||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email?.toLowerCase()?.replace(/\s/g, ""),
|
||||||
phone: phone,
|
phone: phone,
|
||||||
company: company,
|
company: company,
|
||||||
jobTitle: jobTitle
|
jobTitle: jobTitle
|
||||||
@@ -106,10 +106,10 @@ const AddAdmin = () => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const user = new Parse.User();
|
const user = new Parse.User();
|
||||||
user.set("name", name);
|
user.set("name", name);
|
||||||
user.set("email", email);
|
user.set("email", email?.toLowerCase()?.replace(/\s/g, ""));
|
||||||
user.set("password", password);
|
user.set("password", password);
|
||||||
user.set("phone", phone);
|
user.set("phone", phone);
|
||||||
user.set("username", email);
|
user.set("username", email?.toLowerCase()?.replace(/\s/g, ""));
|
||||||
const userRes = await user.save();
|
const userRes = await user.save();
|
||||||
if (userRes) {
|
if (userRes) {
|
||||||
const params = {
|
const params = {
|
||||||
@@ -117,7 +117,7 @@ const AddAdmin = () => {
|
|||||||
jobTitle: jobTitle,
|
jobTitle: jobTitle,
|
||||||
company: company,
|
company: company,
|
||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email?.toLowerCase()?.replace(/\s/g, ""),
|
||||||
phone: phone,
|
phone: phone,
|
||||||
role: "contracts_Admin",
|
role: "contracts_Admin",
|
||||||
timezone: usertimezone
|
timezone: usertimezone
|
||||||
@@ -176,7 +176,6 @@ const AddAdmin = () => {
|
|||||||
localStorage.setItem("accesstoken", sessionToken);
|
localStorage.setItem("accesstoken", sessionToken);
|
||||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||||
localStorage.setItem("scriptId", true);
|
|
||||||
if (_user.ProfilePic) {
|
if (_user.ProfilePic) {
|
||||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||||
} else {
|
} else {
|
||||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
|||||||
getBase64FromUrl,
|
getBase64FromUrl,
|
||||||
handleDownloadCertificate,
|
handleDownloadCertificate,
|
||||||
handleDownloadPdf,
|
handleDownloadPdf,
|
||||||
handleToPrint
|
handleToPrint,
|
||||||
} from "../constant/Utils";
|
} from "../constant/Utils";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
+5
-3
@@ -7,7 +7,9 @@ import Alert from "../primitives/Alert";
|
|||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
import { emailRegex } from "../constant/const";
|
import {
|
||||||
|
emailRegex,
|
||||||
|
} from "../constant/const";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ function ForgotPassword() {
|
|||||||
if (state.email) {
|
if (state.email) {
|
||||||
const username = state.email;
|
const username = state.email;
|
||||||
try {
|
try {
|
||||||
await Parse.User.requestPasswordReset(username);
|
await Parse.User.requestPasswordReset(username);
|
||||||
setToast({ type: "success", message: t("reset-password-alert-1") });
|
setToast({ type: "success", message: t("reset-password-alert-1") });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err.code);
|
console.log("err ", err.code);
|
||||||
@@ -76,7 +78,7 @@ function ForgotPassword() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err while logging out ", err);
|
console.log("err while logging out ", err);
|
||||||
}
|
}
|
||||||
setImage(appInfo?.applogo || undefined);
|
setImage(appInfo?.applogo || undefined);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -42,7 +42,8 @@ function Form() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Forms = (props) => {
|
const Forms = (props) => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
const inputFileRef = useRef(null);
|
const inputFileRef = useRef(null);
|
||||||
@@ -105,12 +106,12 @@ const Forms = (props) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
const initializeValues = async () => {
|
const initializeValues = async () => {
|
||||||
setFormData((obj) => ({
|
setFormData((obj) => ({
|
||||||
...obj,
|
...obj,
|
||||||
NotifyOnSignatures: true,
|
NotifyOnSignatures: true,
|
||||||
SendinOrder: sendinorder,
|
SendinOrder: sendinorder,
|
||||||
IsTourEnabled: istourenabled
|
IsTourEnabled: istourenabled
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
function getFileAsArrayBuffer(file) {
|
function getFileAsArrayBuffer(file) {
|
||||||
@@ -148,81 +149,17 @@ const Forms = (props) => {
|
|||||||
const name = generatePdfName(16);
|
const name = generatePdfName(16);
|
||||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||||
setfileload(true);
|
setfileload(true);
|
||||||
try {
|
|
||||||
const res = await getFileAsArrayBuffer(files[0]);
|
|
||||||
const flatPdf = await flattenPdf(res);
|
|
||||||
const parseFile = new Parse.File(
|
|
||||||
pdfName,
|
|
||||||
[...flatPdf],
|
|
||||||
"application/pdf"
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await parseFile.save({
|
const res = await getFileAsArrayBuffer(files[0]);
|
||||||
progress: (progressValue, loaded, total, { type }) => {
|
const flatPdf = await flattenPdf(res);
|
||||||
if (type === "upload" && progressValue !== null) {
|
const parseFile = new Parse.File(
|
||||||
const percentCompleted = Math.round(
|
pdfName,
|
||||||
(loaded * 100) / total
|
[...flatPdf],
|
||||||
);
|
"application/pdf"
|
||||||
setpercentage(percentCompleted);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// The response object will contain information about the uploaded file
|
|
||||||
// You can access the URL of the uploaded file using response.url()
|
|
||||||
if (response.url()) {
|
|
||||||
const fileRes = await getSecureUrl(response.url());
|
|
||||||
if (fileRes.url) {
|
|
||||||
setFileUpload(fileRes.url);
|
|
||||||
setfileload(false);
|
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
|
||||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
|
||||||
setFormData((obj) => ({ ...obj, Name: title }));
|
|
||||||
SaveFileSize(size, fileRes.url, tenantId);
|
|
||||||
return fileRes.url;
|
|
||||||
} else {
|
|
||||||
removeFile(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
removeFile(e);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
removeFile(e);
|
|
||||||
console.error("Error uploading file:", error);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err?.message?.includes("is encrypted")) {
|
|
||||||
try {
|
|
||||||
setIsDecrypting(true);
|
|
||||||
const size = files?.[0].size;
|
|
||||||
const name = generatePdfName(16);
|
|
||||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("file", files[0]);
|
|
||||||
formData.append("password", "");
|
|
||||||
const config = {
|
|
||||||
headers: { "content-type": "multipart/form-data" },
|
|
||||||
responseType: "blob"
|
|
||||||
};
|
|
||||||
const response = await axios.post(url, formData, config);
|
|
||||||
const pdfBlob = new Blob([response.data], {
|
|
||||||
type: "application/pdf"
|
|
||||||
});
|
|
||||||
const pdfFile = new File([pdfBlob], name, {
|
|
||||||
type: "application/pdf"
|
|
||||||
});
|
|
||||||
setIsDecrypting(false);
|
|
||||||
setfileload(true);
|
|
||||||
const res = await getFileAsArrayBuffer(pdfFile);
|
|
||||||
const flatPdf = await flattenPdf(res);
|
|
||||||
// Upload the file to Parse Server
|
|
||||||
const parseFile = new Parse.File(
|
|
||||||
name,
|
|
||||||
[...flatPdf],
|
|
||||||
"application/pdf"
|
|
||||||
);
|
|
||||||
|
|
||||||
await parseFile.save({
|
try {
|
||||||
|
const response = await parseFile.save({
|
||||||
progress: (progressValue, loaded, total, { type }) => {
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
if (type === "upload" && progressValue !== null) {
|
if (type === "upload" && progressValue !== null) {
|
||||||
const percentCompleted = Math.round(
|
const percentCompleted = Math.round(
|
||||||
@@ -232,16 +169,16 @@ const Forms = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// The response object will contain information about the uploaded file
|
||||||
// Retrieve the URL of the uploaded file
|
// You can access the URL of the uploaded file using response.url()
|
||||||
if (parseFile.url()) {
|
if (response.url()) {
|
||||||
const fileRes = await getSecureUrl(parseFile.url());
|
const fileRes = await getSecureUrl(response.url());
|
||||||
if (fileRes.url) {
|
if (fileRes.url) {
|
||||||
setFileUpload(fileRes.url);
|
setFileUpload(fileRes.url);
|
||||||
removeFile();
|
setfileload(false);
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||||
setFormData((obj) => ({ ...obj, Name: title }));
|
setFormData((obj) => ({ ...obj, Name: title }));
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
|
||||||
SaveFileSize(size, fileRes.url, tenantId);
|
SaveFileSize(size, fileRes.url, tenantId);
|
||||||
return fileRes.url;
|
return fileRes.url;
|
||||||
} else {
|
} else {
|
||||||
@@ -250,22 +187,88 @@ const Forms = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
removeFile(e);
|
removeFile(e);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
removeFile();
|
removeFile(e);
|
||||||
if (err?.response?.status === 401) {
|
console.error("Error uploading file:", error);
|
||||||
setIsPassword(true);
|
}
|
||||||
} else {
|
} catch (err) {
|
||||||
console.log("Error uploading file: ", err?.response);
|
if (err?.message?.includes("is encrypted")) {
|
||||||
setIsDecrypting(false);
|
try {
|
||||||
e.target.value = "";
|
setIsDecrypting(true);
|
||||||
}
|
const size = files?.[0].size;
|
||||||
|
const name = generatePdfName(16);
|
||||||
|
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", files[0]);
|
||||||
|
formData.append("password", "");
|
||||||
|
const config = {
|
||||||
|
headers: { "content-type": "multipart/form-data" },
|
||||||
|
responseType: "blob"
|
||||||
|
};
|
||||||
|
const response = await axios.post(url, formData, config);
|
||||||
|
const pdfBlob = new Blob([response.data], {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
const pdfFile = new File([pdfBlob], name, {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
setIsDecrypting(false);
|
||||||
|
setfileload(true);
|
||||||
|
const res = await getFileAsArrayBuffer(pdfFile);
|
||||||
|
const flatPdf = await flattenPdf(res);
|
||||||
|
// Upload the file to Parse Server
|
||||||
|
const parseFile = new Parse.File(
|
||||||
|
name,
|
||||||
|
[...flatPdf],
|
||||||
|
"application/pdf"
|
||||||
|
);
|
||||||
|
|
||||||
|
await parseFile.save({
|
||||||
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
|
if (type === "upload" && progressValue !== null) {
|
||||||
|
const percentCompleted = Math.round(
|
||||||
|
(loaded * 100) / total
|
||||||
|
);
|
||||||
|
setpercentage(percentCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retrieve the URL of the uploaded file
|
||||||
|
if (parseFile.url()) {
|
||||||
|
const fileRes = await getSecureUrl(parseFile.url());
|
||||||
|
if (fileRes.url) {
|
||||||
|
setFileUpload(fileRes.url);
|
||||||
|
removeFile();
|
||||||
|
const title = generateTitleFromFilename(
|
||||||
|
files?.[0]?.name
|
||||||
|
);
|
||||||
|
setFormData((obj) => ({ ...obj, Name: title }));
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, fileRes.url, tenantId);
|
||||||
|
return fileRes.url;
|
||||||
|
} else {
|
||||||
|
removeFile(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removeFile(e);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
removeFile();
|
||||||
|
if (err?.response?.status === 401) {
|
||||||
|
setIsPassword(true);
|
||||||
|
} else {
|
||||||
|
console.log("Error uploading file: ", err?.response);
|
||||||
|
setIsDecrypting(false);
|
||||||
|
e.target.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("err ", err);
|
||||||
|
setFileUpload("");
|
||||||
|
removeFile(e);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log("err ", err);
|
|
||||||
setFileUpload("");
|
|
||||||
removeFile(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const isImage = files?.[0]?.type.includes("image/");
|
const isImage = files?.[0]?.type.includes("image/");
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@@ -290,50 +293,50 @@ const Forms = (props) => {
|
|||||||
});
|
});
|
||||||
const size = files?.[0]?.size;
|
const size = files?.[0]?.size;
|
||||||
const name = generatePdfName(16);
|
const name = generatePdfName(16);
|
||||||
const getFile = await pdfDoc.save({
|
const getFile = await pdfDoc.save({
|
||||||
useObjectStreams: false
|
useObjectStreams: false
|
||||||
});
|
|
||||||
setfileload(true);
|
|
||||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
|
||||||
const parseFile = new Parse.File(
|
|
||||||
pdfName,
|
|
||||||
[...getFile],
|
|
||||||
"application/pdf"
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await parseFile.save({
|
|
||||||
progress: (progressValue, loaded, total, { type }) => {
|
|
||||||
if (type === "upload" && progressValue !== null) {
|
|
||||||
const percentCompleted = Math.round(
|
|
||||||
(loaded * 100) / total
|
|
||||||
);
|
|
||||||
setpercentage(percentCompleted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// The response object will contain information about the uploaded file
|
setfileload(true);
|
||||||
// You can access the URL of the uploaded file using response.url()
|
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||||
if (response.url()) {
|
const parseFile = new Parse.File(
|
||||||
const fileRes = await getSecureUrl(response.url());
|
pdfName,
|
||||||
if (fileRes.url) {
|
[...getFile],
|
||||||
setFileUpload(fileRes.url);
|
"application/pdf"
|
||||||
setfileload(false);
|
);
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
|
||||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
try {
|
||||||
setFormData((obj) => ({ ...obj, Name: title }));
|
const response = await parseFile.save({
|
||||||
SaveFileSize(size, fileRes.url, tenantId);
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
return fileRes.url;
|
if (type === "upload" && progressValue !== null) {
|
||||||
|
const percentCompleted = Math.round(
|
||||||
|
(loaded * 100) / total
|
||||||
|
);
|
||||||
|
setpercentage(percentCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// The response object will contain information about the uploaded file
|
||||||
|
// You can access the URL of the uploaded file using response.url()
|
||||||
|
if (response.url()) {
|
||||||
|
const fileRes = await getSecureUrl(response.url());
|
||||||
|
if (fileRes.url) {
|
||||||
|
setFileUpload(fileRes.url);
|
||||||
|
setfileload(false);
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||||
|
setFormData((obj) => ({ ...obj, Name: title }));
|
||||||
|
SaveFileSize(size, fileRes.url, tenantId);
|
||||||
|
return fileRes.url;
|
||||||
|
} else {
|
||||||
|
removeFile(e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
removeFile(e);
|
removeFile(e);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
removeFile(e);
|
removeFile(e);
|
||||||
|
console.error("Error uploading file:", error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
removeFile(e);
|
|
||||||
console.error("Error uploading file:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,11 +411,11 @@ const Forms = (props) => {
|
|||||||
object.set("RemindOnceInEvery", remindOnceInEvery);
|
object.set("RemindOnceInEvery", remindOnceInEvery);
|
||||||
object.set("IsTourEnabled", isTourEnabled);
|
object.set("IsTourEnabled", isTourEnabled);
|
||||||
object.set("TimeToCompleteDays", TimeToCompleteDays);
|
object.set("TimeToCompleteDays", TimeToCompleteDays);
|
||||||
object.set("AllowModifications", false);
|
object.set("AllowModifications", false);
|
||||||
object.set("IsEnableOTP", false);
|
object.set("IsEnableOTP", false);
|
||||||
if (formData.NotifyOnSignatures !== undefined) {
|
if (formData.NotifyOnSignatures !== undefined) {
|
||||||
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
||||||
}
|
}
|
||||||
if (formData?.RedirectUrl) {
|
if (formData?.RedirectUrl) {
|
||||||
object.set("RedirectUrl", formData.RedirectUrl);
|
object.set("RedirectUrl", formData.RedirectUrl);
|
||||||
}
|
}
|
||||||
@@ -451,9 +454,10 @@ const Forms = (props) => {
|
|||||||
setSigners([]);
|
setSigners([]);
|
||||||
setBcc([]);
|
setBcc([]);
|
||||||
setFolder({ ObjectId: "", Name: "" });
|
setFolder({ ObjectId: "", Name: "" });
|
||||||
const notifySign = extUserData?.NotifyOnSignatures
|
const notifySign =
|
||||||
? extUserData?.NotifyOnSignatures
|
extUserData?.NotifyOnSignatures
|
||||||
: true;
|
? extUserData?.NotifyOnSignatures
|
||||||
|
: true;
|
||||||
setFormData({
|
setFormData({
|
||||||
Name: "",
|
Name: "",
|
||||||
Description: "",
|
Description: "",
|
||||||
@@ -529,9 +533,10 @@ const Forms = (props) => {
|
|||||||
setSigners([]);
|
setSigners([]);
|
||||||
setBcc([]);
|
setBcc([]);
|
||||||
setFolder({ ObjectId: "", Name: "" });
|
setFolder({ ObjectId: "", Name: "" });
|
||||||
const notifySign = extUserData?.NotifyOnSignatures
|
const notifySign =
|
||||||
? extUserData?.NotifyOnSignatures
|
extUserData?.NotifyOnSignatures
|
||||||
: true;
|
? extUserData?.NotifyOnSignatures
|
||||||
|
: true;
|
||||||
let obj = {
|
let obj = {
|
||||||
Name: "",
|
Name: "",
|
||||||
Description: "",
|
Description: "",
|
||||||
@@ -585,28 +590,36 @@ const Forms = (props) => {
|
|||||||
type: "application/pdf"
|
type: "application/pdf"
|
||||||
});
|
});
|
||||||
setIsDecrypting(false);
|
setIsDecrypting(false);
|
||||||
const res = await getFileAsArrayBuffer(pdfFile);
|
const res = await getFileAsArrayBuffer(pdfFile);
|
||||||
const flatPdf = await flattenPdf(res);
|
const flatPdf = await flattenPdf(res);
|
||||||
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
||||||
await parseFile.save({
|
await parseFile.save({
|
||||||
progress: (progressValue, loaded, total, { type }) => {
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
if (type === "upload" && progressValue !== null) {
|
if (type === "upload" && progressValue !== null) {
|
||||||
const percentCompleted = Math.round((loaded * 100) / total);
|
const percentCompleted = Math.round((loaded * 100) / total);
|
||||||
setpercentage(percentCompleted);
|
setpercentage(percentCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Retrieve the URL of the uploaded file
|
||||||
|
if (parseFile.url()) {
|
||||||
|
const fileRes = await getSecureUrl(parseFile.url());
|
||||||
|
if (fileRes.url) {
|
||||||
|
setFileUpload(fileRes.url);
|
||||||
|
removeFile();
|
||||||
|
const title = generateTitleFromFilename(formData?.file?.name);
|
||||||
|
setFormData((obj) => ({ ...obj, password: "", Name: title }));
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, fileRes.url, tenantId);
|
||||||
|
return fileRes.url;
|
||||||
|
} else {
|
||||||
|
removeFile();
|
||||||
|
setFormData((prev) => ({ ...prev, password: "" }));
|
||||||
|
setIsDecrypting(false);
|
||||||
|
if (inputFileRef.current) {
|
||||||
|
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
// Retrieve the URL of the uploaded file
|
|
||||||
if (parseFile.url()) {
|
|
||||||
const fileRes = await getSecureUrl(parseFile.url());
|
|
||||||
if (fileRes.url) {
|
|
||||||
setFileUpload(fileRes.url);
|
|
||||||
removeFile();
|
|
||||||
const title = generateTitleFromFilename(formData?.file?.name);
|
|
||||||
setFormData((obj) => ({ ...obj, password: "", Name: title }));
|
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
|
||||||
SaveFileSize(size, fileRes.url, tenantId);
|
|
||||||
return fileRes.url;
|
|
||||||
} else {
|
} else {
|
||||||
removeFile();
|
removeFile();
|
||||||
setFormData((prev) => ({ ...prev, password: "" }));
|
setFormData((prev) => ({ ...prev, password: "" }));
|
||||||
@@ -615,14 +628,6 @@ const Forms = (props) => {
|
|||||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
removeFile();
|
|
||||||
setFormData((prev) => ({ ...prev, password: "" }));
|
|
||||||
setIsDecrypting(false);
|
|
||||||
if (inputFileRef.current) {
|
|
||||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
removeFile();
|
removeFile();
|
||||||
if (err?.response?.status === 401) {
|
if (err?.response?.status === 401) {
|
||||||
@@ -744,7 +749,9 @@ const Forms = (props) => {
|
|||||||
)}
|
)}
|
||||||
<div className="text-xs">
|
<div className="text-xs">
|
||||||
<label className="block">
|
<label className="block">
|
||||||
{`${`${t("report-heading.File")} (${t("file-type")}`}${")"}`}
|
{`${`${t("report-heading.File")} (${t("file-type")}`}${
|
||||||
|
")"
|
||||||
|
}`}
|
||||||
<span className="text-red-500 text-[13px]">*</span>
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
</label>
|
</label>
|
||||||
{fileupload.length > 0 ? (
|
{fileupload.length > 0 ? (
|
||||||
@@ -768,7 +775,9 @@ const Forms = (props) => {
|
|||||||
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
onChange={(e) => handleFileInput(e)}
|
onChange={(e) => handleFileInput(e)}
|
||||||
ref={inputFileRef}
|
ref={inputFileRef}
|
||||||
accept={"application/pdf,image/png,image/jpeg"}
|
accept={
|
||||||
|
"application/pdf,image/png,image/jpeg"
|
||||||
|
}
|
||||||
onInvalid={(e) =>
|
onInvalid={(e) =>
|
||||||
e.target.setCustomValidity(t("input-required"))
|
e.target.setCustomValidity(t("input-required"))
|
||||||
}
|
}
|
||||||
@@ -1058,7 +1067,10 @@ const Forms = (props) => {
|
|||||||
{isAdvanceOpt && (
|
{isAdvanceOpt && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: props.title === "New Template" ? "100px" : "280px"
|
height:
|
||||||
|
props.title === "New Template"
|
||||||
|
? "100px"
|
||||||
|
: "280px"
|
||||||
}}
|
}}
|
||||||
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
|
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
|
||||||
></div>
|
></div>
|
||||||
@@ -1167,7 +1179,11 @@ const Forms = (props) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
<div className="flex flex-col md:flex-row md:gap-4">
|
||||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
<div
|
||||||
|
className={
|
||||||
|
`flex items-center gap-2 ml-2 mb-1`
|
||||||
|
}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
type="radio"
|
type="radio"
|
||||||
@@ -1176,7 +1192,11 @@ const Forms = (props) => {
|
|||||||
/>
|
/>
|
||||||
<div className="text-center">{t("yes")}</div>
|
<div className="text-center">{t("yes")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
<div
|
||||||
|
className={
|
||||||
|
`flex items-center gap-2 ml-2 mb-1`
|
||||||
|
}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
type="radio"
|
type="radio"
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router";
|
import { useNavigate, useParams } from "react-router";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { emailRegex } from "../constant/const";
|
import {
|
||||||
import { contractUsers, saveLanguageInLocal } from "../constant/Utils";
|
emailRegex,
|
||||||
|
} from "../constant/const";
|
||||||
|
import {
|
||||||
|
contractUsers,
|
||||||
|
saveLanguageInLocal
|
||||||
|
} from "../constant/Utils";
|
||||||
import logo from "../assets/images/logo.png";
|
import logo from "../assets/images/logo.png";
|
||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
@@ -10,6 +15,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import SelectLanguage from "../components/pdf/SelectLanguage";
|
import SelectLanguage from "../components/pdf/SelectLanguage";
|
||||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
function GuestLogin() {
|
function GuestLogin() {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
@@ -30,6 +37,8 @@ function GuestLogin() {
|
|||||||
const [contactId, setContactId] = useState(contactBookId);
|
const [contactId, setContactId] = useState(contactBookId);
|
||||||
const [sendmail, setSendmail] = useState();
|
const [sendmail, setSendmail] = useState();
|
||||||
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||||
|
|
||||||
|
|
||||||
const navigateToDoc = async (docId, contactId) => {
|
const navigateToDoc = async (docId, contactId) => {
|
||||||
try {
|
try {
|
||||||
const docDetails = await Parse.Cloud.run("getDocument", {
|
const docDetails = await Parse.Cloud.run("getDocument", {
|
||||||
@@ -53,17 +62,22 @@ function GuestLogin() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleServerUrl();
|
handleServerUrl();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||||
const handleServerUrl = async () => {
|
const handleServerUrl = async () => {
|
||||||
setAppLogo(logo);
|
setAppLogo(logo);
|
||||||
|
|
||||||
localStorage.clear(); // Clears everything
|
localStorage.clear(); // Clears everything
|
||||||
localStorage.setItem("appname", "OpenSign™");
|
localStorage.setItem(
|
||||||
|
"appname",
|
||||||
|
"OpenSign™"
|
||||||
|
);
|
||||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||||
localStorage.setItem("isGuestSigner", true);
|
localStorage.setItem("isGuestSigner", true);
|
||||||
saveLanguageInLocal(i18n);
|
saveLanguageInLocal(i18n);
|
||||||
@@ -115,7 +129,7 @@ function GuestLogin() {
|
|||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
email: email?.toLowerCase()?.replace(/\s/g, "")?.toString(),
|
email: email?.toLowerCase()?.replace(/\s/g, "")?.toString(),
|
||||||
docId: documentId
|
docId: documentId,
|
||||||
};
|
};
|
||||||
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
|
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
|
||||||
if (Otp) {
|
if (Otp) {
|
||||||
@@ -124,6 +138,7 @@ function GuestLogin() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(t("something-went-wrong-mssg"));
|
alert(t("something-went-wrong-mssg"));
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,11 +203,14 @@ function GuestLogin() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("err ", error);
|
console.log("err ", error);
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(t("enter-otp-alert"));
|
alert(t("enter-otp-alert"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleUserData = async (e) => {
|
const handleUserData = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) {
|
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) {
|
||||||
@@ -221,6 +239,7 @@ function GuestLogin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
if (e.target.name === "email") {
|
if (e.target.name === "email") {
|
||||||
setContact((prev) => ({
|
setContact((prev) => ({
|
||||||
@@ -231,9 +250,56 @@ function GuestLogin() {
|
|||||||
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Title title="Request Sign" />
|
<Title title="Request Sign" />
|
||||||
|
|
||||||
|
{/* OTP Verification Modal */}
|
||||||
|
{EnterOTP && (
|
||||||
|
<ModalUi
|
||||||
|
isOpen
|
||||||
|
title={t("otp-verification")}
|
||||||
|
handleClose={() => setEnterOtp(false)}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div className="h-[150px] flex justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={(e) => VerifyOTP(e)}>
|
||||||
|
<div className="px-6 py-3 text-base-content">
|
||||||
|
<label className="mb-2">{t("enter-otp")}</label>
|
||||||
|
<input
|
||||||
|
onInvalid={(e) =>
|
||||||
|
e.target.setCustomValidity(t("input-required"))
|
||||||
|
}
|
||||||
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
|
required
|
||||||
|
type="tel"
|
||||||
|
pattern="[0-9]{4}"
|
||||||
|
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||||
|
placeholder={t("otp-placeholder")}
|
||||||
|
value={OTP}
|
||||||
|
onChange={(e) => setOTP(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-6 mb-3">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
{t("verify")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary ml-2"
|
||||||
|
onClick={(e) => handleSendOTPBtn(e)}
|
||||||
|
>
|
||||||
|
{t("resend")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</ModalUi>
|
||||||
|
)}
|
||||||
|
|
||||||
{isLoading.isLoad ? (
|
{isLoading.isLoad ? (
|
||||||
<LoaderWithMsg isLoading={isLoading} />
|
<LoaderWithMsg isLoading={isLoading} />
|
||||||
) : (
|
) : (
|
||||||
@@ -249,63 +315,34 @@ function GuestLogin() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{contactId ? (
|
{contactId ? (
|
||||||
<>
|
<div className="w-full md:w-[50%] text-base-content">
|
||||||
{!EnterOTP ? (
|
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||||
<div className="w-full md:w-[50%] text-base-content">
|
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
|
||||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
{t("get-otp-alert")}
|
||||||
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
|
</legend>
|
||||||
{t("get-otp-alert")}
|
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||||
</legend>
|
<input
|
||||||
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
type="email"
|
||||||
<input
|
name="email"
|
||||||
type="email"
|
value={email}
|
||||||
name="email"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
|
||||||
value={email}
|
disabled
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
|
/>
|
||||||
disabled
|
</div>
|
||||||
/>
|
<div className="mt-3">
|
||||||
</div>
|
<button
|
||||||
<div className="mt-3">
|
className="op-btn op-btn-primary flex items-center"
|
||||||
<button
|
onClick={(e) => {
|
||||||
className="op-btn op-btn-primary"
|
e.preventDefault();
|
||||||
onClick={(e) => handleSendOTPBtn(e)}
|
SendOtp();
|
||||||
disabled={loading}
|
}}
|
||||||
>
|
disabled={loading}
|
||||||
{loading ? t("loading") : t("get-verification-code")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<form
|
|
||||||
className="w-full md:w-[50%] text-base-content"
|
|
||||||
onSubmit={VerifyOTP}
|
|
||||||
>
|
>
|
||||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
<i className="fa-light fa-message-sms mr-2"></i>
|
||||||
<legend className="text-[12px] text-[#878787] mt-2">
|
{loading ? t("loading") : t("get-verification-code")}
|
||||||
{t("guest-email-alert")}
|
</button>
|
||||||
</legend>
|
</div>
|
||||||
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
|
</div>
|
||||||
<p className="text-sm">{t("enter-verification-code")}</p>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
|
||||||
name="OTP"
|
|
||||||
value={OTP}
|
|
||||||
onChange={(e) => setOTP(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2.5">
|
|
||||||
<button
|
|
||||||
className="op-btn op-btn-primary"
|
|
||||||
type="submit"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
{loading ? t("loading") : t("verify")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full md:w-[50%] text-base-content">
|
<div className="w-full md:w-[50%] text-base-content">
|
||||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||||
@@ -7,7 +7,9 @@ import { NavLink, useNavigate, useLocation } from "react-router";
|
|||||||
import login_img from "../assets/images/login_img.svg";
|
import login_img from "../assets/images/login_img.svg";
|
||||||
import { useWindowSize } from "../hook/useWindowSize";
|
import { useWindowSize } from "../hook/useWindowSize";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import { emailRegex } from "../constant/const";
|
import {
|
||||||
|
emailRegex,
|
||||||
|
} from "../constant/const";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
@@ -22,7 +24,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import SelectLanguage from "../components/pdf/SelectLanguage";
|
import SelectLanguage from "../components/pdf/SelectLanguage";
|
||||||
|
|
||||||
function Login() {
|
function Login() {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -30,17 +33,12 @@ function Login() {
|
|||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
email: "",
|
email: "",
|
||||||
|
password: "",
|
||||||
alertType: "success",
|
alertType: "success",
|
||||||
alertMsg: "",
|
alertMsg: "",
|
||||||
password: "",
|
|
||||||
passwordVisible: false,
|
passwordVisible: false,
|
||||||
mobile: "",
|
|
||||||
phone: "",
|
|
||||||
scanResult: "",
|
|
||||||
baseUrl: localStorage.getItem("baseUrl"),
|
|
||||||
parseAppId: localStorage.getItem("parseAppId"),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
thirdpartyLoader: false
|
thirdpartyLoader: false,
|
||||||
});
|
});
|
||||||
const [userDetails, setUserDetails] = useState({
|
const [userDetails, setUserDetails] = useState({
|
||||||
Company: "",
|
Company: "",
|
||||||
@@ -54,6 +52,18 @@ function Login() {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
const setLocalVar = (user) => {
|
||||||
|
localStorage.setItem("accesstoken", user.sessionToken);
|
||||||
|
localStorage.setItem("UserInformation", JSON.stringify(user));
|
||||||
|
localStorage.setItem("userEmail", user.email);
|
||||||
|
if (user.ProfilePic) {
|
||||||
|
localStorage.setItem("profileImg", user.ProfilePic);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("profileImg", "");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const showToast = (type, msg) => {
|
const showToast = (type, msg) => {
|
||||||
setState({ ...state, loading: false, alertType: type, alertMsg: msg });
|
setState({ ...state, loading: false, alertType: type, alertMsg: msg });
|
||||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||||
@@ -62,7 +72,9 @@ function Login() {
|
|||||||
const app = await getAppLogo();
|
const app = await getAppLogo();
|
||||||
if (app?.error === "invalid_json") {
|
if (app?.error === "invalid_json") {
|
||||||
setErrMsg(t("server-down", { appName: appName }));
|
setErrMsg(t("server-down", { appName: appName }));
|
||||||
} else if (app?.user === "not_exist") {
|
} else if (
|
||||||
|
app?.user === "not_exist"
|
||||||
|
) {
|
||||||
navigate("/addadmin");
|
navigate("/addadmin");
|
||||||
}
|
}
|
||||||
if (app?.logo) {
|
if (app?.logo) {
|
||||||
@@ -87,106 +99,44 @@ function Login() {
|
|||||||
const handleSubmit = async (event) => {
|
const handleSubmit = async (event) => {
|
||||||
localStorage.removeItem("accesstoken");
|
localStorage.removeItem("accesstoken");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!emailRegex.test(state.email)) {
|
if (!emailRegex.test(state.email)) {
|
||||||
alert("Please enter a valid email address.");
|
alert("Please enter a valid email address.");
|
||||||
} else {
|
return;
|
||||||
const { email, password } = state;
|
}
|
||||||
if (email && password) {
|
|
||||||
try {
|
|
||||||
setState({ ...state, loading: true });
|
|
||||||
localStorage.setItem("appLogo", appInfo.applogo);
|
|
||||||
// Pass the username and password to logIn function
|
|
||||||
const user = await Parse.User.logIn(email, password);
|
|
||||||
if (user) {
|
|
||||||
let _user = user.toJSON();
|
|
||||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
|
||||||
localStorage.setItem("userEmail", email);
|
|
||||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
|
||||||
localStorage.setItem("scriptId", true);
|
|
||||||
if (_user.ProfilePic) {
|
|
||||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("profileImg", "");
|
|
||||||
}
|
|
||||||
// Check extended class user role and tenentId
|
|
||||||
try {
|
|
||||||
const userSettings = appInfo.settings;
|
|
||||||
await Parse.Cloud.run("getUserDetails")
|
|
||||||
.then(async (extUser) => {
|
|
||||||
if (extUser) {
|
|
||||||
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
|
|
||||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
|
||||||
if (!IsDisabled) {
|
|
||||||
const userRole = extUser?.get("UserRole");
|
|
||||||
const menu =
|
|
||||||
userRole &&
|
|
||||||
userSettings.find((menu) => menu.role === userRole);
|
|
||||||
if (menu) {
|
|
||||||
const _currentRole = userRole;
|
|
||||||
const redirectUrl =
|
|
||||||
location?.state?.from ||
|
|
||||||
`/${menu.pageType}/${menu.pageId}`;
|
|
||||||
let _role = _currentRole.replace("contracts_", "");
|
|
||||||
localStorage.setItem("_user_role", _role);
|
|
||||||
const checkLanguage = extUser?.get("Language");
|
|
||||||
if (checkLanguage) {
|
|
||||||
checkLanguage && i18n.changeLanguage(checkLanguage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const results = [extUser];
|
const { email, password } = state;
|
||||||
const extUser_str = JSON.stringify(results);
|
if (!email || !password) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.setItem("Extand_Class", extUser_str);
|
try {
|
||||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
setState({ ...state, loading: true });
|
||||||
localStorage.setItem("userEmail", extInfo.Email);
|
localStorage.setItem("appLogo", appInfo.applogo);
|
||||||
localStorage.setItem("username", extInfo.Name);
|
const _user = await Parse.Cloud.run("loginuser", { email, password });
|
||||||
if (extInfo?.TenantId) {
|
if (!_user) {
|
||||||
const tenant = {
|
setState({ ...state, loading: false });
|
||||||
Id: extInfo?.TenantId?.objectId || "",
|
return;
|
||||||
Name: extInfo?.TenantId?.TenantName || ""
|
|
||||||
};
|
|
||||||
localStorage.setItem("TenantId", tenant?.Id);
|
|
||||||
dispatch(showTenant(tenant?.Name));
|
|
||||||
localStorage.setItem("TenantName", tenant?.Name);
|
|
||||||
}
|
|
||||||
localStorage.setItem("PageLanding", menu.pageId);
|
|
||||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
|
||||||
localStorage.setItem("pageType", menu.pageType);
|
|
||||||
setState({ ...state, loading: false });
|
|
||||||
// Redirect to the appropriate URL after successful login
|
|
||||||
navigate(redirectUrl);
|
|
||||||
} else {
|
|
||||||
setState({ ...state, loading: false });
|
|
||||||
setIsModal(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showToast("danger", t("do-not-access-contact-admin"));
|
|
||||||
logOutUser();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showToast("danger", t("user-not-found"));
|
|
||||||
logOutUser();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
showToast("danger", t("something-went-wrong-mssg"));
|
|
||||||
console.error("Error while fetching Follow", error);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
showToast("danger", `${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error while logging in user", error);
|
|
||||||
showToast("danger", "Invalid username/password or region");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Get extended user data (including 2FA status) using cloud function
|
||||||
|
try {
|
||||||
|
await Parse.User.become(_user.sessionToken);
|
||||||
|
setLocalVar(_user);
|
||||||
|
await continueLoginFlow();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error checking 2FA status:", error);
|
||||||
|
showToast("danger", t("something-went-wrong-mssg"));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error while logging in user", error);
|
||||||
|
showToast("danger", "Invalid username/password or region");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setThirdpartyLoader = (value) => {
|
const setThirdpartyLoader = (value) => {
|
||||||
setState({ ...state, thirdpartyLoader: value });
|
setState({ ...state, thirdpartyLoader: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const thirdpartyLoginfn = async (sessionToken) => {
|
const thirdpartyLoginfn = async (sessionToken) => {
|
||||||
const baseUrl = localStorage.getItem("baseUrl");
|
const baseUrl = localStorage.getItem("baseUrl");
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
@@ -201,113 +151,27 @@ function Login() {
|
|||||||
});
|
});
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
let _user = res.data;
|
let _user = res.data;
|
||||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
setLocalVar(_user);
|
||||||
localStorage.setItem("userEmail", _user.email);
|
|
||||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
|
||||||
localStorage.setItem("scriptId", true);
|
|
||||||
if (_user.ProfilePic) {
|
|
||||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("profileImg", "");
|
|
||||||
}
|
|
||||||
// Check extended class user role and tenentId
|
// Check extended class user role and tenentId
|
||||||
try {
|
try {
|
||||||
const userSettings = appInfo.settings;
|
const userSettings = appInfo.settings;
|
||||||
await Parse.Cloud.run("getUserDetails")
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
||||||
.then(async (extUser) => {
|
|
||||||
if (extUser) {
|
|
||||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
|
||||||
if (!IsDisabled) {
|
|
||||||
const userRole = extUser?.get("UserRole");
|
|
||||||
const menu =
|
|
||||||
userRole &&
|
|
||||||
userSettings.find((menu) => menu.role === userRole);
|
|
||||||
if (menu) {
|
|
||||||
const _currentRole = userRole;
|
|
||||||
const redirectUrl =
|
|
||||||
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
|
||||||
const _role = _currentRole.replace("contracts_", "");
|
|
||||||
localStorage.setItem("_user_role", _role);
|
|
||||||
const results = [extUser];
|
|
||||||
const extUser_stringify = JSON.stringify(results);
|
|
||||||
localStorage.setItem("Extand_Class", extUser_stringify);
|
|
||||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
|
||||||
localStorage.setItem("userEmail", extInfo?.Email);
|
|
||||||
localStorage.setItem("username", extInfo?.Name);
|
|
||||||
if (extInfo?.TenantId) {
|
|
||||||
const tenant = {
|
|
||||||
Id: extInfo?.TenantId?.objectId || "",
|
|
||||||
Name: extInfo?.TenantId?.TenantName || ""
|
|
||||||
};
|
|
||||||
localStorage.setItem("TenantId", tenant?.Id);
|
|
||||||
dispatch(showTenant(tenant?.Name));
|
|
||||||
localStorage.setItem("TenantName", tenant?.Name);
|
|
||||||
}
|
|
||||||
localStorage.setItem("PageLanding", menu.pageId);
|
|
||||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
|
||||||
localStorage.setItem("pageType", menu.pageType);
|
|
||||||
navigate(redirectUrl);
|
|
||||||
} else {
|
|
||||||
showToast("danger", t("role-not-found"));
|
|
||||||
logOutUser();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showToast("danger", t("do-not-access-contact-admin"));
|
|
||||||
logOutUser();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showToast("danger", t("user-not-found"));
|
|
||||||
logOutUser();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("err in fetching extUser", err);
|
|
||||||
showToast("danger", `${err.message}`);
|
|
||||||
const payload = { sessionToken: sessionToken };
|
|
||||||
handleSubmitbtn(payload);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
showToast("danger", `${error.message}`);
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
setThirdpartyLoader(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const GetLoginData = async () => {
|
|
||||||
setState({ ...state, loading: true });
|
|
||||||
try {
|
|
||||||
const user = await Parse.User.become(localStorage.getItem("accesstoken"));
|
|
||||||
const _user = user.toJSON();
|
|
||||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
|
||||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
|
||||||
localStorage.setItem("scriptId", true);
|
|
||||||
if (_user.ProfilePic) {
|
|
||||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("profileImg", "");
|
|
||||||
}
|
|
||||||
const userSettings = appInfo.settings;
|
|
||||||
await Parse.Cloud.run("getUserDetails").then(async (extUser) => {
|
|
||||||
if (extUser) {
|
if (extUser) {
|
||||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||||
if (!IsDisabled) {
|
if (!IsDisabled) {
|
||||||
const userRole = extUser.get("UserRole");
|
const userRole = extUser?.get("UserRole");
|
||||||
const _currentRole = userRole;
|
|
||||||
const menu =
|
const menu =
|
||||||
userRole && userSettings.find((menu) => menu.role === userRole);
|
userRole && userSettings.find((menu) => menu.role === userRole);
|
||||||
if (menu) {
|
if (menu) {
|
||||||
const _role = _currentRole.replace("contracts_", "");
|
const _currentRole = userRole;
|
||||||
localStorage.setItem("_user_role", _role);
|
|
||||||
const redirectUrl =
|
const redirectUrl =
|
||||||
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
||||||
const results = [extUser];
|
const _role = _currentRole.replace("contracts_", "");
|
||||||
const extendedInfo_stringify = JSON.stringify(results);
|
|
||||||
localStorage.setItem("Extand_Class", extendedInfo_stringify);
|
|
||||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||||
localStorage.setItem("userEmail", extInfo.Email);
|
localStorage.setItem("_user_role", _role);
|
||||||
localStorage.setItem("username", extInfo.Name);
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||||
|
localStorage.setItem("userEmail", extInfo?.Email);
|
||||||
|
localStorage.setItem("username", extInfo?.Name);
|
||||||
if (extInfo?.TenantId) {
|
if (extInfo?.TenantId) {
|
||||||
const tenant = {
|
const tenant = {
|
||||||
Id: extInfo?.TenantId?.objectId || "",
|
Id: extInfo?.TenantId?.objectId || "",
|
||||||
@@ -320,10 +184,9 @@ function Login() {
|
|||||||
localStorage.setItem("PageLanding", menu.pageId);
|
localStorage.setItem("PageLanding", menu.pageId);
|
||||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||||
localStorage.setItem("pageType", menu.pageType);
|
localStorage.setItem("pageType", menu.pageType);
|
||||||
// Redirect to the appropriate URL after successful login
|
navigate(redirectUrl);
|
||||||
navigate(redirectUrl);
|
|
||||||
} else {
|
} else {
|
||||||
setState({ ...state, loading: false });
|
showToast("danger", t("role-not-found"));
|
||||||
logOutUser();
|
logOutUser();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -334,7 +197,66 @@ function Login() {
|
|||||||
showToast("danger", t("user-not-found"));
|
showToast("danger", t("user-not-found"));
|
||||||
logOutUser();
|
logOutUser();
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
console.error("err in fetching extUser", err);
|
||||||
|
showToast("danger", `${err.message}`);
|
||||||
|
const payload = { sessionToken: _user.sessionToken };
|
||||||
|
handleSubmitbtn(payload);
|
||||||
|
} finally {
|
||||||
|
setThirdpartyLoader(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const GetLoginData = async () => {
|
||||||
|
setState({ ...state, loading: true });
|
||||||
|
try {
|
||||||
|
const user = await Parse.User.become(localStorage.getItem("accesstoken"));
|
||||||
|
const _user = user.toJSON();
|
||||||
|
setLocalVar(_user);
|
||||||
|
const userSettings = appInfo.settings;
|
||||||
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
||||||
|
if (extUser) {
|
||||||
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||||
|
if (!IsDisabled) {
|
||||||
|
const userRole = extUser.get("UserRole");
|
||||||
|
const _currentRole = userRole;
|
||||||
|
const menu =
|
||||||
|
userRole && userSettings.find((menu) => menu.role === userRole);
|
||||||
|
if (menu) {
|
||||||
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||||
|
const _role = _currentRole.replace("contracts_", "");
|
||||||
|
localStorage.setItem("_user_role", _role);
|
||||||
|
const redirectUrl =
|
||||||
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
||||||
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||||
|
localStorage.setItem("userEmail", extInfo.Email);
|
||||||
|
localStorage.setItem("username", extInfo.Name);
|
||||||
|
if (extInfo?.TenantId) {
|
||||||
|
const tenant = {
|
||||||
|
Id: extInfo?.TenantId?.objectId || "",
|
||||||
|
Name: extInfo?.TenantId?.TenantName || ""
|
||||||
|
};
|
||||||
|
localStorage.setItem("TenantId", tenant?.Id);
|
||||||
|
dispatch(showTenant(tenant?.Name));
|
||||||
|
localStorage.setItem("TenantName", tenant?.Name);
|
||||||
|
}
|
||||||
|
localStorage.setItem("PageLanding", menu.pageId);
|
||||||
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||||
|
localStorage.setItem("pageType", menu.pageType);
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else {
|
||||||
|
setState({ ...state, loading: false });
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast("danger", t("do-not-access-contact-admin"));
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast("danger", t("user-not-found"));
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast("danger", t("something-went-wrong-mssg"));
|
showToast("danger", t("something-went-wrong-mssg"));
|
||||||
console.log("err", error);
|
console.log("err", error);
|
||||||
@@ -417,6 +339,63 @@ function Login() {
|
|||||||
localStorage.setItem("parseAppId", appid);
|
localStorage.setItem("parseAppId", appid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const continueLoginFlow = async () => {
|
||||||
|
try {
|
||||||
|
const userSettings = appInfo.settings;
|
||||||
|
const extUser = await Parse.Cloud.run("getUserDetails");
|
||||||
|
if (extUser) {
|
||||||
|
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||||
|
if (!IsDisabled) {
|
||||||
|
const userRole = extUser?.get("UserRole");
|
||||||
|
const menu =
|
||||||
|
userRole && userSettings?.find((menu) => menu.role === userRole);
|
||||||
|
if (menu) {
|
||||||
|
const _currentRole = userRole;
|
||||||
|
const redirectUrl =
|
||||||
|
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
||||||
|
const _role = _currentRole.replace("contracts_", "");
|
||||||
|
localStorage.setItem("_user_role", _role);
|
||||||
|
const checkLanguage = extUser?.get("Language");
|
||||||
|
if (checkLanguage) {
|
||||||
|
checkLanguage && i18n.changeLanguage(checkLanguage);
|
||||||
|
}
|
||||||
|
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||||
|
// Continue with storing user data and redirecting
|
||||||
|
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||||
|
localStorage.setItem("userEmail", extInfo.Email);
|
||||||
|
localStorage.setItem("username", extInfo.Name);
|
||||||
|
if (extInfo?.TenantId) {
|
||||||
|
const tenant = {
|
||||||
|
Id: extInfo?.TenantId?.objectId || "",
|
||||||
|
Name: extInfo?.TenantId?.TenantName || ""
|
||||||
|
};
|
||||||
|
localStorage.setItem("TenantId", tenant?.Id);
|
||||||
|
dispatch(showTenant(tenant?.Name));
|
||||||
|
localStorage.setItem("TenantName", tenant?.Name);
|
||||||
|
}
|
||||||
|
localStorage.setItem("PageLanding", menu.pageId);
|
||||||
|
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||||
|
localStorage.setItem("pageType", menu.pageType);
|
||||||
|
setState({ ...state, loading: false });
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else {
|
||||||
|
setState({ ...state, loading: false });
|
||||||
|
setIsModal(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast("danger", t("do-not-access-contact-admin"));
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast("danger", t("user-not-found"));
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during login flow", error);
|
||||||
|
showToast("danger", error.message || t("something-went-wrong-mssg"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return errMsg ? (
|
return errMsg ? (
|
||||||
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
|
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
|
||||||
{errMsg}
|
{errMsg}
|
||||||
@@ -437,7 +416,7 @@ function Login() {
|
|||||||
<div
|
<div
|
||||||
aria-labelledby="loginHeading"
|
aria-labelledby="loginHeading"
|
||||||
role="region"
|
role="region"
|
||||||
className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16 h-screen"
|
className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16 h-full"
|
||||||
>
|
>
|
||||||
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
||||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||||
@@ -476,35 +455,39 @@ function Login() {
|
|||||||
onInput={(e) => e.target.setCustomValidity("")}
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
/>
|
/>
|
||||||
<hr className="my-1 border-none" />
|
<hr className="my-1 border-none" />
|
||||||
<label className="block text-xs" htmlFor="password">
|
<label className="block text-xs" htmlFor="password">
|
||||||
{t("password")}
|
{t("password")}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
type={state.passwordVisible ? "text" : "password"}
|
type={
|
||||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
state.passwordVisible ? "text" : "password"
|
||||||
name="password"
|
}
|
||||||
value={state.password}
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
autoComplete="current-password"
|
name="password"
|
||||||
onChange={handleChange}
|
value={state.password}
|
||||||
onInvalid={(e) =>
|
autoComplete="current-password"
|
||||||
e.target.setCustomValidity(t("input-required"))
|
onChange={handleChange}
|
||||||
}
|
onInvalid={(e) =>
|
||||||
onInput={(e) => e.target.setCustomValidity("")}
|
e.target.setCustomValidity(
|
||||||
required
|
t("input-required")
|
||||||
/>
|
)
|
||||||
<span
|
}
|
||||||
className="absolute cursor-pointer top-[50%] right-[10px] -translate-y-[50%] text-base-content"
|
onInput={(e) => e.target.setCustomValidity("")}
|
||||||
onClick={togglePasswordVisibility}
|
required
|
||||||
>
|
/>
|
||||||
{state.passwordVisible ? (
|
<span
|
||||||
<i className="fa-light fa-eye-slash text-xs pb-1" /> // Close eye icon
|
className="absolute cursor-pointer top-[50%] right-[10px] -translate-y-[50%] text-base-content"
|
||||||
) : (
|
onClick={togglePasswordVisibility}
|
||||||
<i className="fa-light fa-eye text-xs pb-1 " /> // Open eye icon
|
>
|
||||||
)}
|
{state.passwordVisible ? (
|
||||||
</span>
|
<i className="fa-light fa-eye-slash text-xs pb-1" /> // Close eye icon
|
||||||
</div>
|
) : (
|
||||||
|
<i className="fa-light fa-eye text-xs pb-1 " /> // Open eye icon
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="relative mt-1">
|
<div className="relative mt-1">
|
||||||
<NavLink
|
<NavLink
|
||||||
+138
-100
@@ -1,13 +1,17 @@
|
|||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import "../styles/opensigndrive.css";
|
import "../styles/opensigndrive.css";
|
||||||
import { iconColor } from "../constant/const";
|
import {
|
||||||
import { getDrive } from "../constant/Utils";
|
iconColor,
|
||||||
|
} from "../constant/const";
|
||||||
|
import {
|
||||||
|
getDrive
|
||||||
|
} from "../constant/Utils";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||||
import Tour from "reactour";
|
import Tour from "../primitives/Tour";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Loader from "../primitives/Loader";
|
import Loader from "../primitives/Loader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -25,7 +29,8 @@ const AppLoader = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
function Opensigndrive() {
|
function Opensigndrive() {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -58,6 +63,9 @@ function Opensigndrive() {
|
|||||||
const [isDontShow, setIsDontShow] = useState(false);
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
const [tourData, setTourData] = useState();
|
const [tourData, setTourData] = useState();
|
||||||
const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
|
const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||||
|
const debounceTimer = useRef(null);
|
||||||
const orderName = {
|
const orderName = {
|
||||||
Ascending: "Ascending",
|
Ascending: "Ascending",
|
||||||
Descending: "Descending",
|
Descending: "Descending",
|
||||||
@@ -125,7 +133,7 @@ function Opensigndrive() {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
const getDetails = async () => {
|
const getDetails = async () => {
|
||||||
getPdfDocumentList();
|
getPdfDocumentList();
|
||||||
};
|
};
|
||||||
//function for get all pdf document list
|
//function for get all pdf document list
|
||||||
const getPdfDocumentList = async (disbaleLoading) => {
|
const getPdfDocumentList = async (disbaleLoading) => {
|
||||||
@@ -209,9 +217,7 @@ function Opensigndrive() {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setIsLoading({
|
setIsLoading({ isLoad: false });
|
||||||
isLoad: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,15 +225,16 @@ function Opensigndrive() {
|
|||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
//get document of render openSign-drive component using id
|
//get document of render openSign-drive component using id
|
||||||
const documentList = document.getElementById("renderList");
|
const documentList = document.getElementById("renderList");
|
||||||
//documentList.clientHeight property returns the height of an element's content area, including padding but not including borders, margins, or scrollbars.
|
const { scrollTop, clientHeight, scrollHeight } = documentList;
|
||||||
//documentList.scrollHeight property returns the entire height of an element,including the parts that are not visible due to overflow..
|
const scrolled = Math.ceil(scrollTop + clientHeight); // ceil return e.g 3.14 => 4
|
||||||
// documentList.scrollTop property show height of element, how much the content has been scrolled from the top.
|
const totalHeight = Math.floor(scrollHeight); // floor return e.g 3.14 => 3
|
||||||
|
|
||||||
|
// clientHeight property returns the height of an element's content area, including padding but not including borders, margins, or scrollbars.
|
||||||
|
// scrollHeight property returns the entire height of an element,including the parts that are not visible due to overflow..
|
||||||
|
// scrollTop property show height of element, how much the content has been scrolled from the top.
|
||||||
// When the sum of scrollTop and clientHeight is equal to scrollHeight, it means that the user has scrolled to the bottom of the div.
|
// When the sum of scrollTop and clientHeight is equal to scrollHeight, it means that the user has scrolled to the bottom of the div.
|
||||||
if (
|
|
||||||
documentList &&
|
if (scrolled >= totalHeight) {
|
||||||
documentList.scrollTop + documentList.clientHeight >=
|
|
||||||
documentList.scrollHeight
|
|
||||||
) {
|
|
||||||
//disableLoading is used disable initial loader
|
//disableLoading is used disable initial loader
|
||||||
const disableLoading = true;
|
const disableLoading = true;
|
||||||
// If the fetched data length is less than the limit, it means there's no more data to fetch
|
// If the fetched data length is less than the limit, it means there's no more data to fetch
|
||||||
@@ -252,11 +259,7 @@ function Opensigndrive() {
|
|||||||
const handleRoute = (index, folderData) => {
|
const handleRoute = (index, folderData) => {
|
||||||
setSkip(0);
|
setSkip(0);
|
||||||
// after onclick on route filter route from that index
|
// after onclick on route filter route from that index
|
||||||
const updateFolderName = folderName.filter((x, i) => {
|
const updateFolderName = folderName.filter((_, i) => i <= index);
|
||||||
if (i <= index) {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setFolderName(updateFolderName);
|
setFolderName(updateFolderName);
|
||||||
//get route details after onclick path of folder name
|
//get route details after onclick path of folder name
|
||||||
const getCurrentId = folderData[index];
|
const getCurrentId = folderData[index];
|
||||||
@@ -517,6 +520,34 @@ function Opensigndrive() {
|
|||||||
setIsTour(true);
|
setIsTour(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSearchChange = async (e) => {
|
||||||
|
const name = e.target.value.toLowerCase();
|
||||||
|
setSearchTerm(name);
|
||||||
|
// Clear previous timer
|
||||||
|
if (debounceTimer.current) {
|
||||||
|
clearTimeout(debounceTimer.current);
|
||||||
|
}
|
||||||
|
// Start new debounce timer
|
||||||
|
debounceTimer.current = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const res = await Parse.Cloud.run("filterdocs", { searchTerm: name });
|
||||||
|
setPdfData(JSON.parse(JSON.stringify(res)));
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Search error:", err);
|
||||||
|
alert(`Error: ${err.message}`);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
// Cleanup on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (debounceTimer.current) {
|
||||||
|
clearTimeout(debounceTimer.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-base-100 text-base-content rounded-box w-full shadow-md">
|
<div className="bg-base-100 text-base-content rounded-box w-full shadow-md">
|
||||||
<Title title={`${drivename} Drive`} drive={true} />
|
<Title title={`${drivename} Drive`} drive={true} />
|
||||||
@@ -524,22 +555,14 @@ function Opensigndrive() {
|
|||||||
isOpen={isAlert.isShow}
|
isOpen={isAlert.isShow}
|
||||||
title={t("alert")}
|
title={t("alert")}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setIsAlert({
|
setIsAlert({ isShow: false, alertMessage: "" });
|
||||||
isShow: false,
|
|
||||||
alertMessage: ""
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="h-full p-[20px] pb-[15px]">
|
<div className="h-full p-[20px] pb-[15px]">
|
||||||
<p>{isAlert.alertMessage}</p>
|
<p>{isAlert.alertMessage}</p>
|
||||||
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() => setIsAlert({ isShow: false, alertMessage: "" })}
|
||||||
setIsAlert({
|
|
||||||
isShow: false,
|
|
||||||
alertMessage: ""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="op-btn op-btn-neutral op-btn-sm"
|
className="op-btn op-btn-neutral op-btn-sm"
|
||||||
>
|
>
|
||||||
@@ -608,7 +631,7 @@ function Opensigndrive() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row justify-between items-center px-[15px] md:px-[25px] pt-[20px]">
|
<div className="flex flex-row justify-between items-center px-[15px] md:px-[25px] pt-2 md:pt-[20px]">
|
||||||
{tourData && (
|
{tourData && (
|
||||||
<Tour
|
<Tour
|
||||||
onRequestClose={closeTour}
|
onRequestClose={closeTour}
|
||||||
@@ -627,24 +650,42 @@ function Opensigndrive() {
|
|||||||
>
|
>
|
||||||
{handleFolderTab(folderName)}
|
{handleFolderTab(folderName)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center justify-center md:gap-1">
|
||||||
|
{/* Desktop search input */}
|
||||||
|
<div className="hidden md:block p-2">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
placeholder="Search documents…"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-64 text-xs"
|
||||||
|
/>
|
||||||
|
</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]"
|
||||||
|
aria-label="Search"
|
||||||
|
onClick={() => setMobileSearchOpen((open) => !open)}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
style={{ color: `${iconColor}` }}
|
||||||
|
className="fa-solid fa-magnifying-glass"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
id="folder-menu"
|
id="folder-menu"
|
||||||
className={`${
|
className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`}
|
||||||
isOptions ? "dropdown show dropDownStyle" : "dropdown"
|
|
||||||
} hidden md:block`}
|
|
||||||
onClick={() => setIsOptions(!isOptions)}
|
onClick={() => setIsOptions(!isOptions)}
|
||||||
>
|
>
|
||||||
<div className="sort" data-tut="reactourSecond">
|
<div data-tut="reactourSecond">
|
||||||
<i
|
<i
|
||||||
className="fa-light fa-plus-square"
|
className="fa-light fa-plus-square text-[24px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ fontSize: "25px", color: `${iconColor}` }}
|
style={{ color: `${iconColor}` }}
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`${isOptions ? "block" : "hidden"} ${dropdowncss}`}
|
className={`${isOptions ? "block" : "hidden"} ${dropdowncss}`}
|
||||||
// className={isOptions ? "dropdown-menu show" : "dropdown-menu"}
|
|
||||||
aria-labelledby="dropdownMenuButton"
|
aria-labelledby="dropdownMenuButton"
|
||||||
aria-expanded={isOptions ? "true" : "false"}
|
aria-expanded={isOptions ? "true" : "false"}
|
||||||
>
|
>
|
||||||
@@ -683,11 +724,11 @@ function Opensigndrive() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-tut="reactourThird"
|
data-tut="reactourThird"
|
||||||
className="sort "
|
className="cursor-pointer flex flex-row items-center justify-center p-2 hover:bg-gray-200 rounded-md"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className="fa-light fa-sort-amount-asc mr-[5px] text-[14px]"
|
className="fa-light fa-sort-amount-asc mr-[5px] text-[19px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ color: `${iconColor}` }}
|
style={{ color: `${iconColor}` }}
|
||||||
></i>
|
></i>
|
||||||
@@ -702,81 +743,67 @@ function Opensigndrive() {
|
|||||||
aria-labelledby="dropdownMenuButton"
|
aria-labelledby="dropdownMenuButton"
|
||||||
aria-expanded={isShowSort ? "true" : "false"}
|
aria-expanded={isShowSort ? "true" : "false"}
|
||||||
>
|
>
|
||||||
{sortingValue.map((value, ind) => {
|
{sortingValue.map((value, ind) => (
|
||||||
return (
|
<span
|
||||||
<span
|
key={ind}
|
||||||
key={ind}
|
onClick={() => {
|
||||||
onClick={() => {
|
setSelectedSort(value);
|
||||||
setSelectedSort(value);
|
sortingData(value, null, pdfData);
|
||||||
sortingData(value, null, pdfData);
|
}}
|
||||||
}}
|
className="dropdown-item text-[10px] md:text-[13px]"
|
||||||
className="dropdown-item text-[10px] md:text-[13px]"
|
style={{
|
||||||
style={{
|
paddingLeft: selectedSort !== value && "31px"
|
||||||
paddingLeft: selectedSort !== value && "31px"
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{selectedSort === value && (
|
||||||
{selectedSort === value && (
|
<i className="fa-light fa-check" aria-hidden="true"></i>
|
||||||
<i
|
)}
|
||||||
className="fa-light fa-check"
|
<span className="ml-[5px]">
|
||||||
aria-hidden="true"
|
{t(`sort-order.${value}`)}
|
||||||
></i>
|
|
||||||
)}
|
|
||||||
<span className="ml-[5px]">
|
|
||||||
{t(`sort-order.${value}`)}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
);
|
</span>
|
||||||
})}
|
))}
|
||||||
<hr className="hrStyle" />
|
<hr className="hrStyle" />
|
||||||
{sortOrder.map((order, ind) => {
|
{sortOrder.map((order, ind) => (
|
||||||
return (
|
<span
|
||||||
<span
|
key={ind}
|
||||||
key={ind}
|
onClick={() => {
|
||||||
onClick={() => {
|
setSortingOrder(order);
|
||||||
setSortingOrder(order);
|
sortingData(null, order, pdfData);
|
||||||
sortingData(null, order, pdfData);
|
}}
|
||||||
}}
|
className="dropdown-item text-[10px] md:text-[13px]"
|
||||||
className="dropdown-item text-[10px] md:text-[13px]"
|
style={{
|
||||||
style={{
|
paddingLeft: sortingOrder !== order && "31px"
|
||||||
paddingLeft: sortingOrder !== order && "31px"
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{sortingOrder === order && (
|
||||||
{sortingOrder === order && (
|
<i className="fa-light fa-check" aria-hidden="true"></i>
|
||||||
<i
|
)}
|
||||||
className="fa-light fa-check"
|
<span className="ml-[5px]">
|
||||||
aria-hidden="true"
|
{t(`sort-order.${order}`)}
|
||||||
></i>
|
|
||||||
)}
|
|
||||||
<span className="ml-[5px]">
|
|
||||||
{t(`sort-order.${order}`)}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
);
|
</span>
|
||||||
})}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
className="cursor-pointer p-2 hover:bg-gray-200 rounded-md flex justify-center items-center"
|
||||||
data-tut="reactourForth"
|
data-tut="reactourForth"
|
||||||
className="sort"
|
|
||||||
onClick={() => setIsList(!isList)}
|
onClick={() => setIsList(!isList)}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className={
|
className={`${isList ? "fa-light fa-th-large" : "fa-light fa-list"} text-[20px]`}
|
||||||
isList ? "fa-light fa-th-large" : "fa-light fa-list"
|
style={{ color: `${iconColor}` }}
|
||||||
}
|
|
||||||
style={{ fontSize: "24px", color: `${iconColor}` }}
|
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="folder-menu"
|
id="folder-menu"
|
||||||
className={`${
|
className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} md:hidden`}
|
||||||
isOptions ? "dropdown show dropDownStyle" : "dropdown"
|
|
||||||
} md:hidden`}
|
|
||||||
onClick={() => setIsOptions(!isOptions)}
|
onClick={() => setIsOptions(!isOptions)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="p-[19px] my-2 flex items-center justify-center cursor-pointer hover:bg-[var(--mauve-3)] rounded-[2px] shadow-[0_2px_4px_rgba(168,204,206,0.1)]"
|
className="p-3 flex items-center justify-center cursor-pointer rounded-md hover:bg-gray-200"
|
||||||
data-tut="reactourSecond"
|
data-tut="reactourSecond"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
@@ -820,7 +847,18 @@ function Opensigndrive() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Mobile search overlay */}
|
||||||
|
{mobileSearchOpen && (
|
||||||
|
<div className="top-full left-0 w-full bg-white px-4 py-2 shadow-md md:hidden">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
placeholder="Search documents…"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{pdfData && pdfData.length === 0 ? (
|
{pdfData && pdfData.length === 0 ? (
|
||||||
<div className="flex justify-center items-center w-full h-[50vh]">
|
<div className="flex justify-center items-center w-full h-[50vh]">
|
||||||
<span className="text-base-content font-bold">
|
<span className="text-base-content font-bold">
|
||||||
+658
-770
File diff suppressed because it is too large
Load Diff
+129
-78
@@ -3,17 +3,19 @@ import axios from "axios";
|
|||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import "../styles/signature.css";
|
import "../styles/signature.css";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
import { maxTitleLength } from "../constant/const";
|
import {
|
||||||
|
maxTitleLength
|
||||||
|
} from "../constant/const";
|
||||||
import { DndProvider } from "react-dnd";
|
import { DndProvider } from "react-dnd";
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
import { useDrop } from "react-dnd";
|
import { useDrop } from "react-dnd";
|
||||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||||
import Tour from "reactour";
|
import Tour from "../primitives/Tour";
|
||||||
import { useLocation, useParams } from "react-router";
|
import { useLocation, useParams } from "react-router";
|
||||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||||
import Header from "../components/pdf/PdfHeader";
|
import Header from "../components/pdf/PdfHeader";
|
||||||
import { RWebShare } from "react-web-share";
|
import ShareButton from "../primitives/ShareButton";
|
||||||
import {
|
import {
|
||||||
replaceMailVaribles,
|
replaceMailVaribles,
|
||||||
pdfNewWidthFun,
|
pdfNewWidthFun,
|
||||||
@@ -66,11 +68,14 @@ import LottieWithLoader from "../primitives/DotLottieReact";
|
|||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
import AsyncSelect from "react-select/async";
|
import AsyncSelect from "react-select/async";
|
||||||
import AddContact from "../primitives/AddContact";
|
import AddContact from "../primitives/AddContact";
|
||||||
|
import WidgetsValueModal from "../components/pdf/WidgetsValueModal.jsx";
|
||||||
|
|
||||||
function PlaceHolderSign() {
|
function PlaceHolderSign() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const copyUrlRef = useRef(null);
|
const copyUrlRef = useRef(null);
|
||||||
const appName = "OpenSign™";
|
const isShowModal = useSelector((state) => state.widget.isShowModal);
|
||||||
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const editorRef = useRef();
|
const editorRef = useRef();
|
||||||
const { state } = useLocation();
|
const { state } = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -118,7 +123,8 @@ function PlaceHolderSign() {
|
|||||||
const [selectedEmail, setSelectedEmail] = useState(false);
|
const [selectedEmail, setSelectedEmail] = useState(false);
|
||||||
const [isResize, setIsResize] = useState(false);
|
const [isResize, setIsResize] = useState(false);
|
||||||
const [zIndex, setZIndex] = useState(1);
|
const [zIndex, setZIndex] = useState(1);
|
||||||
const [signKey, setSignKey] = useState();
|
// tempSignerId is used to temporarily store the currently selected signer's unique ID, When editing a text widget, it automatically attaches a prefill user, and since prefill users are not shown in the signer list, the selected signer from before editing would be lost. To handle this, we store the currently selected signer's unique ID in tempSignerId before entering the text widget edit mode. Once the text widget settings are completed,
|
||||||
|
// we restore the original selected signer by setting tempSignerId back to uniqueId.This ensures that the correct signer remains selected and visible in the UI even after interacting with a prefill-only widget like the text widget.
|
||||||
const [tempSignerId, setTempSignerId] = useState("");
|
const [tempSignerId, setTempSignerId] = useState("");
|
||||||
const [blockColor, setBlockColor] = useState("");
|
const [blockColor, setBlockColor] = useState("");
|
||||||
const [isTextSetting, setIsTextSetting] = useState(false);
|
const [isTextSetting, setIsTextSetting] = useState(false);
|
||||||
@@ -131,14 +137,11 @@ function PlaceHolderSign() {
|
|||||||
const [isDontShow, setIsDontShow] = useState(false);
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [showDropdown, setShowDropdown] = useState(false);
|
const [showDropdown, setShowDropdown] = useState(false);
|
||||||
const [widgetType, setWidgetType] = useState("");
|
|
||||||
const [isUiLoading, setIsUiLoading] = useState(false);
|
const [isUiLoading, setIsUiLoading] = useState(false);
|
||||||
const [isRadio, setIsRadio] = useState(false);
|
const [isRadio, setIsRadio] = useState(false);
|
||||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
|
||||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||||
const [isNameModal, setIsNameModal] = useState(false);
|
const [isNameModal, setIsNameModal] = useState(false);
|
||||||
const [widgetName, setWidgetName] = useState(false);
|
|
||||||
const [mailStatus, setMailStatus] = useState("");
|
const [mailStatus, setMailStatus] = useState("");
|
||||||
const [isCurrUser, setIsCurrUser] = useState(false);
|
const [isCurrUser, setIsCurrUser] = useState(false);
|
||||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||||
@@ -510,7 +513,8 @@ function PlaceHolderSign() {
|
|||||||
pageNumber,
|
pageNumber,
|
||||||
containerWH
|
containerWH
|
||||||
);
|
);
|
||||||
let dropData = [];
|
let dropData = [],
|
||||||
|
dropObj;
|
||||||
let placeHolder;
|
let placeHolder;
|
||||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||||
const widgetWidth =
|
const widgetWidth =
|
||||||
@@ -521,7 +525,7 @@ function PlaceHolderSign() {
|
|||||||
if (item === "onclick") {
|
if (item === "onclick") {
|
||||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||||
// `getBoundingClientRect()` is used to get accurate measurement height of the div
|
// `getBoundingClientRect()` is used to get accurate measurement height of the div
|
||||||
const dropObj = {
|
dropObj = {
|
||||||
//onclick put placeholder center on pdf
|
//onclick put placeholder center on pdf
|
||||||
xPosition: widgetWidth / 4 + containerWH.width / 2,
|
xPosition: widgetWidth / 4 + containerWH.width / 2,
|
||||||
yPosition: widgetHeight + divHeight / 2,
|
yPosition: widgetHeight + divHeight / 2,
|
||||||
@@ -553,7 +557,7 @@ function PlaceHolderSign() {
|
|||||||
const getYPosition = signBtnPosition[0]
|
const getYPosition = signBtnPosition[0]
|
||||||
? y - signBtnPosition[0].yPos
|
? y - signBtnPosition[0].yPos
|
||||||
: y;
|
: y;
|
||||||
const dropObj = {
|
dropObj = {
|
||||||
xPosition: getXPosition / (containerScale * scale),
|
xPosition: getXPosition / (containerScale * scale),
|
||||||
yPosition: getYPosition / (containerScale * scale),
|
yPosition: getYPosition / (containerScale * scale),
|
||||||
isStamp:
|
isStamp:
|
||||||
@@ -569,7 +573,6 @@ function PlaceHolderSign() {
|
|||||||
dropData.push(dropObj);
|
dropData.push(dropObj);
|
||||||
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
||||||
}
|
}
|
||||||
setSelectWidgetId(key);
|
|
||||||
if (signer) {
|
if (signer) {
|
||||||
let filterSignerPos, currentPagePosition;
|
let filterSignerPos, currentPagePosition;
|
||||||
if (dragTypeValue === textWidget) {
|
if (dragTypeValue === textWidget) {
|
||||||
@@ -661,10 +664,7 @@ function PlaceHolderSign() {
|
|||||||
} else if (dragTypeValue === radioButtonWidget) {
|
} else if (dragTypeValue === radioButtonWidget) {
|
||||||
setIsRadio(true);
|
setIsRadio(true);
|
||||||
}
|
}
|
||||||
setWidgetType(dragTypeValue);
|
setCurrWidgetsDetails(dropObj);
|
||||||
setSignKey(key);
|
|
||||||
setCurrWidgetsDetails({});
|
|
||||||
setWidgetName(dragTypeValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -869,7 +869,11 @@ function PlaceHolderSign() {
|
|||||||
scale
|
scale
|
||||||
);
|
);
|
||||||
const pdfName = generatePdfName(16);
|
const pdfName = generatePdfName(16);
|
||||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, "");
|
const pdfUrl = await convertBase64ToFile(
|
||||||
|
pdfName,
|
||||||
|
pdfBase64,
|
||||||
|
"",
|
||||||
|
);
|
||||||
const tenantId = localStorage.getItem("TenantId");
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
const buffer = atob(pdfBase64);
|
const buffer = atob(pdfBase64);
|
||||||
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||||
@@ -881,7 +885,11 @@ function PlaceHolderSign() {
|
|||||||
} else if (pdfBase64Url) {
|
} else if (pdfBase64Url) {
|
||||||
try {
|
try {
|
||||||
const pdfName = generatePdfName(16);
|
const pdfName = generatePdfName(16);
|
||||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
const pdfUrl = await convertBase64ToFile(
|
||||||
|
pdfName,
|
||||||
|
pdfBase64Url,
|
||||||
|
"",
|
||||||
|
);
|
||||||
return pdfUrl;
|
return pdfUrl;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("error to convertBase64ToFile in placeholder flow", err);
|
console.log("error to convertBase64ToFile in placeholder flow", err);
|
||||||
@@ -955,7 +963,7 @@ function PlaceHolderSign() {
|
|||||||
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
||||||
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
||||||
setSignerExistModal(true);
|
setSignerExistModal(true);
|
||||||
setSelectWidgetId(IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key);
|
setCurrWidgetsDetails(IsSignerNotExist[0]?.placeHolder?.[0]?.pos);
|
||||||
} else {
|
} else {
|
||||||
saveDocumentDetails();
|
saveDocumentDetails();
|
||||||
}
|
}
|
||||||
@@ -987,7 +995,11 @@ function PlaceHolderSign() {
|
|||||||
let pdfUrl;
|
let pdfUrl;
|
||||||
if (isUploadPdf) {
|
if (isUploadPdf) {
|
||||||
const pdfName = generatePdfName(16);
|
const pdfName = generatePdfName(16);
|
||||||
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
pdfUrl = await convertBase64ToFile(
|
||||||
|
pdfName,
|
||||||
|
pdfBase64Url,
|
||||||
|
"",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const docCls = new Parse.Object("contracts_Document");
|
const docCls = new Parse.Object("contracts_Document");
|
||||||
@@ -1058,9 +1070,7 @@ function PlaceHolderSign() {
|
|||||||
ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
|
ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
|
||||||
};
|
};
|
||||||
await axios.put(
|
await axios.put(
|
||||||
`${localStorage.getItem(
|
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${documentId}`,
|
||||||
"baseUrl"
|
|
||||||
)}classes/contracts_Document/${documentId}`,
|
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -1131,9 +1141,13 @@ function PlaceHolderSign() {
|
|||||||
<i className="fa-light fa-copy" />
|
<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>
|
</button>
|
||||||
<RWebShare data={{ url: data.url, title: t("sign-url") }}>
|
<ShareButton
|
||||||
|
title={t("sign-url")}
|
||||||
|
text={t("sign-url")}
|
||||||
|
url={data.url}
|
||||||
|
>
|
||||||
<i className="fa-light fa-share-from-square op-link op-link-secondary no-underline"></i>
|
<i className="fa-light fa-share-from-square op-link op-link-secondary no-underline"></i>
|
||||||
</RWebShare>
|
</ShareButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -1152,7 +1166,8 @@ function PlaceHolderSign() {
|
|||||||
year: "numeric"
|
year: "numeric"
|
||||||
});
|
});
|
||||||
|
|
||||||
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
|
let senderEmail =
|
||||||
|
pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||||
let signerMail = signersdata.slice();
|
let signerMail = signersdata.slice();
|
||||||
|
|
||||||
@@ -1178,11 +1193,16 @@ function PlaceHolderSign() {
|
|||||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||||
? pdfDetails[0].ExtUserPtr.Company
|
? pdfDetails[0].ExtUserPtr.Company
|
||||||
: "";
|
: "";
|
||||||
const senderName = pdfDetails?.[0].ExtUserPtr.Name;
|
const senderName =
|
||||||
|
pdfDetails?.[0].ExtUserPtr.Name;
|
||||||
const documentName = `${pdfDetails?.[0].Name}`;
|
const documentName = `${pdfDetails?.[0].Name}`;
|
||||||
let replaceVar;
|
let replaceVar;
|
||||||
|
|
||||||
if (requestBody && requestSubject && isCustomize) {
|
if (
|
||||||
|
requestBody &&
|
||||||
|
requestSubject &&
|
||||||
|
isCustomize
|
||||||
|
) {
|
||||||
const replacedRequestBody = requestBody.replace(/"/g, "'");
|
const replacedRequestBody = requestBody.replace(/"/g, "'");
|
||||||
htmlReqBody =
|
htmlReqBody =
|
||||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||||
@@ -1207,7 +1227,10 @@ function PlaceHolderSign() {
|
|||||||
htmlReqBody,
|
htmlReqBody,
|
||||||
variables
|
variables
|
||||||
);
|
);
|
||||||
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
|
} else if (
|
||||||
|
tenantMailTemplate?.body &&
|
||||||
|
tenantMailTemplate?.subject
|
||||||
|
) {
|
||||||
const mailBody = tenantMailTemplate?.body;
|
const mailBody = tenantMailTemplate?.body;
|
||||||
const mailSubject = tenantMailTemplate?.subject;
|
const mailSubject = tenantMailTemplate?.subject;
|
||||||
const replacedRequestBody = mailBody.replace(/"/g, "'");
|
const replacedRequestBody = mailBody.replace(/"/g, "'");
|
||||||
@@ -1246,7 +1269,8 @@ function PlaceHolderSign() {
|
|||||||
? replaceVar?.subject
|
? replaceVar?.subject
|
||||||
: mailTemplate(mailparam).subject,
|
: mailTemplate(mailparam).subject,
|
||||||
replyto: senderEmail,
|
replyto: senderEmail,
|
||||||
from: senderEmail,
|
from:
|
||||||
|
senderEmail,
|
||||||
html: replaceVar?.body
|
html: replaceVar?.body
|
||||||
? replaceVar?.body
|
? replaceVar?.body
|
||||||
: mailTemplate(mailparam).body
|
: mailTemplate(mailparam).body
|
||||||
@@ -1261,13 +1285,20 @@ function PlaceHolderSign() {
|
|||||||
setMailStatus("success");
|
setMailStatus("success");
|
||||||
try {
|
try {
|
||||||
let data;
|
let data;
|
||||||
if (requestBody && requestSubject && isCustomize) {
|
if (
|
||||||
|
requestBody &&
|
||||||
|
requestSubject &&
|
||||||
|
isCustomize
|
||||||
|
) {
|
||||||
data = {
|
data = {
|
||||||
RequestBody: htmlReqBody,
|
RequestBody: htmlReqBody,
|
||||||
RequestSubject: requestSubject,
|
RequestSubject: requestSubject,
|
||||||
SendMail: true
|
SendMail: true
|
||||||
};
|
};
|
||||||
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
|
} else if (
|
||||||
|
tenantMailTemplate?.body &&
|
||||||
|
tenantMailTemplate?.subject
|
||||||
|
) {
|
||||||
data = {
|
data = {
|
||||||
RequestBody: tenantMailTemplate?.body,
|
RequestBody: tenantMailTemplate?.body,
|
||||||
RequestSubject: tenantMailTemplate?.subject,
|
RequestSubject: tenantMailTemplate?.subject,
|
||||||
@@ -1305,7 +1336,8 @@ function PlaceHolderSign() {
|
|||||||
setIsSend(true);
|
setIsSend(true);
|
||||||
setIsMailSend(true);
|
setIsMailSend(true);
|
||||||
setIsUiLoading(false);
|
setIsUiLoading(false);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
setMailStatus("failed");
|
setMailStatus("failed");
|
||||||
setIsSend(true);
|
setIsSend(true);
|
||||||
setIsMailSend(true);
|
setIsMailSend(true);
|
||||||
@@ -1397,21 +1429,21 @@ function PlaceHolderSign() {
|
|||||||
const getXYdata = getPageNumer[0].pos;
|
const getXYdata = getPageNumer[0].pos;
|
||||||
const getPosData = getXYdata;
|
const getPosData = getXYdata;
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPosData.map((position) => {
|
||||||
if (position.key === signKey) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (widgetType === radioButtonWidget) {
|
if (currWidgetsDetails?.type === radioButtonWidget) {
|
||||||
if (addOption) {
|
if (addOption) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
Height: position.Height
|
Height: position.Height
|
||||||
? position.Height + 15
|
? position.Height + 15
|
||||||
: defaultWidthHeight(widgetType).height + 15
|
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
|
||||||
};
|
};
|
||||||
} else if (deleteOption) {
|
} else if (deleteOption) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
Height: position.Height
|
Height: position.Height
|
||||||
? position.Height - 15
|
? position.Height - 15
|
||||||
: defaultWidthHeight(widgetType).height - 15
|
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -1432,20 +1464,20 @@ function PlaceHolderSign() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (widgetType === "checkbox") {
|
} else if (currWidgetsDetails?.type === "checkbox") {
|
||||||
if (addOption) {
|
if (addOption) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
Height: position.Height
|
Height: position.Height
|
||||||
? position.Height + 15
|
? position.Height + 15
|
||||||
: defaultWidthHeight(widgetType).height + 15
|
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
|
||||||
};
|
};
|
||||||
} else if (deleteOption) {
|
} else if (deleteOption) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
Height: position.Height
|
Height: position.Height
|
||||||
? position.Height - 15
|
? position.Height - 15
|
||||||
: defaultWidthHeight(widgetType).height - 15
|
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -1536,7 +1568,7 @@ function PlaceHolderSign() {
|
|||||||
const getXYdata = getPageNumer[0].pos;
|
const getXYdata = getPageNumer[0].pos;
|
||||||
const getPosData = getXYdata;
|
const getPosData = getXYdata;
|
||||||
const addSignPos = getPosData.map((position) => {
|
const addSignPos = getPosData.map((position) => {
|
||||||
if (position.key === signKey) {
|
if (position.key === currWidgetsDetails?.key) {
|
||||||
if (position.type === textInputWidget) {
|
if (position.type === textInputWidget) {
|
||||||
return {
|
return {
|
||||||
...position,
|
...position,
|
||||||
@@ -1546,7 +1578,8 @@ function PlaceHolderSign() {
|
|||||||
status: defaultdata?.status || "required",
|
status: defaultdata?.status || "required",
|
||||||
hint: defaultdata?.hint || "",
|
hint: defaultdata?.hint || "",
|
||||||
defaultValue: defaultdata?.defaultValue || "",
|
defaultValue: defaultdata?.defaultValue || "",
|
||||||
validation: {},
|
validation:
|
||||||
|
{},
|
||||||
fontSize:
|
fontSize:
|
||||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||||
fontColor:
|
fontColor:
|
||||||
@@ -2029,26 +2062,30 @@ function PlaceHolderSign() {
|
|||||||
{!isCustomize && (
|
{!isCustomize && (
|
||||||
<span>{t("placeholder-alert-3")}</span>
|
<span>{t("placeholder-alert-3")}</span>
|
||||||
)}
|
)}
|
||||||
{isCustomize && (
|
{
|
||||||
<>
|
isCustomize && (
|
||||||
<EmailBody
|
<>
|
||||||
editorRef={editorRef}
|
<EmailBody
|
||||||
requestBody={requestBody}
|
editorRef={editorRef}
|
||||||
requestSubject={requestSubject}
|
requestBody={requestBody}
|
||||||
handleOnchangeRequest={handleOnchangeRequest}
|
requestSubject={requestSubject}
|
||||||
setRequestSubject={setRequestSubject}
|
handleOnchangeRequest={
|
||||||
/>
|
handleOnchangeRequest
|
||||||
<div
|
}
|
||||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
setRequestSubject={setRequestSubject}
|
||||||
onClick={() => {
|
/>
|
||||||
setRequestBody(defaultBody);
|
<div
|
||||||
setRequestSubject(defaultSubject);
|
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setRequestBody(defaultBody);
|
||||||
<span>{t("reset-to-default")}</span>
|
setRequestSubject(defaultSubject);
|
||||||
</div>
|
}}
|
||||||
</>
|
>
|
||||||
)}
|
<span>{t("reset-to-default")}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
|
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<button
|
<button
|
||||||
@@ -2066,14 +2103,16 @@ function PlaceHolderSign() {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isCustomize && (
|
{
|
||||||
<span
|
!isCustomize && (
|
||||||
className="op-link op-link-accent text-sm"
|
<span
|
||||||
onClick={() => setIsCustomize(!isCustomize)}
|
className="op-link op-link-accent text-sm"
|
||||||
>
|
onClick={() => setIsCustomize(!isCustomize)}
|
||||||
{t("cutomize-email")}
|
>
|
||||||
</span>
|
{t("cutomize-email")}
|
||||||
)}
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -2329,9 +2368,9 @@ function PlaceHolderSign() {
|
|||||||
setXyPosition={setSignerPos}
|
setXyPosition={setSignerPos}
|
||||||
allPages={allPages}
|
allPages={allPages}
|
||||||
pageNumber={pageNumber}
|
pageNumber={pageNumber}
|
||||||
signKey={signKey}
|
signKey={currWidgetsDetails?.key}
|
||||||
Id={uniqueId}
|
Id={uniqueId}
|
||||||
widgetType={widgetType}
|
widgetType={currWidgetsDetails?.type}
|
||||||
setUniqueId={setUniqueId}
|
setUniqueId={setUniqueId}
|
||||||
tempSignerId={tempSignerId}
|
tempSignerId={tempSignerId}
|
||||||
setTempSignerId={setTempSignerId}
|
setTempSignerId={setTempSignerId}
|
||||||
@@ -2436,17 +2475,13 @@ function PlaceHolderSign() {
|
|||||||
setZIndex={setZIndex}
|
setZIndex={setZIndex}
|
||||||
setIsPageCopy={setIsPageCopy}
|
setIsPageCopy={setIsPageCopy}
|
||||||
signersdata={signersdata}
|
signersdata={signersdata}
|
||||||
setSignKey={setSignKey}
|
|
||||||
handleLinkUser={handleLinkUser}
|
handleLinkUser={handleLinkUser}
|
||||||
setUniqueId={setUniqueId}
|
setUniqueId={setUniqueId}
|
||||||
isDragging={isDragging}
|
isDragging={isDragging}
|
||||||
setShowDropdown={setShowDropdown}
|
setShowDropdown={setShowDropdown}
|
||||||
setWidgetType={setWidgetType}
|
|
||||||
setIsRadio={setIsRadio}
|
setIsRadio={setIsRadio}
|
||||||
setIsCheckbox={setIsCheckbox}
|
setIsCheckbox={setIsCheckbox}
|
||||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
setSelectWidgetId={setSelectWidgetId}
|
|
||||||
selectWidgetId={selectWidgetId}
|
|
||||||
handleNameModal={setIsNameModal}
|
handleNameModal={setIsNameModal}
|
||||||
setTempSignerId={setTempSignerId}
|
setTempSignerId={setTempSignerId}
|
||||||
uniqueId={uniqueId}
|
uniqueId={uniqueId}
|
||||||
@@ -2461,6 +2496,7 @@ function PlaceHolderSign() {
|
|||||||
setFontColor={setFontColor}
|
setFontColor={setFontColor}
|
||||||
unSignedWidgetId={unSignedWidgetId}
|
unSignedWidgetId={unSignedWidgetId}
|
||||||
divRef={divRef}
|
divRef={divRef}
|
||||||
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -2498,7 +2534,6 @@ function PlaceHolderSign() {
|
|||||||
handleDeleteUser={handleDeleteUser}
|
handleDeleteUser={handleDeleteUser}
|
||||||
uniqueId={uniqueId}
|
uniqueId={uniqueId}
|
||||||
setSignerPos={setSignerPos}
|
setSignerPos={setSignerPos}
|
||||||
setSelectWidgetId={setSelectWidgetId}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -2544,6 +2579,22 @@ function PlaceHolderSign() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isShowModal[currWidgetsDetails?.key] && (
|
||||||
|
<WidgetsValueModal
|
||||||
|
key={currWidgetsDetails?.key}
|
||||||
|
xyPosition={signerPos}
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
setXyPosition={setSignerPos}
|
||||||
|
uniqueId={uniqueId}
|
||||||
|
setPageNumber={setPageNumber}
|
||||||
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
|
index={pageNumber}
|
||||||
|
isSave={true}
|
||||||
|
tempSignerId={tempSignerId}
|
||||||
|
setUniqueId={setUniqueId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={isAlreadyPlace.status}
|
isOpen={isAlreadyPlace.status}
|
||||||
title={t("document-alert")}
|
title={t("document-alert")}
|
||||||
@@ -2572,7 +2623,7 @@ function PlaceHolderSign() {
|
|||||||
)}
|
)}
|
||||||
<WidgetNameModal
|
<WidgetNameModal
|
||||||
signatureType={signatureType}
|
signatureType={signatureType}
|
||||||
widgetName={widgetName}
|
widgetName={currWidgetsDetails?.options?.name}
|
||||||
defaultdata={currWidgetsDetails}
|
defaultdata={currWidgetsDetails}
|
||||||
isOpen={isNameModal}
|
isOpen={isNameModal}
|
||||||
handleClose={handleNameModal}
|
handleClose={handleNameModal}
|
||||||
+357
-282
@@ -23,7 +23,8 @@ import EditorToolbar, {
|
|||||||
import DateFormatSelector from "../components/shared/fields/DateFormatSelector";
|
import DateFormatSelector from "../components/shared/fields/DateFormatSelector";
|
||||||
|
|
||||||
const Preferences = () => {
|
const Preferences = () => {
|
||||||
const appName = "OpenSign™";
|
const appName =
|
||||||
|
"OpenSign™";
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const editorRef = useRef();
|
const editorRef = useRef();
|
||||||
const editorRefCom = useRef();
|
const editorRefCom = useRef();
|
||||||
@@ -44,13 +45,17 @@ const Preferences = () => {
|
|||||||
const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState();
|
const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState();
|
||||||
const [timezone, setTimezone] = useState(usertimezone);
|
const [timezone, setTimezone] = useState(usertimezone);
|
||||||
const [activeTab, setactiveTab] = useState(0);
|
const [activeTab, setactiveTab] = useState(0);
|
||||||
const generaltab = { title: t("general"), icon: "fa-light fa-gears" };
|
const generaltab = {
|
||||||
|
name: "general",
|
||||||
|
title: t("general"),
|
||||||
|
icon: "fa-light fa-gears"
|
||||||
|
};
|
||||||
const [tab, setTab] = useState([generaltab]);
|
const [tab, setTab] = useState([generaltab]);
|
||||||
const [sendinOrder, setSendinOrder] = useState(true);
|
const [sendinOrder, setSendinOrder] = useState(true);
|
||||||
const [isTourEnabled, setIsTourEnabled] = useState(false);
|
const [isTourEnabled, setIsTourEnabled] = useState(false);
|
||||||
const [dateFormat, setDateFormat] = useState("MM/DD/YYYY");
|
const [dateFormat, setDateFormat] = useState("MM/DD/YYYY");
|
||||||
const [is12HourTime, setIs12HourTime] = useState(false);
|
const [is12HourTime, setIs12HourTime] = useState(false);
|
||||||
|
const [isLTVEnabled, setIsLTVEnabled] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSignType();
|
fetchSignType();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -60,9 +65,12 @@ const Preferences = () => {
|
|||||||
setIsTopLoader(true);
|
setIsTopLoader(true);
|
||||||
const EmailTab =
|
const EmailTab =
|
||||||
extClass?.[0]?.UserRole === "contracts_Admin"
|
extClass?.[0]?.UserRole === "contracts_Admin"
|
||||||
? [{ title: t("email"), icon: "fa-light fa-envelope" }]
|
? [{ name: "email", title: t("email"), icon: "fa-light fa-envelope" }]
|
||||||
: [];
|
: [];
|
||||||
const arr = [generaltab, ...EmailTab];
|
const arr = [
|
||||||
|
generaltab,
|
||||||
|
...EmailTab,
|
||||||
|
];
|
||||||
setTab(arr);
|
setTab(arr);
|
||||||
try {
|
try {
|
||||||
const user = JSON.parse(
|
const user = JSON.parse(
|
||||||
@@ -110,6 +118,9 @@ const Preferences = () => {
|
|||||||
const is12Hr =
|
const is12Hr =
|
||||||
_getUser?.Is12HourTime !== undefined ? _getUser?.Is12HourTime : false;
|
_getUser?.Is12HourTime !== undefined ? _getUser?.Is12HourTime : false;
|
||||||
setIs12HourTime(is12Hr);
|
setIs12HourTime(is12Hr);
|
||||||
|
const isLTVEnabled =
|
||||||
|
_getUser?.IsLTVEnabled !== undefined ? _getUser?.IsLTVEnabled : false;
|
||||||
|
setIsLTVEnabled(isLTVEnabled);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err while getting user details", err);
|
console.log("err while getting user details", err);
|
||||||
@@ -167,7 +178,8 @@ const Preferences = () => {
|
|||||||
SendinOrder: sendinOrder,
|
SendinOrder: sendinOrder,
|
||||||
IsTourEnabled: isTourEnabled,
|
IsTourEnabled: isTourEnabled,
|
||||||
DateFormat: dateFormat,
|
DateFormat: dateFormat,
|
||||||
Is12HourTime: is12HourTime
|
Is12HourTime: is12HourTime,
|
||||||
|
IsLTVEnabled: isLTVEnabled
|
||||||
};
|
};
|
||||||
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
||||||
if (updateRes) {
|
if (updateRes) {
|
||||||
@@ -284,6 +296,15 @@ const Preferences = () => {
|
|||||||
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
||||||
setRequestBody(updateRes?.RequestBody);
|
setRequestBody(updateRes?.RequestBody);
|
||||||
setRequestSubject(updateRes?.RequestSubject);
|
setRequestSubject(updateRes?.RequestSubject);
|
||||||
|
let extUser =
|
||||||
|
localStorage.getItem("Extand_Class") &&
|
||||||
|
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||||
|
if (extUser && extUser?.objectId) {
|
||||||
|
extUser.TenantId.RequestBody = updateRes?.RequestBody;
|
||||||
|
extUser.TenantId.RequestBody = updateRes?.RequestSubject;
|
||||||
|
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||||
|
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||||
|
}
|
||||||
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
||||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||||
}
|
}
|
||||||
@@ -322,7 +343,7 @@ const Preferences = () => {
|
|||||||
|
|
||||||
const handleTourInput = () => setIsTourEnabled(!isTourEnabled);
|
const handleTourInput = () => setIsTourEnabled(!isTourEnabled);
|
||||||
const handleSendinOrderInput = () => setSendinOrder(!sendinOrder);
|
const handleSendinOrderInput = () => setSendinOrder(!sendinOrder);
|
||||||
|
const tabName = (ind) => tab.find((t, i) => i === ind)?.name;
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title={t("Preferences")} />
|
<Title title={t("Preferences")} />
|
||||||
@@ -344,7 +365,7 @@ const Preferences = () => {
|
|||||||
<Loader />
|
<Loader />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
<h1 className="ml-4 mt-3 text-lg mb-2 font-semibold text-base-content">
|
||||||
{appName} {t("Preferences")}{" "}
|
{appName} {t("Preferences")}{" "}
|
||||||
<span>
|
<span>
|
||||||
<Tooltip message={`${appName} ${t("Preferences")}`} />
|
<Tooltip message={`${appName} ${t("Preferences")}`} />
|
||||||
@@ -360,9 +381,9 @@ const Preferences = () => {
|
|||||||
onClick={() => setactiveTab(ind)}
|
onClick={() => setactiveTab(ind)}
|
||||||
key={ind}
|
key={ind}
|
||||||
role="tab"
|
role="tab"
|
||||||
className={`${
|
className={`${activeTab === ind ? "op-tab-active" : ""} op-tab text-xs md:text-base pb-2 md:pb-0 transition-all `}
|
||||||
activeTab === ind ? "op-tab-active" : ""
|
aria-selected={activeTab === ind}
|
||||||
} op-tab text-xs md:text-base pb-2 md:pb-0 }`}
|
aria-controls={`panel-${tabData.title}`}
|
||||||
>
|
>
|
||||||
<i className={tabData.icon}></i>
|
<i className={tabData.icon}></i>
|
||||||
<span className="ml-1 md:ml-2">{tabData.title}</span>
|
<span className="ml-1 md:ml-2">{tabData.title}</span>
|
||||||
@@ -370,275 +391,336 @@ const Preferences = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center md:justify-start mt-3 md:mt-4 break-all">
|
<div
|
||||||
{activeTab === 0 ? (
|
id="panel-general"
|
||||||
<div className="ml-4 mt-1 mb-2 flex flex-col">
|
className="px-6 pt-4 pb-6"
|
||||||
<div className="mb-[0.75rem]">
|
aria-labelledby="tab-general"
|
||||||
<label
|
role="tabpanel"
|
||||||
className="mb-[0.7rem] text-[12px]"
|
>
|
||||||
htmlFor="signaturetype"
|
{tabName(activeTab) === "general" && (
|
||||||
>
|
<div className="grid grid-cols-1 md:grid-cols-12 md:gap-x-8">
|
||||||
<span className="font-medium text-[14px]">
|
{/* Left Column - Signature Settings */}
|
||||||
{t("allowed-signature-types")}
|
<div className="md:col-span-5 flex flex-col">
|
||||||
</span>
|
{/* Signature Types Section */}
|
||||||
<a data-tooltip-id="signtypes-tooltip" className="ml-1">
|
<div className="mb-6">
|
||||||
<sup>
|
<label
|
||||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
className="text-[14px] mb-[0.7rem] font-medium"
|
||||||
</sup>
|
htmlFor="signaturetype"
|
||||||
</a>
|
|
||||||
<ReactTooltip
|
|
||||||
id="signtypes-tooltip"
|
|
||||||
className="z-[999]"
|
|
||||||
>
|
>
|
||||||
<div className="max-w-[200px] md:max-w-[450px]">
|
{t("allowed-signature-types")}
|
||||||
<p className="font-bold">
|
<a
|
||||||
{t("allowed-signature-types")}
|
data-tooltip-id="signtypes-tooltip"
|
||||||
</p>
|
className="ml-1"
|
||||||
<p>{t("allowed-signature-types-help.p1")}</p>
|
>
|
||||||
<div className="p-[5px] ml-2">
|
<sup>
|
||||||
<ol className="list-disc">
|
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||||
<li>
|
</sup>
|
||||||
<span className="font-bold">Draw: </span>
|
</a>
|
||||||
<span>
|
<ReactTooltip
|
||||||
{t("allowed-signature-types-help.l1")}
|
id="signtypes-tooltip"
|
||||||
</span>
|
className="z-[999]"
|
||||||
</li>
|
>
|
||||||
<li>
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
<span className="font-bold">Type: </span>
|
<p className="font-bold">
|
||||||
<span>
|
{t("allowed-signature-types")}
|
||||||
{t("allowed-signature-types-help.l2")}
|
</p>
|
||||||
</span>
|
<p>{t("allowed-signature-types-help.p1")}</p>
|
||||||
</li>
|
<div className="p-[5px] ml-2">
|
||||||
<li>
|
<ol className="list-disc">
|
||||||
<span className="font-bold">Upload: </span>
|
<li>
|
||||||
<span>
|
<span className="font-bold">Draw: </span>
|
||||||
{t("allowed-signature-types-help.l3")}
|
<span>
|
||||||
</span>
|
{t("allowed-signature-types-help.l1")}
|
||||||
</li>
|
</span>
|
||||||
<li>
|
</li>
|
||||||
<span className="font-bold">Default: </span>
|
<li>
|
||||||
<span>
|
<span className="font-bold">Type: </span>
|
||||||
{t("allowed-signature-types-help.l4")}
|
<span>
|
||||||
</span>
|
{t("allowed-signature-types-help.l2")}
|
||||||
</li>
|
</span>
|
||||||
</ol>
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">Upload: </span>
|
||||||
|
<span>
|
||||||
|
{t("allowed-signature-types-help.l3")}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">Default: </span>
|
||||||
|
<span>
|
||||||
|
{t("allowed-signature-types-help.l4")}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ReactTooltip>
|
||||||
</ReactTooltip>
|
</label>
|
||||||
</label>
|
<div className="flex flex-col md:flex-row gap-3 mb-2">
|
||||||
<div className="ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
{signatureType.map((type, i) => (
|
||||||
{signatureType.map((type, i) => (
|
<div
|
||||||
|
key={i}
|
||||||
|
className="flex flex-row gap-2 items-center"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="op-checkbox op-checkbox-xs"
|
||||||
|
type="checkbox"
|
||||||
|
id={`signature-type-${type.name}`}
|
||||||
|
name="signaturetype"
|
||||||
|
onChange={() => handleCheckboxChange(i)}
|
||||||
|
checked={type.enabled}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor={`signature-type-${type.name}`}
|
||||||
|
className="text-sm font-medium text-base-content hover:underline underline-offset-2 cursor-pointer capitalize mb-0"
|
||||||
|
title={`Enabling this allows signers to ${type.name} signature`}
|
||||||
|
>
|
||||||
|
{type?.name === "typed" ? "type" : type?.name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Notify on Signatures Section */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||||
|
{t("notify-on-signatures")}
|
||||||
|
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||||
|
<sup>
|
||||||
|
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||||
|
</sup>
|
||||||
|
</a>
|
||||||
|
<ReactTooltip id="nos-tooltip" className="z-[999]">
|
||||||
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
|
<p className="font-bold">
|
||||||
|
{t("notify-on-signatures")}
|
||||||
|
</p>
|
||||||
|
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||||
|
<p>{t("notify-on-signatures-help.note")}</p>
|
||||||
|
</div>
|
||||||
|
</ReactTooltip>
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-row gap-6">
|
||||||
<div
|
<div
|
||||||
key={i}
|
className={
|
||||||
className="flex flex-row gap-[5px] items-center"
|
"flex items-center gap-2"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
id="notify-yes"
|
||||||
type="checkbox"
|
className="op-radio op-radio-xs"
|
||||||
name="signaturetype"
|
type="radio"
|
||||||
onChange={() => handleCheckboxChange(i)}
|
onChange={() => handleNotifySignChange(true)}
|
||||||
checked={type.enabled}
|
checked={isNotifyOnSignatures === true}
|
||||||
/>
|
/>
|
||||||
<div
|
<label
|
||||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
htmlFor="notify-yes"
|
||||||
title={`Enabling this allow signers to ${type.name} signature`}
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
>
|
>
|
||||||
{type?.name === "typed" ? "type" : type?.name}
|
{t("yes")}
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div
|
||||||
</div>
|
className={
|
||||||
</div>
|
"flex items-center gap-2"
|
||||||
<div className="mb-[0.75rem]">
|
}
|
||||||
<label className="mb-[0.7rem] text-[12px]">
|
>
|
||||||
<span className="text-[14px] font-medium">
|
<input
|
||||||
{t("notify-on-signatures")}
|
id="notify-no"
|
||||||
</span>
|
className="op-radio op-radio-xs"
|
||||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
type="radio"
|
||||||
<sup>
|
onChange={() => handleNotifySignChange(false)}
|
||||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
checked={isNotifyOnSignatures === false}
|
||||||
</sup>
|
/>
|
||||||
</a>
|
<label
|
||||||
<ReactTooltip id="nos-tooltip" className="z-[999]">
|
htmlFor="notify-no"
|
||||||
<div className="max-w-[200px] md:max-w-[450px]">
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
<p className="font-bold">
|
>
|
||||||
{t("notify-on-signatures")}
|
{t("no")}
|
||||||
</p>
|
</label>
|
||||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
|
||||||
<p>{t("notify-on-signatures-help.note")}</p>
|
|
||||||
</div>
|
|
||||||
</ReactTooltip>
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className={"flex items-center gap-2 ml-2 mb-1"}>
|
|
||||||
<input
|
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
|
||||||
type="radio"
|
|
||||||
onChange={() => handleNotifySignChange(true)}
|
|
||||||
checked={isNotifyOnSignatures === true}
|
|
||||||
/>
|
|
||||||
<div className="text-[13px] cursor-default capitalize">
|
|
||||||
{t("yes")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={"flex items-center gap-2 ml-2 mb-1"}>
|
|
||||||
<input
|
|
||||||
className="mr-[2px] op-radio op-radio-xs"
|
|
||||||
type="radio"
|
|
||||||
onChange={() => handleNotifySignChange(false)}
|
|
||||||
checked={isNotifyOnSignatures === false}
|
|
||||||
/>
|
|
||||||
<div className="text-[13px] cursor-default capitalize">
|
|
||||||
{t("no")}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="mb-[0.75rem]">
|
{/* Send in Order Section */}
|
||||||
<TimezoneSelector
|
<div className="mb-6">
|
||||||
timezone={timezone}
|
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||||
setTimezone={setTimezone}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.75rem]">
|
|
||||||
<DateFormatSelector
|
|
||||||
timezone={timezone}
|
|
||||||
dateFormat={dateFormat}
|
|
||||||
is12HourTime={is12HourTime}
|
|
||||||
setIs12HourTime={setIs12HourTime}
|
|
||||||
setDateFormat={setDateFormat}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mb-[0.75rem] text-[12px]">
|
|
||||||
<label className="block mb-[0.7rem]">
|
|
||||||
<span className="text-[14px] font-medium">
|
|
||||||
{t("send-in-order")}
|
{t("send-in-order")}
|
||||||
</span>
|
<a
|
||||||
<a
|
data-tooltip-id="sendInOrder-tooltip"
|
||||||
data-tooltip-id="sendInOrder-tooltip"
|
className="ml-1"
|
||||||
className="ml-1"
|
>
|
||||||
>
|
<sup>
|
||||||
<sup>
|
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
</sup>
|
||||||
</sup>
|
</a>
|
||||||
</a>
|
<ReactTooltip
|
||||||
<ReactTooltip
|
id="sendInOrder-tooltip"
|
||||||
id="sendInOrder-tooltip"
|
className="z-[999]"
|
||||||
className="z-[999]"
|
>
|
||||||
>
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
<div className="max-w-[200px] md:max-w-[450px]">
|
<p className="font-bold">{t("send-in-order")}</p>
|
||||||
<p className="font-bold">{t("send-in-order")}</p>
|
<p>{t("send-in-order-help.p1")}</p>
|
||||||
<p>{t("send-in-order-help.p1")}</p>
|
<div className="p-[5px]">
|
||||||
<div className="p-[5px]">
|
<ol className="list-disc">
|
||||||
<ol className="list-disc">
|
<li>
|
||||||
<li>
|
<span className="font-bold">
|
||||||
<span className="font-bold">
|
{t("yes")}:{" "}
|
||||||
{t("yes")}:{" "}
|
</span>
|
||||||
</span>
|
<span>{t("send-in-order-help.p2")}</span>
|
||||||
<span>{t("send-in-order-help.p2")}</span>
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
<span className="font-bold">
|
||||||
<span className="font-bold">{t("no")}: </span>
|
{t("no")}:{" "}
|
||||||
<span>{t("send-in-order-help.p3")}</span>
|
</span>
|
||||||
</li>
|
<span>{t("send-in-order-help.p3")}</span>
|
||||||
</ol>
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<p>{t("send-in-order-help.p4")}</p>
|
||||||
</div>
|
</div>
|
||||||
<p>{t("send-in-order-help.p4")}</p>
|
</ReactTooltip>
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-row gap-6">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
id="order-yes"
|
||||||
|
type="radio"
|
||||||
|
value={true}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
name="SendinOrder"
|
||||||
|
checked={sendinOrder}
|
||||||
|
onChange={handleSendinOrderInput}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="order-yes"
|
||||||
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
|
>
|
||||||
|
{t("yes")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
id="order-no"
|
||||||
|
type="radio"
|
||||||
|
value={false}
|
||||||
|
name="SendinOrder"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
checked={!sendinOrder}
|
||||||
|
onChange={handleSendinOrderInput}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="order-no"
|
||||||
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
|
>
|
||||||
|
{t("no")}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</ReactTooltip>
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={true}
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
name="SendinOrder"
|
|
||||||
checked={sendinOrder}
|
|
||||||
onChange={handleSendinOrderInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("yes")}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={false}
|
|
||||||
name="SendinOrder"
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
checked={!sendinOrder}
|
|
||||||
onChange={handleSendinOrderInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("no")}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="mb-[0.75rem] text-[12px]">
|
{/* Enable Tour Section */}
|
||||||
<label className="block mb-[0.7rem]">
|
<div className="mb-6">
|
||||||
<span className="text-[14px] font-medium">
|
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||||
{t("enable-tour")}
|
{t("enable-tour")}
|
||||||
</span>
|
<a
|
||||||
<a
|
data-tooltip-id="istourenabled-tooltip"
|
||||||
data-tooltip-id="istourenabled-tooltip"
|
className="ml-1"
|
||||||
className="ml-1"
|
>
|
||||||
>
|
<sup>
|
||||||
<sup>
|
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
</sup>
|
||||||
</sup>
|
</a>
|
||||||
</a>
|
<ReactTooltip
|
||||||
<ReactTooltip
|
id="istourenabled-tooltip"
|
||||||
id="istourenabled-tooltip"
|
className="z-[999]"
|
||||||
className="z-[999]"
|
>
|
||||||
>
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
<div className="max-w-[200px] md:max-w-[450px]">
|
<p className="font-bold">{t("enable-tour")}</p>
|
||||||
<p className="font-bold">{t("enable-tour")}</p>
|
<div className="p-[5px]">
|
||||||
<div className="p-[5px]">
|
<ol className="list-disc">
|
||||||
<ol className="list-disc">
|
<li>
|
||||||
<li>
|
<span className="font-bold">
|
||||||
<span className="font-bold">
|
{t("yes")}:{" "}
|
||||||
{t("yes")}:{" "}
|
</span>
|
||||||
</span>
|
<span>{t("istourenabled-help.p1")}</span>
|
||||||
<span>{t("istourenabled-help.p1")}</span>
|
</li>
|
||||||
</li>
|
<li>
|
||||||
<li>
|
<span className="font-bold">
|
||||||
<span className="font-bold">{t("no")}: </span>
|
{t("no")}:{" "}
|
||||||
<span>{t("istourenabled-help.p2")}</span>
|
</span>
|
||||||
</li>
|
<span>{t("istourenabled-help.p2")}</span>
|
||||||
</ol>
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{t("istourenabled-help.p3", {
|
||||||
|
appName: appName
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
</ReactTooltip>
|
||||||
{t("istourenabled-help.p3", {
|
</label>
|
||||||
appName: appName
|
<div className="flex flex-row gap-6">
|
||||||
})}
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<input
|
||||||
|
id="tour-yes"
|
||||||
|
type="radio"
|
||||||
|
value={true}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
name="IsTourEnabled"
|
||||||
|
checked={isTourEnabled}
|
||||||
|
onChange={handleTourInput}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="tour-yes"
|
||||||
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
|
>
|
||||||
|
{t("yes")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
id="tour-no"
|
||||||
|
type="radio"
|
||||||
|
value={false}
|
||||||
|
name="IsTourEnabled"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
checked={!isTourEnabled}
|
||||||
|
onChange={handleTourInput}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="tour-no"
|
||||||
|
className="text-sm text-base-content cursor-pointer mb-0"
|
||||||
|
>
|
||||||
|
{t("no")}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</ReactTooltip>
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-col md:flex-row md:gap-4">
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={true}
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
name="IsTourEnabled"
|
|
||||||
checked={isTourEnabled}
|
|
||||||
onChange={handleTourInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("yes")}</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value={false}
|
|
||||||
name="IsTourEnabled"
|
|
||||||
className="op-radio op-radio-xs"
|
|
||||||
checked={!isTourEnabled}
|
|
||||||
onChange={handleTourInput}
|
|
||||||
/>
|
|
||||||
<div className="text-center">{t("no")}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-[0.75rem]">
|
{/* Right Column - Timezone & Date Settings */}
|
||||||
|
<div className="md:col-span-7 flex flex-col">
|
||||||
|
<div className="mb-6">
|
||||||
|
<TimezoneSelector
|
||||||
|
timezone={timezone}
|
||||||
|
setTimezone={setTimezone}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<DateFormatSelector
|
||||||
|
timezone={timezone}
|
||||||
|
dateFormat={dateFormat}
|
||||||
|
is12HourTime={is12HourTime}
|
||||||
|
setIs12HourTime={setIs12HourTime}
|
||||||
|
setDateFormat={setDateFormat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Save Button - Full Width */}
|
||||||
|
<div className="md:col-span-12 flex justify-start mt-2">
|
||||||
<button
|
<button
|
||||||
className="op-btn op-btn-primary w-[110px]"
|
className="op-btn op-btn-primary w-[110px]"
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
@@ -647,8 +729,9 @@ const Preferences = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)}
|
||||||
<div className="flex flex-col mx-4 mb-4">
|
{tabName(activeTab) === "email" && (
|
||||||
|
<div className="flex flex-col mb-4">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h1 className="text-[14px] mb-[0.7rem] font-medium">
|
<h1 className="text-[14px] mb-[0.7rem] font-medium">
|
||||||
{t("request-email")}
|
{t("request-email")}
|
||||||
@@ -660,12 +743,10 @@ const Preferences = () => {
|
|||||||
>
|
>
|
||||||
<div className="text-lg font-normal">
|
<div className="text-lg font-normal">
|
||||||
<label className="text-sm">
|
<label className="text-sm">
|
||||||
{t("subject")}
|
{t("subject")}{" "}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={"request-sub-tooltip"}
|
id={"request-sub-tooltip"}
|
||||||
message={`${t(
|
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||||
"variables-use"
|
|
||||||
)}: {{sender_name}} {{document_title}}`}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -674,20 +755,16 @@ const Preferences = () => {
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setRequestSubject(e.target.value)
|
setRequestSubject(e.target.value)
|
||||||
}
|
}
|
||||||
placeholder={`{{sender_name}} ${t(
|
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
|
||||||
"send-to-sign"
|
|
||||||
)} {{document_title}}`}
|
|
||||||
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg font-normal py-2">
|
<div className="text-lg font-normal py-2">
|
||||||
<label className="text-sm mt-3">
|
<label className="text-sm mt-3">
|
||||||
{t("body")}
|
{t("body")}{" "}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={"request-body-tooltip"}
|
id={"request-body-tooltip"}
|
||||||
message={`${t(
|
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||||
"variables-use"
|
|
||||||
)}: {{sender_name}} {{document_title}}`}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<EditorToolbar containerId="toolbar1" />
|
<EditorToolbar containerId="toolbar1" />
|
||||||
@@ -730,12 +807,10 @@ const Preferences = () => {
|
|||||||
>
|
>
|
||||||
<div className="text-lg font-normal">
|
<div className="text-lg font-normal">
|
||||||
<label className="text-sm">
|
<label className="text-sm">
|
||||||
{t("subject")}
|
{t("subject")}{" "}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={"complete-sub-tooltip"}
|
id={"complete-sub-tooltip"}
|
||||||
message={`${t(
|
message={`${t("variables-use")}:{{sender_name}} {{document_title}}`}
|
||||||
"variables-use"
|
|
||||||
)}:{{sender_name}} {{document_title}}`}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -744,20 +819,16 @@ const Preferences = () => {
|
|||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setCompletionSubject(e.target.value)
|
setCompletionSubject(e.target.value)
|
||||||
}
|
}
|
||||||
placeholder={`{{sender_name}} ${t(
|
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
|
||||||
"send-to-sign"
|
|
||||||
)} {{document_title}}`}
|
|
||||||
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg font-normal py-2">
|
<div className="text-lg font-normal py-2">
|
||||||
<label className="text-sm mt-3">
|
<label className="text-sm mt-3">
|
||||||
{t("body")}
|
{t("body")}{" "}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={"complete-body-tooltip"}
|
id={"complete-body-tooltip"}
|
||||||
message={`${t(
|
message={`${t("variables-use")}:{{sender_name}} {{document_title}} {{signing_url}}`}
|
||||||
"variables-use"
|
|
||||||
)}:{{sender_name}} {{document_title}} {{signing_url}}`}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<EditorToolbar containerId="toolbar2" />
|
<EditorToolbar containerId="toolbar2" />
|
||||||
@@ -793,6 +864,10 @@ const Preferences = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{tabName(activeTab) === "security" && (
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user