Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20752161d3 | ||
|
|
ac86b6ef61 | ||
|
|
dac8957904 | ||
|
|
ef686aec6a | ||
|
|
f31155e0b4 | ||
|
|
78e4857d1a | ||
|
|
c3144366d9 | ||
|
|
f00a7c9e50 | ||
|
|
803d25e9d1 | ||
|
|
ca1b87309f | ||
|
|
0b4548130d | ||
|
|
e8b3c7fed8 | ||
|
|
2ce43ec2c8 | ||
|
|
d5af1e8a17 | ||
|
|
fab7998e22 | ||
|
|
4a4db6851e | ||
|
|
870973e7f3 | ||
|
|
4146680c1e | ||
|
|
d9fcc69ca4 | ||
|
|
d1c405912b | ||
|
|
745aca1754 | ||
|
|
672eeea851 | ||
|
|
98efff65c9 | ||
|
|
fe21978538 | ||
|
|
ca4ccfd889 | ||
|
|
6cb2d69566 | ||
|
|
98b9ebb228 | ||
|
|
1c2f453496 | ||
|
|
b01d8125d9 | ||
|
|
85bc2fbbb0 | ||
|
|
e857724502 | ||
|
|
086d8ee5be | ||
|
|
04543e826a | ||
|
|
04f6e38778 | ||
|
|
1b5c85fd5f | ||
|
|
123425d7d1 | ||
|
|
a5b9d8178c | ||
|
|
aefd2ff933 | ||
|
|
fe93be8be7 | ||
|
|
369e3660fc | ||
|
|
378c0f057b | ||
|
|
4f3c034cec | ||
|
|
e7d3453744 | ||
|
|
47cc0ef580 | ||
|
|
04f71be6a2 | ||
|
|
07603f4bc2 | ||
|
|
ea16180551 | ||
|
|
75ad566d54 | ||
|
|
fda9fb534b | ||
|
|
cb3e31ebbf | ||
|
|
400525debb |
@@ -241,6 +241,15 @@
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ehowe",
|
||||
"name": "Eugene Howe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/756984?v=4",
|
||||
"profile": "https://github.com/ehowe",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,34 +21,46 @@ jobs:
|
||||
dockerfile: apps/OpenSign/Dockerhubfile
|
||||
- image: opensign/opensignserver
|
||||
dockerfile: apps/OpenSignServer/Dockerhubfile
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
|
||||
# 👇 Enable emulation so we can build for arm64 on amd64 runners
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
# 👇 Enable Buildx for multi-arch builds
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Extract metadata (tags, labels) for Docker
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ matrix.image }}
|
||||
-
|
||||
name: Debug - List files opensign
|
||||
|
||||
- name: Debug - List files opensign
|
||||
run: ls -R apps/OpenSign/ # Adjust the path as needed
|
||||
-
|
||||
name: Debug - List files opensignserver
|
||||
|
||||
- name: Debug - List files opensignserver
|
||||
run: ls -R apps/OpenSignServer/ # Adjust the path as needed
|
||||
-
|
||||
name: Build and push
|
||||
|
||||
- name: Build and push (multi-arch)
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.dockerfile }}
|
||||
push: true
|
||||
# 👇 Build for both AMD64 & ARM64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
@@ -165,6 +165,7 @@ We would like to thank all our contributors and users for their support and feed
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1024mb"><img src="https://avatars.githubusercontent.com/u/9301204?v=4?s=100" width="100px;" alt="1024mb"/><br /><sub><b>1024mb</b></sub></a><br /><a href="#translation-1024mb" title="Translation">🌍</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ehowe"><img src="https://avatars.githubusercontent.com/u/756984?v=4?s=100" width="100px;" alt="Eugene Howe"/><br /><sub><b>Eugene Howe</b></sub></a><br /><a href="#code-ehowe" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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="theme-color" content="#000000" />
|
||||
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
||||
<!-- <link rel="apple-touch-icon" href="/logo192.png" /> -->
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" />
|
||||
<title>...Loading</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root" style="touch-action:pan-x pan-y;"></div>
|
||||
<script type="module" src="/src/index.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -1,59 +1,58 @@
|
||||
{
|
||||
"name": "open_sign",
|
||||
"version": "2.21.1",
|
||||
"version": "2.30.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@imgly/background-removal": "^1.6.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.14.2",
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@imgly/background-removal": "^1.7.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.16.2",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.2.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"axios": "^1.10.0",
|
||||
"@reduxjs/toolkit": "^2.10.1",
|
||||
"axios": "^1.13.2",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^25.3.0",
|
||||
"i18next": "^25.6.3",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"parse": "^6.1.1",
|
||||
"parse": "^7.0.2",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pkijs": "^3.0.8",
|
||||
"pkijs": "^3.3.3",
|
||||
"print-js": "^1.6.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"quill-html-edit-button": "^3.0.0",
|
||||
"radix-ui": "^1.4.2",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-datepicker": "^8.4.0",
|
||||
"react-datepicker": "^8.9.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-i18next": "^15.7.4",
|
||||
"react-konva": "^18.2.12",
|
||||
"react-pdf": "^9.2.1",
|
||||
"react-quill-new": "^3.4.6",
|
||||
"react-quill-new": "^3.6.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router": "^7.6.3",
|
||||
"react-router": "^7.9.6",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.1",
|
||||
"react-select": "^5.10.2",
|
||||
"react-signature-canvas": "^1.1.0-alpha.2",
|
||||
"react-timezone-select": "^3.2.8",
|
||||
"react-tooltip": "^5.29.1",
|
||||
"react-tooltip": "^5.30.0",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"regex-parser": "^2.3.1",
|
||||
"serve": "^14.2.4",
|
||||
"serve": "^14.2.5",
|
||||
"styled-components": "^5.3.11",
|
||||
"web-vitals": "^5.0.3",
|
||||
"web-vitals": "^5.1.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -94,40 +93,38 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.7",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@babel/runtime-corejs2": "^7.27.6",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@babel/core": "^7.28.3",
|
||||
"@babel/preset-env": "^7.28.3",
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/runtime-corejs2": "^7.28.3",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/react": "^18.3.23",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
||||
"@types/react": "^19.2.6",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"babel-loader": "^10.0.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.2.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.24",
|
||||
"dotenv": "^16.6.1",
|
||||
"eslint": "^9.30.0",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"jsdom": "^26.1.0",
|
||||
"lint-staged": "^16.1.2",
|
||||
"jsdom": "^27.2.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"tailwindcss": "^3.4.18",
|
||||
"vite": "^7.2.4",
|
||||
"vite-plugin-svgr": "^4.5.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB |
@@ -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-btn": "Jetzt einrichten",
|
||||
"sandbox-news": "Dies ist eine Sandbox-Umgebung. Bitte nicht für produktive Zwecke verwenden.",
|
||||
@@ -8,7 +8,7 @@
|
||||
"dark-mode": "Dunkelmodus",
|
||||
"name": "Name",
|
||||
"phone": "Telefon",
|
||||
"phone-optional": "optional",
|
||||
"phone-optional": "Optional",
|
||||
"email": "E-Mail",
|
||||
"company": "Unternehmen",
|
||||
"job-title": "Berufsbezeichnung",
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "Name",
|
||||
"Date": "Datum"
|
||||
},
|
||||
"folder": "Ordner",
|
||||
"pdf": "Pdf",
|
||||
"context-menu": {
|
||||
"Download": "Herunterladen",
|
||||
"Rename": "Umbenennen",
|
||||
@@ -44,6 +46,10 @@
|
||||
"contact-now": "Jetzt kontaktieren",
|
||||
"upgrade-to": "Upgrade zu",
|
||||
"plan": "Plan",
|
||||
"connect": "Verbinden",
|
||||
"connect-to-g-drive": "Mit Google Drive verbinden",
|
||||
"reconnect-to-g-drive": "Erneut mit Google Drive verbinden",
|
||||
"gdrive-info-connect": "Wenn Google Drive verbunden ist, wird das abgeschlossene Dokument im Ordner {{appName}} auf Google Drive gespeichert.",
|
||||
"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-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.",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "Willkommen zurück!",
|
||||
"Login-to-your-account": "Melden Sie sich bei Ihrem Konto an",
|
||||
"password": "Passwort",
|
||||
"forgot-password": "Passwort vergessen?",
|
||||
"forgot-password": "Passwort vergessen",
|
||||
"loading": "Wird geladen...",
|
||||
"of": "von",
|
||||
"sign-SSO": "Mit SSO anmelden",
|
||||
"login-page": "Anmeldeseite",
|
||||
"or": "ODER",
|
||||
"signup-page": "Registrierungsseite",
|
||||
"password-match-length": "Neues Passwort und Bestätigungspasswort sollten übereinstimmen",
|
||||
"password-length": "Das Passwort sollte 8 Zeichen lang sein",
|
||||
"password-case": "Das Passwort sollte einen Großbuchstaben, einen Kleinbuchstaben und eine Zahl enthalten",
|
||||
"password-special-char": "Das Passwort sollte ein Sonderzeichen enthalten",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "Teams",
|
||||
"Team-update-successfully": "Team erfolgreich aktualisiert.",
|
||||
"Users": "Benutzer"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "Globales SMTP"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Benötigt Ihre Unterschrift",
|
||||
@@ -159,7 +167,8 @@
|
||||
"daily-mail-quota": "Tägliches E-Mail-Kontingent",
|
||||
"Save as template": "Als Vorlage speichern",
|
||||
"Fix & resend": "Korrigieren und erneut senden",
|
||||
"Kiosk Mode": "Kiosk Modus"
|
||||
"Kiosk Mode": "Kiosk Modus",
|
||||
"Reset password": "Passwort zurücksetzen"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nr.",
|
||||
@@ -181,7 +190,20 @@
|
||||
"created-date": "Erstellungsdatum",
|
||||
"Type": "Type",
|
||||
"Logs": "Protokolle",
|
||||
"Expiry-date": "Ablaufdatum"
|
||||
"Expiry-date": "Ablaufdatum",
|
||||
"Company": "Unternehmen",
|
||||
"JobTitle": "Berufsbezeichnung",
|
||||
"Time to complete (Days)": "Zeit zur Fertigstellung (Tage)",
|
||||
"Auto reminder": "Automatische Erinnerung",
|
||||
"Remind once in every (Days)": "Erinnere einmal alle (Tage)",
|
||||
"Enable OTP verification": "OTP-Verifizierung aktivieren",
|
||||
"Enable Tour": "Tour aktivieren",
|
||||
"Notify on signatures": "Bei Signaturen benachrichtigen",
|
||||
"Allow modifications": "Änderungen zulassen",
|
||||
"Redirect url": "Weiterleitungs-URL",
|
||||
"Created Date": "Erstellungsdatum",
|
||||
"Updated Date": "Aktualisierungsdatum",
|
||||
"Expiry Date": "Ablaufdatum"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Dies sind Dokumente, die Sie begonnen, aber noch nicht zum Versenden fertiggestellt haben.",
|
||||
@@ -193,16 +215,15 @@
|
||||
"Contactbook": "Dies ist eine Liste von Kontakten/Unterzeichnern, die Sie hinzugefügt haben. Diese erscheinen als Vorschläge, wenn Sie neue Unterzeichner hinzufügen möchten.",
|
||||
"Templates": "Dies ist eine Liste von Vorlagen, die Ihnen zur Verfügung stehen, um Dokumente zu erstellen. Sie können die Schaltfläche 'Verwenden' anklicken, um ein neues Dokument mit einer Vorlage zu erstellen, das Dokument zu ändern und Unterzeichner im nächsten Schritt hinzuzufügen."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Selbst unterschreiben",
|
||||
"Request Signatures": "Signaturen anfordern",
|
||||
"New Template": "Neue Vorlage"
|
||||
},
|
||||
"Sign Yourself": "Selbst unterschreiben",
|
||||
"Request Signatures": "Signaturen anfordern",
|
||||
"New Template": "Neue Vorlage",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "Datei ausgewählt",
|
||||
"template-title": "Vorlagentitel",
|
||||
"document-title": "Dokumenttitel",
|
||||
"title": "Titel",
|
||||
"description": "Beschreibung",
|
||||
"time-to-complete": "Bearbeitungszeit (Tage)",
|
||||
"send-in-order": "In der Reihenfolge senden",
|
||||
@@ -280,7 +301,7 @@
|
||||
"deactivate": "deaktivieren",
|
||||
"this-user": "diesen Benutzer",
|
||||
"delete-user": "Benutzer löschen",
|
||||
"delete": "löschen",
|
||||
"delete": "Löschen",
|
||||
"add-user": "Benutzer hinzufügen",
|
||||
"password-generateed": "Das Passwort wird nur einmal generiert; stellen Sie sicher, dass Sie es kopieren.",
|
||||
"Team status": "Teamstatus",
|
||||
@@ -367,7 +388,8 @@
|
||||
"dropdown": "Dropdown",
|
||||
"radio button": "Radiobutton",
|
||||
"image": "Bild",
|
||||
"email": "E-Mail"
|
||||
"email": "E-Mail",
|
||||
"number": "Zahl"
|
||||
},
|
||||
"fields": "Felder",
|
||||
"recipients": "Empfänger",
|
||||
@@ -379,6 +401,7 @@
|
||||
"certificate": "Zertifikat",
|
||||
"decline": "Ablehnen",
|
||||
"finish": "Fertigstellen",
|
||||
"done": "Fertig",
|
||||
"mail": "E-Mail",
|
||||
"sign-now": "Jetzt unterzeichnen",
|
||||
"successfully-signed": "Erfolgreich unterzeichnet!",
|
||||
@@ -388,6 +411,10 @@
|
||||
"Email-verified-alert-1": "E-Mail wurde verifiziert.",
|
||||
"Email-verified-alert-2": "E-Mail wurde bereits verifiziert.",
|
||||
"upload-stamp-image": "Stempelbild hochladen",
|
||||
"draw-signature": "Unterschrift zeichnen",
|
||||
"draw-initials": "Initialen zeichnen",
|
||||
"enter-text": "Text eingeben",
|
||||
"enter-widgettype": "{{widgetType}} eingeben",
|
||||
"draw": "Zeichnen",
|
||||
"type": "Schreiben",
|
||||
"color-type": {
|
||||
@@ -400,7 +427,7 @@
|
||||
"upload": "Hochladen",
|
||||
"initial-teb": "Initialen",
|
||||
"signature-tab": "Unterschrift",
|
||||
"initial-alert": "Meine Initiale nicht gefunden",
|
||||
"initial-alert": "Meine Initiale wurde nicht gefunden",
|
||||
"copy-title": "Widget kopieren zu",
|
||||
"contact-delete-alert": "Sind Sie sicher, dass Sie diesen Kontakt löschen möchten?",
|
||||
"reset-password-alert-1": "Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail-Adresse gesendet.",
|
||||
@@ -419,6 +446,19 @@
|
||||
"maximum-check": "Maximale Anzahl",
|
||||
"cell-count": "Zellzahl",
|
||||
"default-value": "Standardwert",
|
||||
"formula": "Formel",
|
||||
"formula-placeholder": "Erstellen Sie eine Formel mit Zahlen-Widgets",
|
||||
"formula-no-widgets": "Für diesen Unterzeichner sind noch keine weiteren Zahlen-Widgets vorhanden.",
|
||||
"supported-operators": "Unterstützte Operatoren:",
|
||||
"formula-helper": "Klicken Sie auf einen Zahlen-Widget-Chip, um ihn einzufügen (z. B. {{number-1}} + {{number-2}}).",
|
||||
"formula-error-letter": "Widget-Namen müssen in doppelte geschweifte Klammern gesetzt werden (z. B. {{number-1}}).",
|
||||
"formula-error-characters": "Außerhalb der Klammern sind nur Zahlen sowie +, -, *, /, (, ) erlaubt.",
|
||||
"formula-error-braces": "Jede öffnende {{ muss eine passende }} haben.",
|
||||
"formula-error-operators": "Verwenden Sie bei Operatoren oder Klammern mindestens zwei Werte.",
|
||||
"formula-error-leading-operator": "Formeln dürfen nicht mit Operatoren beginnen.",
|
||||
"decimal-places": "Dezimalstellen",
|
||||
"number-of-decimal-places": "Anzahl der Dezimalstellen",
|
||||
"decimal-places-helper": "Auf 0 setzen für ganze Zahlen",
|
||||
"select": "Auswählen",
|
||||
"read-only": "Ist schreibgeschützt",
|
||||
"hide-labels": "Labels ausblenden",
|
||||
@@ -465,7 +505,7 @@
|
||||
"add-recipients": "Empfänger hinzufügen",
|
||||
"loading-mssg": "Das könnte etwas Zeit in Anspruch nehmen",
|
||||
"send-mail": "E-Mail senden",
|
||||
"signature-field-widget": "Für jeden Benutzer muss mindestens ein Signaturfeld hinzugefügt werden. Sie haben keine Signaturfelder für {{signersName}} hinzugefügt",
|
||||
"signature-field-widget": "Jeder Benutzer muss mindestens ein Signatur-Widget haben. Sie haben kein Signatur-Widget für {{signersName}} hinzugefügt.",
|
||||
"placeholder-alert-1": "Bitte stellen Sie sicher, dass für alle Empfänger mindestens ein Signatur-Widget hinzugefügt wurde.",
|
||||
"placeholder-alert-2": "Bitte bestätigen Sie, dass Sie das Textfeld ausgefüllt haben.",
|
||||
"placeholder-alert-3": "Sind Sie sicher, dass Sie dieses Dokument zur Unterzeichnung senden möchten?",
|
||||
@@ -600,7 +640,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "Sie haben sich erfolgreich angemeldet! Werfen wir einen Blick darauf.",
|
||||
"home-layout-2": "Um Dokumente zum Selbstunterzeichnen hochzuladen oder um Unterschriften anderer anzufordern, wählen Sie einfach die entsprechenden Schaltflächen.",
|
||||
"home-layout-3": "Sie sind bereit, {{appName}} zu verwenden! Wenn Sie Unterstützung benötigen, kontaktieren Sie uns gerne.",
|
||||
"home-layout-3": "Sie sind bereit, {{appName}} zu verwenden!",
|
||||
"home-layout-4": "Markiere uns auf",
|
||||
"generate-token": "Jetzt upgraden, um ein Produktions-API-Token zu generieren.",
|
||||
"opensign-drive-1": "Klicken Sie auf die Breadcrumb-Links, um einfach durch die Ordnerhierarchie zu navigieren und die Dokumente in jedem Ordner anzuzeigen.",
|
||||
"opensign-drive-2": "Klicken Sie auf die Schaltfläche Hinzufügen, um einen neuen Ordner oder ein neues Dokument zu erstellen.",
|
||||
@@ -610,28 +651,26 @@
|
||||
"opensign-drive-6": "Klicken Sie mit der rechten Maustaste auf ein Dokument, um Optionen wie Herunterladen, Umbenennen, Verschieben und Löschen anzuzeigen. Klicken Sie auf das Dokument, um es zu öffnen.",
|
||||
"opensign-drive-7": "Klicken Sie mit der rechten Maustaste auf einen Ordner, um Optionen anzuzeigen. Wählen Sie Umbenennen, um den Namen des Ordners zu ändern, oder klicken Sie auf den Ordner, um seinen Inhalt anzuzeigen.",
|
||||
"pdf-request-file-1": "Liste der Unterzeichner, die das Dokument noch unterschreiben müssen.",
|
||||
"pdf-request-file-2": "Klicken Sie auf die Platzhalter im Dokument, um zu unterschreiben. Sie sehen dann Optionen, Ihre Unterschrift zu zeichnen, einzugeben oder ein Bild hochzuladen.",
|
||||
"pdf-request-file-3": "Klicken Sie auf die Schaltflächen Ablehnen oder Fertig stellen, um durch Ihr Dokument zu navigieren. Verwenden Sie das Auslassungsmenü für weitere Optionen, einschließlich der Schaltfläche Herunterladen.",
|
||||
"pdf-request-file-2": "Klicken Sie auf eines der Felder im Dokument, um zu beginnen. Danach können Sie die erforderlichen Informationen eingeben.",
|
||||
"pdf-request-file-3": "Sobald Sie alle erforderlichen Felder ausgefüllt haben, klicken Sie auf „Fertigstellen“. Danach können Sie das unterschriebene Dokument herunterladen. Je nach den Einstellungen des Absenders erhalten Sie möglicherweise eine Kopie des abgeschlossenen Dokuments zusammen mit dem Abschlusszertifikat, sobald alle Empfänger unterschrieben haben.",
|
||||
"pdf-request-file-4": "Liste der Unterzeichner, die das Dokument bereits unterzeichnet haben.",
|
||||
"pdf-request-file-5": "Sie können auf Automatisch alles unterschreiben klicken, um automatisch an allen für Sie vorgesehenen Stellen zu unterschreiben. Überprüfen Sie das Dokument gründlich, bevor Sie diese Schaltfläche verwenden.",
|
||||
"pdf-request-file-6": "Bitte füllen Sie die Felder auf Seite {{pagenumbers}} aus, die alle zur einfachen Identifizierung in der gleichen Farbe hervorgehoben sind.",
|
||||
"placeholder-sign-1": "Wählen Sie einen Empfänger aus dieser Liste aus, um einen Platzhalter hinzuzufügen, an dem er unterschreiben soll. Der Platzhalter wird in der gleichen Farbe wie der Empfängername angezeigt, sobald Sie ihn im Dokument ablegen.",
|
||||
"placeholder-sign-1": "Wählen Sie einen Empfänger aus dieser Liste, um ein Widget hinzuzufügen. Das Widget wird in derselben Farbe wie der Empfängername angezeigt, sobald Sie es auf das Dokument ziehen.",
|
||||
"placeholder-sign-2": "Das Klicken auf die Schaltfläche 'Empfänger hinzufügen' ermöglicht es Ihnen, weitere Unterzeichner hinzuzufügen.",
|
||||
"placeholder-sign-3": "Klicken Sie auf diese Schaltfläche, um weitere Empfänger/Unterzeichner zum Dokument hinzuzufügen.",
|
||||
"placeholder-sign-4": "Ziehen Sie ein Feld in das Dokument oder klicken Sie darauf, um es hinzuzufügen.",
|
||||
"placeholder-sign-5": "Der PDF-Inhaltsbereich zeigt bereits die vorhandenen Platzhalter der Vorlage an. Diese Platzhalter entsprechen der Farbe des Empfängernamens, um sie leicht erkennbar zu machen.",
|
||||
"placeholder-sign-4": "Ziehen oder klicken Sie auf ein Widget, um es dem Dokument hinzuzufügen.",
|
||||
"placeholder-sign-5": "Der PDF-Inhaltsbereich zeigt bereits die vorhandenen Widgets der Vorlage an. Diese Widgets entsprechen der Farbe des Empfängernamens, um sie leicht erkennbar zu machen.",
|
||||
"placeholder-sign-6": "Mit einem Klick auf 'Weiter' wird das Dokument gespeichert. Im nächsten Schritt können Sie die E-Mails, die an die Empfänger versendet werden sollen, anpassen oder die Signaturlinks kopieren und diese selbst mit den Empfängern teilen.",
|
||||
"report-1":"Klicken Sie auf die Schaltfläche „Hinzufügen“, um eine neue Vorlage zu erstellen. Vorlagen sind wiederverwendbare Dokumente, mit denen schnell neue Dokumente mit derselben Struktur und unterschiedlichen Unterzeichnern erstellt werden können. Eine HR-Vorlage für die Einarbeitung könnte beispielsweise vordefinierte Rollen wie „Personalleiter“ und „Neuer Mitarbeiter“ enthalten. Bei jeder Verwendung der Vorlage können Sie die Rolle „Neuer Mitarbeiter“ verschiedenen neuen Mitarbeitern zuweisen, während die Rolle „Personalleiter“ unverändert bleibt. So wird ein nahtloser Einarbeitungsprozess für jeden neuen Mitarbeiter ermöglicht.",
|
||||
"report-1": "Klicken Sie auf die Schaltfläche „Hinzufügen“, um eine neue Vorlage zu erstellen. Vorlagen sind wiederverwendbare Dokumente, mit denen schnell neue Dokumente mit derselben Struktur und unterschiedlichen Unterzeichnern erstellt werden können. Eine HR-Vorlage für die Einarbeitung könnte beispielsweise vordefinierte Rollen wie „Personalleiter“ und „Neuer Mitarbeiter“ enthalten. Bei jeder Verwendung der Vorlage können Sie die Rolle „Neuer Mitarbeiter“ verschiedenen neuen Mitarbeitern zuweisen, während die Rolle „Personalleiter“ unverändert bleibt. So wird ein nahtloser Einarbeitungsprozess für jeden neuen Mitarbeiter ermöglicht.",
|
||||
"redirect": "Klicken Sie auf die Schaltfläche 'Verwenden', um ein neues Dokument aus einer bestehenden Vorlage zu erstellen.",
|
||||
"bulksend": "Um schnell mehrere Dokumente mithilfe einer vorhandenen Vorlage zu versenden, indem Sie einfach die E-Mail-Adressen der Empfänger erstellen, klicken Sie auf die Schaltfläche ‚Massenversand‘",
|
||||
"option": "Dieses Menü zeigt weitere Optionen wie Bearbeiten und Löschen. Verwenden Sie die Schaltfläche 'Bearbeiten', um Unterzeichnerrollen hinzuzufügen, Felder zu ändern und Ihre Vorlage zu aktualisieren. Änderungen gelten für alle zukünftigen Dokumente, die aus dieser Vorlage erstellt werden, wirken sich jedoch nicht auf vorhandene Dokumente aus. Verwenden Sie die Schaltfläche 'Löschen', um die Vorlage zu entfernen.",
|
||||
"signyour-self-1": "Wählen und ziehen Sie Ihre bevorzugten Widgets auf das PDF, um Ihr Dokument vor dem Unterzeichnen anzupassen. Wählen Sie die perfekten Stellen für jede Anpassung aus, um das Dokument an Ihre Bedürfnisse anzupassen.",
|
||||
"bulksend": "Um schnell mehrere Dokumente mit einer vorhandenen Vorlage zu versenden, geben Sie einfach die E-Mail-Adressen der Empfänger ein und klicken Sie auf die Schaltfläche 'Massenversand'. Sie können bis zu 50 Empfänger erreichen.",
|
||||
"option": "Dieses Menü zeigt weitere Optionen wie Bearbeiten, Löschen, Umbenennen, Duplizieren, Teilen usw. <1>Klicken Sie hier</1>, um mehr über alle verfügbaren Optionen zu erfahren. <3>Hinweis: Änderungen an einer vorhandenen Vorlage gelten für alle zukünftigen Dokumente, die aus dieser Vorlage erstellt werden, betreffen jedoch nicht bereits versandte Dokumente.</3>",
|
||||
"signyour-self-1": "Wählen oder ziehen Sie Ihre bevorzugten Widgets auf das PDF, um Ihr Dokument vor der Unterzeichnung anzupassen. Platzieren Sie die Widgets an den perfekten Stellen, um das Dokument an Ihre Bedürfnisse anzupassen.",
|
||||
"signyour-self-2": "Ziehen Sie ein Widget irgendwo in diesen Bereich. Sie können es später in der Größe ändern und verschieben.",
|
||||
"template-placeholder-1": "Das Klicken auf die Schaltfläche 'Rolle hinzufügen' ermöglicht es Ihnen, verschiedene Unterzeichnerrollen hinzuzufügen. Sie können Benutzern diese Rollen in den folgenden Schritten zuweisen.",
|
||||
"template-placeholder-2": "Sobald Rollen hinzugefügt wurden, wählen Sie eine Rolle aus der Liste aus, um einen Platzhalter hinzuzufügen, an dem der Unterzeichner unterschreiben soll. Der Platzhalter wird in der gleichen Farbe wie der Rollenname angezeigt, sobald Sie ihn im Dokument ablegen.",
|
||||
"template-placeholder-3": "Ziehen Sie ein Feld in das Dokument oder klicken Sie darauf, um es hinzuzufügen.",
|
||||
"template-placeholder-4": "Ziehen Sie den Platzhalter für eine Rolle an eine beliebige Stelle im Dokument. Denken Sie daran, dass er in der gleichen Farbe wie der Name des Empfängers angezeigt wird, um die Zuordnung zu erleichtern.",
|
||||
"template-placeholder-5": "Das Klicken auf 'Weiter' speichert die aktuelle Vorlage. Nach dem Speichern werden Sie gefragt, ob Sie ein neues Dokument aus dieser Vorlage erstellen möchten.",
|
||||
"template-placeholder-2": "Nachdem Sie Rollen hinzugefügt haben, wählen Sie eine aus der Liste, um ein Widget für diesen Empfänger zu platzieren. Sie können entweder auf das Widget klicken oder es auf das Dokument ziehen. Sobald es platziert ist, wird das Widget in derselben Farbe wie die ausgewählte Rolle angezeigt.",
|
||||
"template-placeholder-3": "Das Klicken auf 'Weiter' speichert die aktuelle Vorlage. Nach dem Speichern werden Sie gefragt, ob Sie ein neues Dokument aus dieser Vorlage erstellen möchten.",
|
||||
"webhook-1": "Jetzt upgraden, um einen Webhook einzurichten",
|
||||
"Need your Signature": "Das Klicken auf diese Karte führt Sie zu einer Liste der Dokumente, die auf Ihre Überprüfung warten.",
|
||||
"Out for signatures": "Das Klicken auf diese Karte führt Sie zu einer Liste der Dokumente, die auf eine Signatur warten.",
|
||||
@@ -728,7 +767,7 @@
|
||||
"public-tour-message": "Die Vorlage muss öffentlich sein, bevor Sie einen teilbaren Link generieren können.",
|
||||
"add-user-template": "Sie müssen eine Rolle hinzufügen, bevor Sie Felder dafür hinzufügen können.",
|
||||
"pdf-uncompatible": "Diese PDF-Datei ist nicht kompatibel, bitte kontaktieren Sie {{appName}}",
|
||||
"text-field-tour": "Felder vom Typ 'Text' müssen im Voraus ausgefüllt werden, bevor das Dokument versendet wird. Wenn Sie möchten, dass die Unterzeichner Eingaben machen, verwenden Sie das Feld 'Texteingabe'.",
|
||||
"text-field-tour": "Felder zum Vorabfüllen müssen vor dem Senden des Dokuments ausgefüllt werden. Wenn Sie Eingaben von den Unterzeichnern benötigen, verwenden Sie stattdessen die Felder für Unterzeichner.",
|
||||
"attach-signer-tour": "Sie müssen einen Unterzeichner jeder Rolle zuweisen. Dies können Sie durch Klicken auf dieses Symbol tun. Sobald Sie einen Unterzeichner auswählen, wird er allen Feldern der zugehörigen Rolle zugewiesen, die in derselben Farbe erscheinen.",
|
||||
"allowed-signature-types": "Erlaubte Signaturtypen",
|
||||
"at-least-one-signature-type": "Mindestens ein Signaturtyp sollte aktiviert sein.",
|
||||
@@ -776,7 +815,7 @@
|
||||
"agree-p1": "Ich bestätige, dass ich die ",
|
||||
"agree-p2": "Offenlegung elektronischer Aufzeichnungen und Signaturen",
|
||||
"agree-p3": "gelesen und verstanden habe und der Verwendung elektronischer Aufzeichnungen und Signaturen zustimme.",
|
||||
"agrre-button": "Zustimmen & Fortfahren",
|
||||
"agrre-button": "Ich bestätige und stimme zu, fortzufahren",
|
||||
"term-cond-title": "Allgemeine Geschäftsbedingungen",
|
||||
"term-cond-h": "OFFENLEGUNG ELEKTRONISCHER AUFZEICHNUNGEN UND SIGNATUREN",
|
||||
"term-cond-p1": "Diese Offenlegung elektronischer Aufzeichnungen und Signaturen ('Offenlegung') ist eine Vereinbarung zwischen dem Dokumentersteller ('Sender') und dem Unterzeichner ('Ihnen'), bereitgestellt über die {{appName}} Plattform ('Plattform'). Durch das Signieren von Dokumenten über {{appName}} stimmen Sie den in dieser Offenlegung beschriebenen Bedingungen zu. Bitte lesen Sie sie sorgfältig durch, bevor Sie fortfahren.",
|
||||
@@ -879,6 +918,10 @@
|
||||
"thanks-for-feedback": "Danke für Ihr Feedback 🙏",
|
||||
"share-your-feedback": "Teilen Sie Ihr Feedback",
|
||||
"share-your-review": "Teilen Sie Ihre Bewertung",
|
||||
"please-select-rating": "Bitte wählen Sie eine Bewertung aus",
|
||||
"feedback-optional": "Feedback (optional)",
|
||||
"feedback-saved": "Feedback wurde gespeichert.",
|
||||
"feedback-save-error": "Feedback konnte nicht gespeichert werden, bitte versuchen Sie es erneut.",
|
||||
"date-format": "Datumsformat",
|
||||
"document-deleted": "Das Dokument wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.",
|
||||
"save-as-template-?": "Sind Sie sicher, dass Sie dieses Dokument als Vorlage speichern möchten?",
|
||||
@@ -1032,14 +1075,204 @@
|
||||
"not-calculated": "Nicht berechnet",
|
||||
"not-found-in-signature": "Nicht in Signatur gefunden",
|
||||
"readonly-error": "Das schreibgeschützte {{widgetName}}-Widget muss einen Standardwert haben oder kann optional gemacht werden.",
|
||||
"choose-one":"Wählen Sie eine aus",
|
||||
"choose-one": "Wählen Sie eine aus",
|
||||
"search-templates": "Vorlagen durchsuchen…",
|
||||
"search-documents": "Dokumente suchen…",
|
||||
"search-contacts": "Kontakte durchsuchen…",
|
||||
"edit-draft": "Entwurf bearbeiten",
|
||||
"add-role-alert": "Bitte fügen Sie mindestens eine Rolle hinzu",
|
||||
"invalid-email-found": "Ungültige E-Mail gefunden: {{email}}",
|
||||
"duplicate-email-found": "Doppelte E-Mail gefunden: {{email}}",
|
||||
"vertical": "Vertikal",
|
||||
"horizontal": "Horizontal",
|
||||
"billing": "Abrechnung",
|
||||
"console": "Konsole"
|
||||
}
|
||||
"console": "Konsole",
|
||||
"prefill-widget": "Vorausgefüllte Widgets",
|
||||
"action-prohibited": "Diese Aktion ist für Ihre E-Mail-Domain nicht erlaubt. Bitte wenden Sie sich an Ihren Administrator, um Hilfe zu erhalten.",
|
||||
"must-have-at-least-one-vacant-role": "Mindestens eine Rolle muss unzugewiesen sein, bevor Sie ein template auf 'public' setzen.",
|
||||
"remove-duplicate": "Bitte doppelte Option entfernen",
|
||||
"prefill-bulk-error": "Der Massenversand ist nicht erlaubt, wenn Prefill-Widgets hinzugefügt wurden. Bitte entfernen Sie die Prefill-Widgets, um fortzufahren.",
|
||||
"session-expired-title": "Sitzung abgelaufen",
|
||||
"access-denied": "Zugriff verweigert",
|
||||
"upgrade": "Upgrade",
|
||||
"do-not-access-app": "Sie haben keinen Zugriff auf diese Anwendung.",
|
||||
"dont-have-access": "Sie haben keinen Zugriff.",
|
||||
"valid-email-alert": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
|
||||
"otp-not-validate": "OTP ist ungültig.",
|
||||
"domain-not-allowed": "Diese Domain ist nicht erlaubt",
|
||||
"atleast-one-recipient-alert": "Bitte fügen Sie mindestens einen Empfänger hinzu!",
|
||||
"incorrect-password-or-decryption-failed": "Falsches Passwort oder Entschlüsselung fehlgeschlagen.",
|
||||
"incorrect-password-for-file": "Falsches Passwort für Datei: {{file}}",
|
||||
"error-uploading-pdf": "Fehler beim Hochladen der PDF.",
|
||||
"provide-password": "Bitte geben Sie das Passwort an.",
|
||||
"only-pdf-allowed": "Nur PDF-Dateien sind erlaubt.",
|
||||
"invalid-username-password-region": "Ungültiger Benutzername/Passwort oder Region.",
|
||||
"pfx-extension-alert": "Bitte laden Sie eine Datei mit der Endung .pfx hoch.",
|
||||
"email-already-exist": "E-Mail existiert bereits",
|
||||
"branding": "Markenbildung",
|
||||
"branding-help": "Branding ermöglicht White-Labeling für Ihre App",
|
||||
"custom-sub-domain": "Benutzerdefinierte Subdomain",
|
||||
"app-name": "App Name",
|
||||
"provide-domain-name": "Geben Sie Ihren Domainnamen an",
|
||||
"provide-app-name": "Geben Sie Ihren App-Namen an",
|
||||
"logo": "Logo",
|
||||
"upload-app-logo": "Laden Sie Ihr App-Logo hoch",
|
||||
"prefill-unfilled-widget": "Die folgenden Pflichtfelder dürfen nicht leer sein: {{emptyWidget}}. Bitte füllen Sie diese aus, um fortzufahren.",
|
||||
"Dashboard": "Armaturenbrett",
|
||||
"Analytics": "Analytik",
|
||||
"Templates": "Vorlagen",
|
||||
"Need your sign": "Benötigt Ihre Unterschrift",
|
||||
"In Progress": "In Bearbeitung",
|
||||
"Completed": "Abgeschlossen",
|
||||
"Drafts": "Entwürfe",
|
||||
"Declined": "Abgelehnt",
|
||||
"Expired": "Abgelaufen",
|
||||
"Contactbook": "Kontaktbuch",
|
||||
"My Signature": "Meine Unterschrift",
|
||||
"API Token": "API-Token",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Einstellungen",
|
||||
"Teams": "Teams",
|
||||
"Users": "Benutzer",
|
||||
"Drive": "Drive",
|
||||
"Branding": "Markenbildung",
|
||||
"Mail": "Mail",
|
||||
"Storage": "Speicher",
|
||||
"Signing certificate": "Signierzertifikat",
|
||||
"General": "Allgemein",
|
||||
"Organizations": "Organisationen",
|
||||
"OrgAdmins": "OrgAdmins",
|
||||
"Debug Pdf": "PDF debuggen",
|
||||
"New Document": "Neues Dokument",
|
||||
"subscription": "Abonnement",
|
||||
"Draft document": "Dokumententwurf",
|
||||
"Draft template": "Vorlagenentwurf",
|
||||
"Public sign": "Öffentliche Signatur",
|
||||
"Signup": "Registrieren",
|
||||
"delete-contact": "Kontakt löschen",
|
||||
"total-records-found": "Gesamtanzahl gefundener Einträge: {{count}}",
|
||||
"Invalid-records-found": "Ungültige Einträge gefunden: {{records}}",
|
||||
"previous": "Zurück",
|
||||
"page-n-of-n": "Seite {{currentPage}} von {{totalPages}}",
|
||||
"import": "Importieren",
|
||||
"search": "Suchen",
|
||||
"viewed-on": "Angesehen am: {{ViewedOn}}",
|
||||
"signed-on": "Unterschrieben am: {{SignedOn}}",
|
||||
"hide": "Ausblenden",
|
||||
"show-more": "Mehr anzeigen",
|
||||
"browse-or-drag-to-replace-existing-file": "Durchsuchen oder per Drag & Drop eine neue Datei ziehen, um die vorhandene zu ersetzen",
|
||||
"optional-details": "Optionale Angaben",
|
||||
"hide-optional-details": "optionale Angaben ausblenden",
|
||||
"mail-adapter-subscription-alert": "Bitte upgraden Sie auf den Professional- oder Team-Plan, um den Mail-Adapter einzurichten.",
|
||||
"connect-to-mail": "Mit Gmail verbinden",
|
||||
"custom-smtp": "Benutzerdefiniertes SMTP",
|
||||
"default-smtp": "{{appName}} Standard-SMTP",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"sender-email": "Absender-E-Mail",
|
||||
"username": "Benutzername",
|
||||
"use-default-mail-adapter": "Möchten Sie wirklich die Standard-Mailserver von {{appName}} verwenden, um Ihre Signaturanfragen zu versenden? Wir empfehlen, Ihre eigenen Gmail- oder SMTP-Server zu verwenden, um die Zustellbarkeit zu verbessern.",
|
||||
"verification-code-sent-registered-email": "Ein Bestätigungscode wurde an Ihre registrierte E-Mail-Adresse <1>{{useremail}}</1> gesendet. Bitte geben Sie den Code unten ein, um Ihre Einstellungen zu bestätigen.",
|
||||
"smpt-credentials": "SMTP-Zugangsdaten",
|
||||
"delete-account": "Konto löschen",
|
||||
"delete-account-que": "Sind Sie sicher, dass Sie Ihr Konto löschen möchten?",
|
||||
"delete-account-que-user": "Sie sind dabei, diesen Benutzer und alle zugehörigen Daten dauerhaft zu löschen. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||
"user-deleted-successfully": "Benutzer und alle zugehörigen Daten wurden erfolgreich gelöscht.",
|
||||
"account-deletion-request-sent-via-mail": "Wir haben Ihnen einen Bestätigungslink per E-Mail gesendet. Genehmigen Sie die Anfrage, um die Löschung Ihres Kontos abzuschließen.",
|
||||
"type-exact-email-delete": "Geben Sie die genaue E-Mail ein, um das Löschen zu aktivieren",
|
||||
"email-does-not-match": "E-Mail stimmt nicht überein.",
|
||||
"please-type-to-confirm": "Bitte geben Sie <1>{{userEmail}}</1> ein, um zu bestätigen:",
|
||||
"email-settings": "E-Mail-Einstellungen",
|
||||
"email-settings-help": "Um eine bessere Zustellbarkeit Ihrer Signaturanfrage-E-Mails im Posteingang zu gewährleisten, können Sie Ihren eigenen E-Mail-Anbieter verbinden. Wählen Sie eine der folgenden Optionen:",
|
||||
"connect-to-gmail": "Mit Gmail verbinden: ",
|
||||
"connect-to-gmail-help": "Verwenden Sie Ihr Gmail-Konto, um Signaturanfrage-E-Mails direkt aus Ihrem eigenen Posteingang zu senden. Dies verbessert die Zustellraten und Zuverlässigkeit.",
|
||||
"connect-to-smtp": "Benutzerdefiniertes SMTP: ",
|
||||
"connect-to-smtp-help": "Verwenden Sie Ihren eigenen SMTP-Server, um E-Mails über Ihre Domain zu versenden. Diese Option gibt Ihnen die volle Kontrolle über Ihre E-Mail-Infrastruktur und verbessert die Zustellbarkeit sowie die Markenkonsistenz.",
|
||||
"connect-to-default": "Wenn Sie möchten, können Sie auch die Standard-Mailserver von {{appName}} verwenden, wir empfehlen jedoch für optimale Ergebnisse die Nutzung Ihrer eigenen.",
|
||||
"email-settings-redirect-message": "Diese Einstellung wurde von der Konsole zu den Haupteinstellungen → Präferenzen verschoben. Diese Seite wird in zukünftigen Versionen entfernt.",
|
||||
"go-to-preferences-menu": "Zum Menü 'Einstellungen' gehen",
|
||||
"document-download-filename-format": "Dateiname-Format für Dokument-Download",
|
||||
"preview": "Vorschau: ",
|
||||
"download-filename-format-help": "Wählen Sie, wie heruntergeladene PDFs benannt werden. Ihre Auswahl wird in Ihrem Profil gespeichert und in der gesamten App verwendet.",
|
||||
"delete-action-prohibited": "Diese Aktion ist nicht erlaubt. Bitte wenden Sie sich an Ihren Administrator, um die Löschung des Kontos zu beantragen.",
|
||||
"not-verified": "Nicht verifiziert",
|
||||
"verified": "Verifiziert",
|
||||
"expires": "Läuft ab",
|
||||
"fix-resend-error": "Sie können dieses Dokument nicht korrigieren und erneut senden, da es vorab ausgefüllte Widgets enthält.",
|
||||
"duplicate-template-error": "Sie können diese Vorlage nicht duplizieren, da sie vorab ausgefüllte Widgets enthält.",
|
||||
"save-as-template-error": "Dieses Dokument kann nicht als Vorlage gespeichert werden, da es vorab ausgefüllte Widgets enthält.",
|
||||
"redirecting-you-in": "Sie werden in {{redirectTimeLeft}} Sek. weitergeleitet...",
|
||||
"pdf-tools-tour": "Klicken Sie auf diese Schaltflächen, um Seiten hinzuzufügen, zu löschen, neu anzuordnen, zu drehen und zu zoomen.",
|
||||
"widgets": "Widgets",
|
||||
"prefill-tour": "Verwenden Sie diese Option, um Informationen im Voraus einzugeben, bevor Sie das Dokument an Empfänger senden.",
|
||||
"empty-prefill-error": "Die folgenden Pflichtfelder dürfen nicht leer bleiben:",
|
||||
"please-fill-out": "Bitte füllen Sie diese aus, um fortzufahren.",
|
||||
"custom-signing-certificate": "Benutzerdefiniertes Signaturzertifikat",
|
||||
"signing-certificate-help": "Sie können Ihr eigenes Dokumentensignaturzertifikat hochladen, das zum Signieren aller Ihrer Dokumente sowie Abschlusszertifikate verwendet wird. Die Zertifikatsdatei sollte ein P12-Zertifikat im PFX-Format sein.",
|
||||
"certificate-file-p12-in-PFX-format": "Zertifikatsdatei (p12-Zertifikat im PFX-Format)",
|
||||
"password-of-pfx-file": "Passwort der PFX-Datei eingeben",
|
||||
"update": "Aktualisieren",
|
||||
"use-default-certificate": "Standardzertifikat von {{appName}} verwenden",
|
||||
"upgrade-to-team-plan": "Auf Team-Plan upgraden",
|
||||
"setup-file-storage": "Dateispeicher einrichten",
|
||||
"save-and-activate": "Speichern & aktivieren",
|
||||
"logging-out-to-apply-settings": "Sie werden abgemeldet, um neue Einstellungen anzuwenden",
|
||||
"reconnect-to-default": "Erneut mit {{appName}} verbinden",
|
||||
"active-file-adapter": "Aktiver Dateiadapter",
|
||||
"file-adapter-unique-name": "Eindeutiger Name des Dateiadapters",
|
||||
"unique-name-for-file-adapter": "Eindeutigen Namen für Dateiadapter eingeben",
|
||||
"storage-provider": "Speicheranbieter",
|
||||
"enter-bucket-name": "Bucket-Namen eingeben",
|
||||
"enter-space-name": "Space-Namen eingeben",
|
||||
"enter-region-of-bucket": "Region des Buckets eingeben",
|
||||
"enter-region-of-space": "Region des Spaces eingeben",
|
||||
"enter-access-key": "Access Key eingeben",
|
||||
"enter-secret-access-key": "Secret Access Key eingeben",
|
||||
"otp-email": "Wir haben einen Bestätigungscode gesendet",
|
||||
"save-as-temp-warn": "Hinweis: Dieses Dokument enthält vorausgefüllte Widgets, die automatisch entfernt werden, da sie bereits im Basisdokument integriert sind.",
|
||||
"edit-document": "Dokument bearbeiten",
|
||||
"modify": "Ändern",
|
||||
"merge-certificate-to-pdf": "Zertifikat mit PDF zusammenführen",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "Dies stellt sicher, dass das Abschlusszertifikat im endgültigen PDF-Dokument enthalten ist. Bitte beachten Sie jedoch, dass das Zertifikat nach dem Zusammenführen nicht mehr vom Hauptdokument getrennt werden kann.",
|
||||
"p2": "Wenn Sie sich entscheiden, nicht zusammenzuführen, wird das Abschlusszertifikat als separate PDF-Datei zusammen mit dem unterzeichneten Dokument bereitgestellt."
|
||||
},
|
||||
"read-only-date-error": "Das schreibgeschützte Datums-Widget muss ein Standarddatum haben.",
|
||||
"set-date": "Datum festlegen",
|
||||
"set-today": "Unterzeichnungsdatum",
|
||||
"enter-name": "Name eingeben",
|
||||
"enter-email": "E-Mail eingeben",
|
||||
"subscribe-to-opensign-msg": "Abonnieren Sie {{appName}} und genießen Sie unbegrenzt kostenlose digitale Signaturen.",
|
||||
"duplicate-subscribe-msg": "Mit Duplizieren können Sie eine exakte Kopie der ausgewählten Vorlage erstellen, die Sie wiederverwenden oder ändern können, ohne das Original zu beeinflussen.",
|
||||
"save-as-template-msg": "Speichern Sie dieses Dokument als wiederverwendbare Vorlage, die Sie für zukünftige Dokumente erneut nutzen können.",
|
||||
"public-credit-alert": "Das Signieren über diesen Link verbraucht Ihre kostenlosen E-Mail-Credits. Als kostenloser Nutzer erhalten Sie 15 E-Mail-Credits pro Monat, um zu verhindern, dass Spammer unsere Systeme missbrauchen. Um höhere Limits und ununterbrochenen Zugriff zu genießen, abonnieren Sie die <1>OpenSign™-Bezahlpläne.</1>",
|
||||
"know-more-about": "Mehr erfahren über",
|
||||
"free-unlimited-signatures": "Kostenlose unbegrenzte Signaturen",
|
||||
"email-quota-updated": "Tägliches E-Mail-Kontingent erfolgreich aktualisiert.",
|
||||
"password-has-been-reset": "Passwort wurde zurückgesetzt.",
|
||||
"Unauthorized to reset your own password.": "Nicht berechtigt, Ihr eigenes Passwort zurückzusetzen.",
|
||||
"User not found or not allowed.": "Benutzer nicht gefunden oder nicht erlaubt.",
|
||||
"User not found.": "Benutzer nicht gefunden.",
|
||||
"Admin user tenant not found.": "Admin-Benutzer-Tenant nicht gefunden.",
|
||||
"User is not authenticated.": "Benutzer ist nicht authentifiziert.",
|
||||
"Please provide required parameters.": "Bitte geben Sie die erforderlichen Parameter an.",
|
||||
"reset-password": "Benutzerpasswort zurücksetzen",
|
||||
"enter-strong-password": "Geben Sie ein sicheres Passwort ein oder generieren Sie eines automatisch.",
|
||||
"autogenerate": "Automatisch generieren",
|
||||
"extend": "Verlängern",
|
||||
"console-general-help": "Um einzelnen Benutzern zu ermöglichen, ihre eigenen E-Mail-Vorlagen anzupassen, navigieren Sie zu",
|
||||
"console-mail-help": "Um unternehmensweite E-Mail-Vorlagen zu aktualisieren, gehen Sie zu",
|
||||
"admin-setting-disabled": "Diese Einstellung ist deaktiviert. Bitte wenden Sie sich für Unterstützung an den Administrator.",
|
||||
"company-email-settings": "Unternehmens-E-Mail-Einstellungen",
|
||||
"allow-email-template-individually": "E-Mail-Vorlagenanpassung für Benutzer individuell erlauben ",
|
||||
"user-email-template-general-help": "Wenn diese Option deaktiviert ist, können Benutzer ihre E-Mail-Vorlagen nicht mehr anpassen.",
|
||||
"disable-individual-user-smtp": "individuelle benutzer-smtp-einstellungen deaktivieren",
|
||||
"enable-individual-user-smtp": "individuelle benutzer-smtp-einstellungen aktivieren",
|
||||
"user-smtp-general-help": "Wenn deaktiviert, können Benutzer ihre eigenen SMTP-Einstellungen nicht konfigurieren. Alle E-Mails verwenden dann die globale SMTP-Konfiguration.",
|
||||
"global-smtp-settings": "Globale SMTP-Einstellungen",
|
||||
"global-smtp-settings-help-p1": "Diese Einstellungen gelten global für Ihr gesamtes {{appName}}-Konto. Wenn ein Benutzer keine eigenen E-Mail-Zustellungseinstellungen konfiguriert hat, verwendet das System automatisch die hier definierten globalen SMTP-Einstellungen.",
|
||||
"global-smtp-settings-help-p2": "Um eine bessere Zustellbarkeit aller Signaturanfragen sicherzustellen, können Sie Ihren bevorzugten E-Mail-Anbieter verbinden. Wählen Sie eine der folgenden Optionen:",
|
||||
"global-connect-to-gmail-help": "Verwenden Sie ein Gmail-Konto, um Signaturanfragen direkt aus dem Posteingang Ihrer Organisation zu senden. Dies verbessert die Zustellrate, Reputation und Zuverlässigkeit.",
|
||||
"global-connect-to-smtp-help": "Verwenden Sie Ihren eigenen SMTP-Server, um E-Mails über Ihre Domain zu senden. Dies bietet volle Kontrolle über Ihre E-Mail-Infrastruktur, verbessert die Zustellbarkeit und sorgt für konsistentes Branding.",
|
||||
"global-smtp-settings-help-p3": "Wenn keine individuellen Benutzereinstellungen konfiguriert sind, werden automatisch diese globalen Einstellungen angewendet."
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"dark-mode": "Dark mode",
|
||||
"name": "Name",
|
||||
"phone": "Phone",
|
||||
"phone-optional": "optional",
|
||||
"phone-optional": "Optional",
|
||||
"email": "Email",
|
||||
"company": "Company",
|
||||
"job-title": "Job title",
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "Name",
|
||||
"Date": "Date"
|
||||
},
|
||||
"folder": "Folder",
|
||||
"pdf": "Pdf",
|
||||
"context-menu": {
|
||||
"Download": "Download",
|
||||
"Rename": "Rename",
|
||||
@@ -44,6 +46,10 @@
|
||||
"contact-now": "Contact now",
|
||||
"upgrade-to": "Upgrade to",
|
||||
"plan": "Plan",
|
||||
"connect": "connect",
|
||||
"connect-to-g-drive": "Connect to Google Drive",
|
||||
"reconnect-to-g-drive": "Reconnect to Google Drive",
|
||||
"gdrive-info-connect": "When Google Drive is connected, the completed document will be saved in the {{appName}} folder on Google Drive.",
|
||||
"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-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "Welcome back!",
|
||||
"Login-to-your-account": "Login to your account",
|
||||
"password": "Password",
|
||||
"forgot-password": "Forgot password?",
|
||||
"forgot-password": "Forgot password",
|
||||
"loading": "Loading...",
|
||||
"of": "of",
|
||||
"sign-SSO": "Sign in with SSO",
|
||||
"login-page": "Login P+age",
|
||||
"or": "OR",
|
||||
"signup-page": "Signup page",
|
||||
"password-match-length": "New password and confirmation password should match",
|
||||
"password-length": "Password should be 8 characters long",
|
||||
"password-case": "Password should contain uppercase letter, lowercase letter, digit",
|
||||
"password-special-char": " Password should contain special character",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "Teams",
|
||||
"Team-update-successfully": "Team Updated successfully.",
|
||||
"Users": "Users"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "Global SMTP"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Need your signature",
|
||||
@@ -159,7 +167,8 @@
|
||||
"daily-mail-quota": "Daily Email Quota",
|
||||
"Save as template": "Save as template",
|
||||
"Fix & resend": "Fix & Resend",
|
||||
"Kiosk Mode": "Kiosk Mode"
|
||||
"Kiosk Mode": "Kiosk Mode",
|
||||
"Reset password": "Reset password"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Sr.No",
|
||||
@@ -181,7 +190,20 @@
|
||||
"created-date": "Created date",
|
||||
"Type": "Type",
|
||||
"Logs": "Logs",
|
||||
"Expiry-date": "Expiry date"
|
||||
"Expiry-date": "Expiry date",
|
||||
"Company": "Company",
|
||||
"JobTitle": "Job title",
|
||||
"Time to complete (Days)": "Time to complete (Days)",
|
||||
"Auto reminder": "Auto reminder",
|
||||
"Remind once in every (Days)": "Remind once in every (Days)",
|
||||
"Enable OTP verification": "Enable OTP verification",
|
||||
"Enable Tour": "Enable Tour",
|
||||
"Notify on signatures": "Notify on signatures",
|
||||
"Allow modifications": "Allow modifications",
|
||||
"Redirect url": "Redirect url",
|
||||
"Created Date": "Created Date",
|
||||
"Updated Date": "Updated Date",
|
||||
"Expiry Date": "Expiry Date"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -193,16 +215,15 @@
|
||||
"Contactbook": "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document.",
|
||||
"Templates": "This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Sign yourself",
|
||||
"Request Signatures": "Request signatures",
|
||||
"New Template": "New template"
|
||||
},
|
||||
"Sign Yourself": "Sign yourself",
|
||||
"Request Signatures": "Request signatures",
|
||||
"New Template": "New template",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "file(s) selected",
|
||||
"template-title": "Template title",
|
||||
"document-title": "Document title",
|
||||
"title": "Title",
|
||||
"description": "Description",
|
||||
"time-to-complete": "Time to complete (Days)",
|
||||
"send-in-order": "Send in order",
|
||||
@@ -280,7 +301,7 @@
|
||||
"deactivate": "deactivate",
|
||||
"this-user": " this user",
|
||||
"delete-user": "Delete user",
|
||||
"delete": "delete",
|
||||
"delete": "Delete",
|
||||
"add-user": "Add user",
|
||||
"password-generateed": "Password will only be generated once; make sure to copy it.",
|
||||
"Team status": "Team status",
|
||||
@@ -367,7 +388,8 @@
|
||||
"dropdown": "dropdown",
|
||||
"radio button": "radio button",
|
||||
"image": "image",
|
||||
"email": "email"
|
||||
"email": "email",
|
||||
"number": "number"
|
||||
},
|
||||
"fields": "Fields",
|
||||
"recipients": "Recipients",
|
||||
@@ -379,6 +401,7 @@
|
||||
"certificate": "Certificate",
|
||||
"decline": "Decline",
|
||||
"finish": "Finish",
|
||||
"done": "Done",
|
||||
"mail": "Mail",
|
||||
"sign-now": "Sign now",
|
||||
"successfully-signed": "Successfully signed!",
|
||||
@@ -388,6 +411,10 @@
|
||||
"Email-verified-alert-1": "Email is verified.",
|
||||
"Email-verified-alert-2": "Email is already verified.",
|
||||
"upload-stamp-image": "Upload stamp image",
|
||||
"draw-signature": "Draw signature",
|
||||
"draw-initials": "Draw initials",
|
||||
"enter-text": "Enter text",
|
||||
"enter-widgettype": "Enter {{widgetType}}",
|
||||
"draw": "Draw",
|
||||
"type": "Type",
|
||||
"color-type": {
|
||||
@@ -408,6 +435,7 @@
|
||||
"reset-password-alert-3": "Reset Your Password",
|
||||
"faild-animation": "Failed to load animation",
|
||||
"apply": "Apply",
|
||||
"select-columns": "Select columns",
|
||||
"copy-type": {
|
||||
"All pages": "All pages",
|
||||
"All pages but last": "All pages but last",
|
||||
@@ -419,6 +447,19 @@
|
||||
"maximum-check": "Maximum check",
|
||||
"cell-count": "Cell count",
|
||||
"default-value": "Default value",
|
||||
"formula": "Formula",
|
||||
"formula-placeholder": "Build a formula using number widgets",
|
||||
"formula-no-widgets": "No other number widgets are assigned to this signer yet.",
|
||||
"supported-operators": "Supported operators:",
|
||||
"formula-helper": "Click a number widget chip to insert it (e.g. {{number-1}} + {{number-2}}).",
|
||||
"formula-error-letter": "Wrap widget names in double curly braces (e.g. {{number-1}}).",
|
||||
"formula-error-characters": "Only numbers and +, -, *, /, (, ) are allowed outside of braces.",
|
||||
"formula-error-braces": "Each opening {{ must have a matching }}.",
|
||||
"formula-error-operators": "Add at least two values when using operators or parentheses.",
|
||||
"formula-error-leading-operator": "Formulas cannot start with operators.",
|
||||
"decimal-places": "Decimal Places",
|
||||
"number-of-decimal-places": "Number of decimal places",
|
||||
"decimal-places-helper": "Set to 0 for whole numbers",
|
||||
"select": "Select",
|
||||
"read-only": "read only",
|
||||
"hide-labels": "Hide labels",
|
||||
@@ -465,13 +506,13 @@
|
||||
"add-recipients": "Add recipients",
|
||||
"loading-mssg": "This might take some time",
|
||||
"send-mail": "Send Mail",
|
||||
"signature-field-widget": "A minimum of one signature field must be added for each user. You have not added signature fields for {{signersName}}",
|
||||
"signature-field-widget": "Each user must have at least one signature widget. You have not added a signature widget for {{signersName}}",
|
||||
"placeholder-alert-1": "Please ensure there's at least one signature widget added for all recipients.",
|
||||
"placeholder-alert-2": "Please confirm that you have filled the text field.",
|
||||
"placeholder-alert-3": " Are you sure you want to send out this document for signatures?",
|
||||
"placeholder-alert-4": "You have successfully sent mails to all recipients!",
|
||||
"placeholder-mail-alert": "You have successfully sent email to {{name}}. Subsequent signers will get email(s) once {{name}} signs the document",
|
||||
"placeholder-mail-alert-you": "Subsequent signers will get email(s) once you signs the document.",
|
||||
"placeholder-mail-alert-you": "Subsequent signers will get email(s) once you sign the document.",
|
||||
"placeholder-alert-5": "Do you want to sign the document right now?",
|
||||
"placeholder-alert-6": "Please setup mail adapter to send mail!",
|
||||
"placeholder-alert-7": "Please select signer for add placeholder!",
|
||||
@@ -600,7 +641,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "You have logged in successfully! Let's take a look.",
|
||||
"home-layout-2": "To upload documents for self-signing or to request others' signatures, simply select the respective buttons.",
|
||||
"home-layout-3": "You are ready to start using {{appName}}! If you need support feel free to contact us.",
|
||||
"home-layout-3": "You are ready to start using {{appName}}!",
|
||||
"home-layout-4": "Star us on",
|
||||
"generate-token": "Upgrade now to generate production API token.",
|
||||
"opensign-drive-1": "Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.",
|
||||
"opensign-drive-2": "Click the add button to create a new folder or document.",
|
||||
@@ -609,35 +651,33 @@
|
||||
"opensign-drive-5": "The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.",
|
||||
"opensign-drive-6": "Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.",
|
||||
"opensign-drive-7": "Right-click on any folder to see options. Choose 'Rename' to change the folder's name or click on the folder to navigate through its contents.",
|
||||
"pdf-request-file-1": "List of signers who still need to sign the document .",
|
||||
"pdf-request-file-2": "Click any of the placeholders appearing on the document to sign. You will then see options to draw your signature, type it, or upload an image .",
|
||||
"pdf-request-file-3": "Click Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .",
|
||||
"pdf-request-file-1": "List of signers who still need to sign the document.",
|
||||
"pdf-request-file-2": "Click any of the fields appearing on the document to begin. You will then be able to fill in the required information.",
|
||||
"pdf-request-file-3": "Once you've filled in all the required fields, click “Finish.” You'll then be able to download the signed document. Depending on the sender's settings, you may receive a copy of the completed document along with the completion certificate once all recipients have finished signing.",
|
||||
"pdf-request-file-4": "List of signers who have already signed the document .",
|
||||
"pdf-request-file-5": "You can click 'Auto Sign All' to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .",
|
||||
"pdf-request-file-6": "Please complete the fields on page number {{pagenumbers}}, all highlighted in the same color for easy identification.",
|
||||
"placeholder-sign-1": "Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.",
|
||||
"placeholder-sign-1": "Select a recipient from this list to add a widgets. The widget will appear in the same colour as the recipient name once you drop it on the document.",
|
||||
"placeholder-sign-2": "Clicking 'Add recipients' button will allow you to add more signers.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-4": "Drag or click a widget to add it to the document.",
|
||||
"placeholder-sign-5": "The PDF content area already displays the template's existing Widgets. For your convenience, these widgets will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-6": "Clicking 'Next' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like 'HR Manager' and 'New Employee'. Each time you use the template, you can assign the 'New Employee' role to different incoming staff members, while the 'HR Manager' role remains constant, facilitating a seamless onboarding process for each recruit. ",
|
||||
"redirect": "Click the 'Use' button to create a new document from an existing template.",
|
||||
"bulksend": "To quickly send multiple documents using an existing template by just creating the recipient email addresses, click the 'Bulk Send' button.",
|
||||
"option": "This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won't affect existing documents.Use the Delete button you can delete template. ",
|
||||
"signyour-self-1": "Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.",
|
||||
"bulksend": "To quickly send multiple documents using an existing template by just entering the recipient email addresses, click the 'Bulk Send' button. You can send to up to 50 recipients.",
|
||||
"option": "This menu reveals more options such as Edit, Delete, Rename, Duplicate, Share, etc. <1>Click here</1> to read more about all available options. <3>Note: Changes to an existing template will apply to all future documents created from that template but won't affect documents that are already sent out.</3>",
|
||||
"signyour-self-1": "Select or drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each widget to tailor the document to your needs.",
|
||||
"signyour-self-2": "Drag and drop anywhere in this area. You can resize and move it later.",
|
||||
"template-placeholder-1": "Clicking 'Add role' button will allow you to add various signer roles. You can attach users to each role in subsequent steps.",
|
||||
"template-placeholder-2": "Once roles are added, select a role from list to add a place-holder where he is supposed to sign. The placeholder will appear in the same colour as the role name once you drop it on the document.",
|
||||
"template-placeholder-3": "Drag or click on a field to add it to the document.",
|
||||
"template-placeholder-4": "Drag the placeholder for a role anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.",
|
||||
"template-placeholder-5": "Clicking 'Next' will store the current template. After saving, you'll be prompted to create a new document from this template if you wish.",
|
||||
"template-placeholder-2": "After adding roles, choose one from the list to place a widget for that recipient. You can either click the widget or drag it onto the document. Once placed, the widget will display in the same color as the selected role.",
|
||||
"template-placeholder-3": "Clicking 'Next' will store the current template. After saving, you'll be prompted to create a new document from this template if you wish.",
|
||||
"webhook-1": "Upgrade now to set webhook",
|
||||
"Need your Signature": "Clicking on this card will take you to the list of documents awaiting your review.",
|
||||
"Out for signatures": "Clicking on this card will take you to a list of documents awaiting signature.",
|
||||
"Recent signature requests": "This is a list of documents that are waiting for your signature.",
|
||||
"Recently sent for signatures": "This is a list of documents you've sent to other parties for signature.",
|
||||
"Drafts": "This are documents you have started but have not finalized for sending.",
|
||||
"Drafts": "These are documents you have started but have not finalized for sending.",
|
||||
"public-template": "This video demonstrates how to set up your personalized public profile, such as 'https://opensign.me/your-username'. You'll also learn how to customize your tagline and make your templates available for public signing.",
|
||||
"allowModify-widgets": "You can drag and drop any of these fields onto the document, in addition to the fields already designated for you by the document creator."
|
||||
},
|
||||
@@ -728,7 +768,7 @@
|
||||
"public-tour-message": "The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it.",
|
||||
"pdf-uncompatible": "This pdf is not compatible, please contact {{appName}}",
|
||||
"text-field-tour": "Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
|
||||
"text-field-tour": "'Prefill' fields must be filled in advance before the document is sent. If you need the signers to provide input, use signers fields instead.",
|
||||
"attach-signer-tour": "You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
|
||||
"allowed-signature-types": "Allowed signature types",
|
||||
"at-least-one-signature-type": "At least one signature type should be enabled.",
|
||||
@@ -776,7 +816,7 @@
|
||||
"agree-p1": "I confirm that I have read and understood the ",
|
||||
"agree-p2": "Electronic Record and Signature Disclosure",
|
||||
"agree-p3": "and consent to use electronic records and signatures.",
|
||||
"agrre-button": " Agree & Continue",
|
||||
"agrre-button": "I confirm & agree to continue",
|
||||
"term-cond-title": "Terms and conditions",
|
||||
"term-cond-h": "ELECTRONIC RECORD AND SIGNATURE DISCLOSURE",
|
||||
"term-cond-p1": "This Electronic Record and Signature Disclosure ('Disclosure') is an agreement between the Document Creator ('Sender') and the Signer ('You'), facilitated through the {{appName}} platform ('Platform'). By signing documents via {{appName}}, you agree to the terms outlined in this Disclosure. Please read it carefully before proceeding.",
|
||||
@@ -879,6 +919,10 @@
|
||||
"thanks-for-feedback": "Thanks for your feedback 🙏",
|
||||
"share-your-feedback": "Share your feedback",
|
||||
"share-your-review": "Share your review",
|
||||
"please-select-rating": "Please select a rating",
|
||||
"feedback-optional": "Feedback (Optional)",
|
||||
"feedback-saved": "Feedback has been saved.",
|
||||
"feedback-save-error": "Unable to save feedback, please try again.",
|
||||
"date-format": "Date format",
|
||||
"document-deleted": "The document has been deleted or you don't have access. Please contact the sender.",
|
||||
"save-as-template-?": "Are you sure you want to save this document as template?",
|
||||
@@ -976,20 +1020,20 @@
|
||||
"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."
|
||||
"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",
|
||||
"verify-document": "Verify document",
|
||||
"finish-mssg": " Are you sure you want to finish the document ?",
|
||||
"review": "Review",
|
||||
"next-field": "Next Field",
|
||||
"required-mssg": "{{leftRequiredWidget}} of {{totalWidget}} fields left",
|
||||
"verify-document": "Verify document",
|
||||
"verify-document-signature": "Verify Document Signature",
|
||||
"select-pdf-document": "Select PDF Document",
|
||||
"selected-file": "Selected file",
|
||||
@@ -1032,14 +1076,204 @@
|
||||
"not-calculated": "Not calculated",
|
||||
"not-found-in-signature": "Not found in signature",
|
||||
"readonly-error": "Read-only {{widgetName}} widget must have a default value or you can make it optional.",
|
||||
"choose-one":"Choose One",
|
||||
"choose-one": "Choose One",
|
||||
"search-templates": "Search templates…",
|
||||
"search-documents": "Search documents…",
|
||||
"search-contacts": "Search contacts…",
|
||||
"add-role-alert": "Please add at least one role",
|
||||
"edit-draft": "Edit draft",
|
||||
"invalid-email-found": "Invalid email found: {{email}}",
|
||||
"duplicate-email-found": "Duplicate email found: {{email}}",
|
||||
"vertical": "Vertical",
|
||||
"horizontal": "Horizontal",
|
||||
"billing": "Billing",
|
||||
"console": "Console"
|
||||
"console": "Console",
|
||||
"prefill-widget": "Prefill Widgets",
|
||||
"action-prohibited": "This action isn't allowed for your email domain. Please contact your administrator for assistance.",
|
||||
"must-have-at-least-one-vacant-role": "You must have at least one role unassigned before setting a template to 'public'.",
|
||||
"remove-duplicate": "Please remove duplicate option",
|
||||
"prefill-bulk-error": "Bulk send is not allowed when prefill widgets are added. Please remove the prefill widgets to proceed.",
|
||||
"session-expired-title": "Session Expired",
|
||||
"access-denied": "Access denied",
|
||||
"upgrade": "Upgrade",
|
||||
"do-not-access-app": "You don't have access to this application.",
|
||||
"dont-have-access": "You don't have access.",
|
||||
"valid-email-alert": "Please enter a valid email address.",
|
||||
"otp-not-validate": "OTP is not valid.",
|
||||
"domain-not-allowed": "This domain is not allowed",
|
||||
"atleast-one-recipient-alert": "Please add at least one recipient!",
|
||||
"incorrect-password-or-decryption-failed": "Incorrect password or decryption failed.",
|
||||
"incorrect-password-for-file": "Incorrect password for file: {{file}}",
|
||||
"error-uploading-pdf": "Error while uploading PDF.",
|
||||
"provide-password": "Please provide password.",
|
||||
"only-pdf-allowed": "Only PDF files are allowed.",
|
||||
"invalid-username-password-region": "Invalid username/password or region.",
|
||||
"pfx-extension-alert": "Please upload a file with a .pfx extension.",
|
||||
"email-already-exist": "Email already exists",
|
||||
"branding": "Branding",
|
||||
"branding-help": "Branding provides white labelling to your app",
|
||||
"custom-sub-domain": "Custom sub-domain",
|
||||
"app-name": "App Name",
|
||||
"provide-domain-name": "provide your domain name",
|
||||
"provide-app-name": "provide your app name",
|
||||
"logo": "Logo",
|
||||
"upload-app-logo": "upload your app logo",
|
||||
"prefill-unfilled-widget": "The following required field(s) cannot be left empty: {{emptyWidget}} Please fill them out to proceed.",
|
||||
"Dashboard": "Dashboard",
|
||||
"Analytics": "Analytics",
|
||||
"Templates": "Templates",
|
||||
"Need your sign": "Need your sign",
|
||||
"In Progress": "In progress",
|
||||
"Completed": "Completed",
|
||||
"Drafts": "Drafts",
|
||||
"Declined": "Declined",
|
||||
"Expired": "Expired",
|
||||
"Contactbook": "Contactbook",
|
||||
"My Signature": "My signature",
|
||||
"API Token": "API token",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferences",
|
||||
"Teams": "Teams",
|
||||
"Users": "Users",
|
||||
"Drive": "Drive",
|
||||
"Branding": "Branding",
|
||||
"Mail": "Mail",
|
||||
"Storage": "Storage",
|
||||
"Signing certificate": "Signing certificate",
|
||||
"General": "General",
|
||||
"Organizations": "Organizations",
|
||||
"OrgAdmins": "OrgAdmins",
|
||||
"Debug Pdf": "Debug Pdf",
|
||||
"New Document": "New Document",
|
||||
"subscription": "subscription",
|
||||
"Draft document": "Draft document",
|
||||
"Draft template": "Draft template",
|
||||
"Public sign": "Public sign",
|
||||
"Signup": "Signup",
|
||||
"delete-contact": "Delete Contact",
|
||||
"total-records-found": "Total records found: {{count}}",
|
||||
"Invalid-records-found": "Invalid records found: {{records}}",
|
||||
"previous": "Previous",
|
||||
"page-n-of-n": "Page {{currentPage}} of {{totalPages}}",
|
||||
"import": "Import",
|
||||
"search": "Search",
|
||||
"viewed-on": "Viewed on: {{ViewedOn}}",
|
||||
"signed-on": "Signed on: {{SignedOn}}",
|
||||
"hide": "Hide",
|
||||
"show-more": "Show More",
|
||||
"browse-or-drag-to-replace-existing-file": "Browse or drag & drop a new file to replace the existing one",
|
||||
"optional-details": "Optional details",
|
||||
"hide-optional-details": "Hide optional details",
|
||||
"mail-adapter-subscription-alert": "Please upgrade to Professional or Team plan to setup custom SMTP.",
|
||||
"connect-to-mail": "Connect to Gmail",
|
||||
"custom-smtp": "Custom SMTP",
|
||||
"default-smtp": "{{appName}} default SMTP",
|
||||
"host": "Host",
|
||||
"port": "Port",
|
||||
"sender-email": "Sender Email",
|
||||
"username": "Username",
|
||||
"use-default-mail-adapter": "Are you sure you want to use {{appName}}'s default mail servers to send your signature request emails? We recommend using your own Gmail or SMTP servers for improved inbox deliverability.",
|
||||
"verification-code-sent-registered-email": "A verification code has been sent to your registered email <1>{{useremail}}</1> confirm your settings. Please enter the code below.",
|
||||
"smpt-credentials": "SMTP Credentials",
|
||||
"delete-account": "Delete account",
|
||||
"delete-account-que": "Are you sure you want to delete your account?",
|
||||
"delete-account-que-user": "You are about to permanently delete this user and all associated data. This action cannot be undone.",
|
||||
"user-deleted-successfully": "User and all associated data deleted successfully.",
|
||||
"account-deletion-request-sent-via-mail": "We've emailed you a confirmation link. Approve the request to complete your account deletion.",
|
||||
"type-exact-email-delete": "Type the exact email to enable delete",
|
||||
"email-does-not-match": "Email does not match.",
|
||||
"please-type-to-confirm": "Please type <1>{{userEmail}}</1> to confirm:",
|
||||
"email-settings": "Email settings",
|
||||
"email-settings-help": " To ensure better inbox deliverability for your signature request emails, you can connect your own email provider. Choose one of the following options:",
|
||||
"connect-to-gmail": "Connect to Gmail: ",
|
||||
"connect-to-gmail-help": "Use your Gmail account to send signature request emails directly from your own inbox, improving delivery rates and reliability.",
|
||||
"connect-to-smtp": "Custom SMTP: ",
|
||||
"connect-to-smtp-help": "Use your custom SMTP server for sending emails through your domain. This option gives you full control over your email infrastructure, enhancing deliverability and brand consistency.",
|
||||
"connect-to-default": "If you prefer, you can also Use {{appName}} default mail servers, though we recommend using your own for optimal results.",
|
||||
"email-settings-redirect-message": "This setting has been moved from the console to Main Menu Settings → Preferences. This page will be removed in upcoming versions.",
|
||||
"go-to-preferences-menu": "Go to Preferences menu",
|
||||
"document-download-filename-format": "Document download filename format",
|
||||
"preview": "Preview: ",
|
||||
"download-filename-format-help": "Choose how downloaded PDFs are named. Your selection is saved to your profile and used across the app.",
|
||||
"delete-action-prohibited": "This action is not permitted. Kindly contact your administrator to request account deletion.",
|
||||
"not-verified": "Not verified",
|
||||
"verified": "Verified",
|
||||
"expires": "Expires",
|
||||
"fix-resend-error": "You can't fix and resend this document because it contains prefill widgets.",
|
||||
"duplicate-template-error": "You can't duplicate this template because it contains prefill widgets.",
|
||||
"save-as-template-error": "This document cannot be saved as a template because it contains prefill widgets.",
|
||||
"redirecting-you-in": "Redirecting you in {{redirectTimeLeft}} sec...",
|
||||
"pdf-tools-tour": "Click these buttons to add, delete, rearrange, rotate and zoom pages.",
|
||||
"widgets": "Widgets",
|
||||
"prefill-tour": "Use this option to enter information in advance before sending the document to recipients.",
|
||||
"empty-prefill-error": "The following required field(s) cannot be left empty:",
|
||||
"please-fill-out": "Please fill them out to proceed.",
|
||||
"custom-signing-certificate": "Custom signing certificate",
|
||||
"signing-certificate-help": "You can upload your own document signing certificate which will be used to sign all your documents as well as completion certificates. The certificate file should be a P12 certificate in PFX format.",
|
||||
"certificate-file-p12-in-PFX-format": "Certificate file (p12 certificate in PFX format)",
|
||||
"password-of-pfx-file": "Enter password of pfx file",
|
||||
"update": "Update",
|
||||
"use-default-certificate": "Use default {{appName}} certificate",
|
||||
"upgrade-to-team-plan": "Upgrade to Team Plan",
|
||||
"setup-file-storage": "Setup file storage",
|
||||
"save-and-activate": "Save & activate",
|
||||
"logging-out-to-apply-settings": "You are being logged out to apply new settings",
|
||||
"reconnect-to-default": "Reconnect to {{appName}}",
|
||||
"active-file-adapter": "Active File Adapter",
|
||||
"file-adapter-unique-name": "File Adapter unique name",
|
||||
"unique-name-for-file-adapter": "Enter unique name for file adapter",
|
||||
"storage-provider": "Storage Provider",
|
||||
"enter-bucket-name": "Enter bucket name",
|
||||
"enter-space-name": "Enter space name",
|
||||
"enter-region-of-bucket": "Enter region of bucket",
|
||||
"enter-region-of-space": "Enter region of space",
|
||||
"enter-access-key": "Enter access key",
|
||||
"enter-secret-access-key": "Enter secret access key",
|
||||
"otp-email": "We've sent a verification code to",
|
||||
"save-as-temp-warn": "Note: This document includes prefilled widgets, which will be automatically removed as they are already incorporated into the base document.",
|
||||
"edit-document": "Edit document",
|
||||
"modify": "Modify",
|
||||
"merge-certificate-to-pdf": "Merge Certificate to PDF",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "This will ensure that the completion certificate is included in the final PDF document. However, please note that once merged, the certificate cannot be separated from the main document.",
|
||||
"p2": "If you choose not to merge, the completion certificate will be provided as a separate PDF file along with the signed document."
|
||||
},
|
||||
"read-only-date-error": "Read-only date widget must have a default date.",
|
||||
"set-date": "Set Date",
|
||||
"set-today": "Signing Date",
|
||||
"enter-name": "Enter name",
|
||||
"enter-email": "Enter email",
|
||||
"subscribe-to-opensign-msg": "Subscribe to {{appName}} and enjoy unlimited free digital signatures.",
|
||||
"duplicate-subscribe-msg": "Duplicate lets you create an exact copy of the selected template, allowing you to reuse or modify it without affecting the original.",
|
||||
"save-as-template-msg": "Save this document as a reusable template that you can use again for future documents.",
|
||||
"public-credit-alert": "Signing through this link consumes your free email credits. As a Free user, you are allotted 15 email credits per month to prevent spammers from abusing our systems. To enjoy higher limits and uninterrupted access, Subscribe to <1>OpenSign™ Paid plans.</1>",
|
||||
"know-more-about": "Know more about",
|
||||
"free-unlimited-signatures": "Free Unlimited Signatures",
|
||||
"email-quota-updated": "Daily email quota updated successfully.",
|
||||
"password-has-been-reset": "Password has been reset.",
|
||||
"Unauthorized to reset your own password.": "Unauthorized to reset your own password.",
|
||||
"User not found or not allowed.": "User not found or not allowed.",
|
||||
"User not found.": "User not found.",
|
||||
"Admin user tenant not found.": "Admin user tenant not found.",
|
||||
"User is not authenticated.": "User is not authenticated.",
|
||||
"Please provide required parameters.": "Please provide required parameters.",
|
||||
"reset-password": "Reset User Password",
|
||||
"enter-strong-password": "Enter a strong password or auto-generate one.",
|
||||
"autogenerate": "Autogenerate",
|
||||
"extend": "Extend",
|
||||
"console-general-help": "To let individual users customize their own email templates, navigate to",
|
||||
"console-mail-help": "To update company-wide email templates, go to",
|
||||
"admin-setting-disabled": "This setting is disabled. Please contact admin for assistance.",
|
||||
"company-email-settings": "Company email settings",
|
||||
"allow-email-template-individually": "Allow email template customization for users individually ",
|
||||
"user-email-template-general-help": "Disabling this will prevent users from customizing their email templates.",
|
||||
"disable-individual-user-smtp": "Disable individual user smtp settings",
|
||||
"enable-individual-user-smtp": "Enable individual user smtp settings",
|
||||
"user-smtp-general-help": "When disabled, users will not be able to configure their own SMTP settings. All emails will use the global SMTP configuration instead.",
|
||||
"global-smtp-settings": "Global SMTP Settings",
|
||||
"global-smtp-settings-help-p1": "These settings apply globally across your entire {{appName}} account. If an individual user has not configured their own email delivery settings, the system will automatically use the Global SMTP Settings defined here.",
|
||||
"global-smtp-settings-help-p2": "To ensure better inbox deliverability for all signature request emails, you can connect your preferred email provider. Choose one of the following options:",
|
||||
"global-connect-to-gmail-help": "Use a Gmail account to send signature request emails directly from your organization's inbox. This improves delivery rates, reputation, and overall reliability.",
|
||||
"global-connect-to-smtp-help": "Use your own SMTP server to send emails through your domain. This provides full control over your email infrastructure, enhances deliverability, and maintains consistent branding.",
|
||||
"global-smtp-settings-help-p3": "If no individual user-level settings are configured, these global settings will be applied automatically."
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"dark-mode": "Modo oscuro",
|
||||
"name": "Nombre",
|
||||
"phone": "Teléfono",
|
||||
"phone-optional": "opcional",
|
||||
"phone-optional": "Opcional",
|
||||
"email": "Correo",
|
||||
"company": "Compañía",
|
||||
"job-title": "Título del puesto",
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "Nombre",
|
||||
"Date": "Fecha"
|
||||
},
|
||||
"folder": "Carpeta",
|
||||
"pdf": "Pdf",
|
||||
"context-menu": {
|
||||
"Download": "Descargar",
|
||||
"Rename": "Renombrar",
|
||||
@@ -44,6 +46,10 @@
|
||||
"contact-now": "Contactar ahora",
|
||||
"upgrade-to": "Mejorar a",
|
||||
"plan": "Plan",
|
||||
"connect": "Conectar",
|
||||
"connect-to-g-drive": "Conectar con Google Drive",
|
||||
"reconnect-to-g-drive": "Volver a conectar con Google Drive",
|
||||
"gdrive-info-connect": "Cuando Google Drive está conectado, el documento completado se guardará en la carpeta {{appName}} de Google Drive.",
|
||||
"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-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.",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "¡Bienvenido de nuevo!",
|
||||
"Login-to-your-account": "Ingresa a tu cuenta",
|
||||
"password": "Contraseña",
|
||||
"forgot-password": "¿Olvidaste la contraseña?",
|
||||
"forgot-password": "¿Olvidaste la contraseña",
|
||||
"loading": "Cargando...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Ingresar con SSO",
|
||||
"login-page": "Página de inicio de sesión",
|
||||
"or": "O",
|
||||
"signup-page": "Página de registro",
|
||||
"password-match-length": "La nueva contraseña y la confirmación deben coincidir",
|
||||
"password-length": "La contraseña debe tener 8 caracteres",
|
||||
"password-case": "La contraseña debe incluir mayúsculas, minúsculas y números",
|
||||
"password-special-char": " La contraseña debe incluir caracteres especiales",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "SMTP Global"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necesitan tu firma",
|
||||
@@ -159,7 +167,8 @@
|
||||
"daily-mail-quota": "Cuota diaria de correos electrónicos",
|
||||
"Save as template": "Guardar como plantilla",
|
||||
"Fix & resend": "Corregir y reenviar",
|
||||
"Kiosk Mode": "Modo Kiosco"
|
||||
"Kiosk Mode": "Modo Kiosco",
|
||||
"Reset password": "Restablecer contraseña"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
@@ -181,7 +190,20 @@
|
||||
"created-date": "Fecha de creación",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Registros",
|
||||
"Expiry-date": "Date d'expiration"
|
||||
"Expiry-date": "Date d'expiration",
|
||||
"Company": "Compañía",
|
||||
"JobTitle": "Título del puesto",
|
||||
"Time to complete (Days)": "Tiempo para completar (días)",
|
||||
"Auto reminder": "Recordatorio automático",
|
||||
"Remind once in every (Days)": "Recordar una vez cada (días)",
|
||||
"Enable OTP verification": "Habilitar verificación OTP",
|
||||
"Enable Tour": "Habilitar recorrido",
|
||||
"Notify on signatures": "Notificar en las firmas",
|
||||
"Allow modifications": "Permitir modificaciones",
|
||||
"Redirect url": "URL de redirección",
|
||||
"Created Date": "Fecha de creación",
|
||||
"Updated Date": "Fecha de actualización",
|
||||
"Expiry Date": "Fecha de vencimiento"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
|
||||
@@ -193,16 +215,15 @@
|
||||
"Contactbook": "Esta es una lista de contactos/firmantes añadidos por ti. Aparecerán como sugerencias cuando intentes añadir firmantes a un nuevo documento.",
|
||||
"Templates": "Esta es una lista de plantillas que están a tu disposición para crear documentos. Puedes hacer clic en el botón «usar» para crear un nuevo documento utilizando una plantilla, modifica el documento y añade firmantes en el siguiente paso."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla"
|
||||
},
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "archivo seleccionado",
|
||||
"template-title": "Título de la plantilla",
|
||||
"document-title": "Título del documento",
|
||||
"title": "Título",
|
||||
"description": "Descripción",
|
||||
"time-to-complete": "Plazo de finalización (días)",
|
||||
"send-in-order": "Enviar en orden",
|
||||
@@ -242,7 +263,6 @@
|
||||
"submit": "Enviar",
|
||||
"reset": "Volver a empezar",
|
||||
"my-signature": "Mi firma",
|
||||
"initial-alert": "Mi inicial no encontrada",
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
@@ -281,7 +301,7 @@
|
||||
"deactivate": "desactivar",
|
||||
"this-user": " este usuario",
|
||||
"delete-user": "Eliminar usuario",
|
||||
"delete": "eliminar",
|
||||
"delete": "Eliminar",
|
||||
"add-user": "Agregar usuario",
|
||||
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
|
||||
"Team status": "Estado del equipo",
|
||||
@@ -368,7 +388,8 @@
|
||||
"dropdown": "desplegable",
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo"
|
||||
"email": "correo",
|
||||
"number": "número"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
@@ -380,6 +401,7 @@
|
||||
"certificate": "Certificado",
|
||||
"decline": "Rechazar",
|
||||
"finish": "Finalizar",
|
||||
"done": "Hecho",
|
||||
"mail": "Correo",
|
||||
"sign-now": "Firmar ahora",
|
||||
"successfully-signed": "¡Firmado exitosamente!",
|
||||
@@ -389,6 +411,10 @@
|
||||
"Email-verified-alert-1": "El correo está verificado.",
|
||||
"Email-verified-alert-2": "El correo ya ha sido verificado.",
|
||||
"upload-stamp-image": "Subir imagen del sello",
|
||||
"draw-signature": "Dibujar firma",
|
||||
"draw-initials": "Dibujar iniciales",
|
||||
"enter-text": "Introducir texto",
|
||||
"enter-widgettype": "Introduzca {{widgetType}}",
|
||||
"draw": "Dibujar",
|
||||
"type": "Escribir",
|
||||
"color-type": {
|
||||
@@ -401,7 +427,7 @@
|
||||
"upload": "Subir",
|
||||
"initial-teb": "Iniciales",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "Tu firma",
|
||||
"initial-alert": "Mi inicial no encontrada",
|
||||
"copy-title": "Copiar widget a",
|
||||
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?",
|
||||
"reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
|
||||
@@ -409,6 +435,7 @@
|
||||
"reset-password-alert-3": "Restablece tu contraseña",
|
||||
"faild-animation": "Error al cargar la animación",
|
||||
"apply": "Aplicar",
|
||||
"select-columns": "Seleccionar columnas",
|
||||
"copy-type": {
|
||||
"All pages": "Todas las páginas",
|
||||
"All pages but last": "Todas las páginas menos la última",
|
||||
@@ -420,6 +447,19 @@
|
||||
"maximum-check": "Chequeo máximo",
|
||||
"cell-count": "recuento de células",
|
||||
"default-value": "Valor por defecto",
|
||||
"formula": "Fórmula",
|
||||
"formula-placeholder": "Crea una fórmula utilizando widgets numéricos",
|
||||
"formula-no-widgets": "Todavía no hay otros widgets numéricos asignados a este firmante.",
|
||||
"supported-operators": "Operadores compatibles:",
|
||||
"formula-helper": "Haz clic en una ficha de widget numérico para insertarla (p. ej. {{number-1}} + {{number-2}}).",
|
||||
"formula-error-letter": "Envuelve los nombres de los widgets entre dobles llaves (p. ej. {{number-1}}).",
|
||||
"formula-error-characters": "Fuera de las llaves solo se permiten números y +, -, *, /, (, ).",
|
||||
"formula-error-braces": "Cada {{ de apertura debe tener una }} correspondiente.",
|
||||
"formula-error-operators": "Cuando uses operadores o paréntesis, agrega al menos dos valores.",
|
||||
"formula-error-leading-operator": "Las fórmulas no pueden comenzar con operadores.",
|
||||
"decimal-places": "Decimales",
|
||||
"number-of-decimal-places": "Número de decimales",
|
||||
"decimal-places-helper": "Establecer en 0 para números enteros",
|
||||
"select": "Seleccionar",
|
||||
"read-only": "Es de solo lectura",
|
||||
"hide-labels": "Esconder etiquetas",
|
||||
@@ -466,12 +506,13 @@
|
||||
"add-recipients": "Agregar destinatarios",
|
||||
"loading-mssg": "Esto podría llevar algún tiempo",
|
||||
"send-mail": "Enviar correo",
|
||||
"signature-field-widget": "Cada usuario debe tener al menos un widget de firma. No ha añadido un widget de firma para {{signersName}}.",
|
||||
"placeholder-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma para cada destinatario.",
|
||||
"placeholder-alert-2": "Por favor, confirma que hayas rellenado el campo de texto.",
|
||||
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
|
||||
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
|
||||
"placeholder-mail-alert": "Has enviado un correo electrónico con éxito a {{name}}. Los siguientes firmantes recibirán un correo electrónico una vez que {{name}} firme el documento.",
|
||||
"placeholder-mail-alert-you": "Los firmantes posteriores recibirán correos electrónicos una vez que firme el documento.",
|
||||
"placeholder-mail-alert-you": "Los firmantes siguientes recibirán un correo electrónico una vez que usted firme el documento.",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
|
||||
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
@@ -600,7 +641,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.",
|
||||
"home-layout-2": "Para subir documentos para firmar tú mismo o para solicitar la firma de otros, simplemente selecciona los botones correspondientes.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar {{appName}}! Si necesitas soporte siéntete libre de contactarnos.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar {{appName}}!",
|
||||
"home-layout-4": "Danos una estrella en",
|
||||
"generate-token": "Mejora ahora para generar un token API de producción.",
|
||||
"opensign-drive-1": "Haz clic en los enlaces para navegar fácilmente por la estructura jerárquica de carpetas y ver los documentos de cada carpeta.",
|
||||
"opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
|
||||
@@ -610,34 +652,32 @@
|
||||
"opensign-drive-6": "Haz clic con el botón derecho en un documento para ver opciones como «Descargar», «Renombrar», «Mover» y «Eliminar». Haz clic en el documento para abrirlo.",
|
||||
"opensign-drive-7": "Haz clic con el botón derecho en cualquier carpeta para ver las opciones. Elige «Renombrar» para cambiar el nombre de la carpeta o haz clic en ella para navegar por su contenido.",
|
||||
"pdf-request-file-1": "Lista de firmantes que todavía tienen que firmar este documento.",
|
||||
"pdf-request-file-2": "Haz clic en cualquiera de los marcadores de posición que aparecen en el documento para firmar. A continuación, verás opciones para dibujar tu firma, escribirla o subir una imagen .",
|
||||
"pdf-request-file-3": "Haga clic en los botones Rechazar o Finalizar para navegar por el documento. Utilice el menú de puntos suspensivos para ver opciones adicionales, incluido el botón Descargar.",
|
||||
"pdf-request-file-2": "Haga clic en cualquiera de los campos que aparecen en el documento para comenzar. Luego podrá completar la información requerida.",
|
||||
"pdf-request-file-3": "Una vez que haya completado todos los campos obligatorios, haga clic en 'Finalizar'. Luego podrá descargar el documento firmado. Según la configuración del remitente, es posible que reciba una copia del documento completado junto con el certificado de finalización una vez que todos los destinatarios hayan firmado.",
|
||||
"pdf-request-file-4": "Lista de firmantes que ya han firmado el documento.",
|
||||
"pdf-request-file-5": "Puedes hacer clic en «Firmar todo automáticamente» para firmar de manera automática en todos los lugares que deban ser firmados por ti. Asegúrate de revisar correctamente el documento antes de hacer clic en ese botón.",
|
||||
"pdf-request-file-6": "Por favor, complete los campos en la página {{pagenumbers}}, todos resaltados con el mismo color para facilitar la identificación.",
|
||||
"placeholder-sign-1": "Selecciona un destinatario de esta lista para agregar un marcador de posición dónde este destinatario tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del destinatario una vez lo sueltes en el documento.",
|
||||
"placeholder-sign-1": "Seleccione un destinatario de esta lista para añadir un widget. El widget aparecerá con el mismo color que el nombre del destinatario una vez que lo coloque en el documento.",
|
||||
"placeholder-sign-2": "Hacer clic en el botón «Agregar destinatarios» te permitirá añadir más firmantes.",
|
||||
"placeholder-sign-3": "Haz clic en este botón para agregar más destinatarios/firmantes al documento.",
|
||||
"placeholder-sign-4": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los marcadores de posición existentes en la plantilla. Para tu comodidad, estos marcadores de posición coincidirán con el color del nombre del destinatario, haciéndolos fácilmente identificables.",
|
||||
"placeholder-sign-4": "Arrastre o haga clic en un widget para añadirlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los widgets existentes de la plantilla. Para su comodidad, estos widgets coincidirán con el color del nombre del destinatario, lo que los hace fácilmente identificables.",
|
||||
"placeholder-sign-6": "Al hacer clic en 'Siguiente' se guardará el documento. En el siguiente paso, puede personalizar los correos electrónicos que se enviarán a los destinatarios o copiar los enlaces de firma y compartirlos usted mismo con los destinatarios.",
|
||||
"report-1": "Haz clic en el botón «Agregar» para crear una nueva plantilla. Las plantillas son documentos reusables diseñados para generar rápidamente nuevos documentos con la misma estructura pero diferentes firmantes. Por ejemplo, una plantilla de RR.HH. para el proceso de incorporación podría tener roles predefinidos como «Director de RR.HH.» y «Nuevo empleado». Cada vez que utilices la plantilla, puedes asignar el rol de «Nuevo empleado» a los distintos miembros del personal que se incorporen, mientras que el rol de « Director de RR.HH.» permanece constante, lo que facilita un proceso de incorporación fluido para cada persona contratada.",
|
||||
"redirect": "Haz clic en el botón «Usar» para crear un nuevo documento desde una plantilla existente.",
|
||||
"bulksend": "Para enviar rápidamente múltiples documentos utilizando una plantilla existente creando simplemente las direcciones de correo electrónico de los destinatarios, haz clic en el botón 'Envío Masivo'.",
|
||||
"option": "Este menú revela más opciones como «Editar» y «Eliminar». Usa el botón «Editar» para agregar roles, modificar campos y actualizar tu plantilla. Los cambios aplicarán para todos los documentos futuros que sean creados usando esta plantilla pero no afectarán a los documentos ya existentes. Con el botón «Eliminar» puedes eliminar la plantilla.",
|
||||
"signyour-self-1": "Para personalizar el documento antes de que sea firmado, selecciona y arrastra al documento los widgets que prefieras. Escoge el espacio perfecto para cada modificación para adaptar el documento a tus necesidades.",
|
||||
"bulksend": "Para enviar rápidamente varios documentos utilizando una plantilla existente, simplemente introduzca las direcciones de correo electrónico de los destinatarios y haga clic en el botón 'Envío masivo'. Puede enviar hasta 50 destinatarios.",
|
||||
"option": "Este menú muestra más opciones como Editar, Eliminar, Renombrar, Duplicar, Compartir, etc. <1>Haga clic aquí</1> para leer más sobre todas las opciones disponibles. <3>Nota: Los cambios en una plantilla existente se aplicarán a todos los futuros documentos creados a partir de esa plantilla, pero no afectarán a los documentos que ya se hayan enviado.</3>",
|
||||
"signyour-self-1": "Seleccione o arrastre sus widgets preferidos al PDF para personalizar su documento antes de firmar. Elija los lugares perfectos para cada widget y adapte el documento a sus necesidades.",
|
||||
"signyour-self-2": "Arrastra y suelta en cualquier lugar de esta área. Después podrás cambiar el tamaño y moverlo.",
|
||||
"template-placeholder-1": "Hacer clic en el botón «Agregar rol» te permitirá agregar varios roles de firmantes. Puedes asociar usuarios a cada rol en los siguientes pasos.",
|
||||
"template-placeholder-2": "Una vez que los roles hayan sido agregados, selecciona un rol de la lista para agregar un marcador de posición donde éste tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del rol una vez lo sueltes en el documento.",
|
||||
"template-placeholder-3": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"template-placeholder-4": "Arrastra el marcador de posición para un rol a cualquier lugar del documento. Recuerda, aparecerá con el mismo color que el nombre del rol para poder referenciarlo fácilmente.",
|
||||
"template-placeholder-5": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"template-placeholder-2": "Después de añadir roles, elija uno de la lista para colocar un widget para ese destinatario. Puede hacer clic en el widget o arrastrarlo al documento. Una vez colocado, el widget se mostrará en el mismo color que el rol seleccionado.",
|
||||
"template-placeholder-3": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"webhook-1": "Mejora ahora para configurar un webhook",
|
||||
"Need your Signature": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan por tu revisión.",
|
||||
"Out for signatures": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan tu firma.",
|
||||
"Recent signature requests": "Esta es una lista de documentos que esperan por tu firma.",
|
||||
"Recently sent for signatures": "Esta es una lista de documentos que has enviado a otras partes para que sea firmado.",
|
||||
"Drafts": "Estos son documentos que has empezado pero que todavía no finalizas para su envío.",
|
||||
"Drafts": "Estos son documentos que ha comenzado pero que aún no ha finalizado para enviar.",
|
||||
"public-template": "Este video muestra cómo puedes configurar tu perfil público personalizado, como «https://opensign.me/tu-nombre-de-usuario». También aprenderás cómo personalizar tu eslogan y cómo hacer tus plantillas disponibles para su firmado público.",
|
||||
"allowModify-widgets": "Puede arrastrar y soltar cualquiera de estos campos en el documento, además de los campos ya designados para usted por el creador del documento"
|
||||
},
|
||||
@@ -728,7 +768,7 @@
|
||||
},
|
||||
"form-title-1": "Configuración del flujo de documentos",
|
||||
"form-title-2": "Configuración de seguridad",
|
||||
"text-field-tour": "Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
|
||||
"text-field-tour": "Los campos de 'Rellenado previo' deben completarse antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice los campos de firmantes.",
|
||||
"attach-signer-tour": "Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
|
||||
"allowed-signature-types": "Tipos de firma permitidos",
|
||||
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
|
||||
@@ -776,7 +816,7 @@
|
||||
"agree-p1": "Confirmo que he leído y comprendido el ",
|
||||
"agree-p2": "Registro Electrónico y Divulgación de Firma",
|
||||
"agree-p3": "y consentimiento para utilizar registros y firmas electrónicas.",
|
||||
"agrre-button": "Aceptar y continuar",
|
||||
"agrre-button": "Confirmo y acepto continuar",
|
||||
"term-cond-title": "Términos y condiciones",
|
||||
"term-cond-h": "DIVULGACIÓN DE REGISTRO ELECTRÓNICO Y FIRMA",
|
||||
"term-cond-p1": "Esta Divulgación de Firma y Registro Electrónico ('Divulgación') es un acuerdo entre el Creador del Documento ('Remitente') y el Firmante ('Usted'), facilitado a través de la plataforma {{appName}} ('Plataforma'). Al firmar documentos a través de {{appName}}, usted acepta los términos descritos en esta Divulgación. Léala detenidamente antes de continuar.",
|
||||
@@ -879,6 +919,10 @@
|
||||
"thanks-for-feedback": "Gracias por su comentario 🙏",
|
||||
"share-your-feedback": "Comparta sus comentarios",
|
||||
"share-your-review": "Comparta su reseña",
|
||||
"please-select-rating": "Por favor seleccione una calificación",
|
||||
"feedback-optional": "Comentarios (opcional)",
|
||||
"feedback-saved": "Los comentarios han sido guardados.",
|
||||
"feedback-save-error": "No se pudieron guardar los comentarios, inténtelo de nuevo.",
|
||||
"date-format": "Formato de fecha",
|
||||
"document-deleted": "El documento ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.",
|
||||
"save-as-template-?": "¿Está seguro de que desea guardar este documento como plantilla?",
|
||||
@@ -1032,14 +1076,204 @@
|
||||
"not-calculated": "No calculado",
|
||||
"not-found-in-signature": "No encontrado en la firma",
|
||||
"readonly-error": "El widget de solo lectura {{widgetName}} debe tener un valor predeterminado o puede hacerlo opcional.",
|
||||
"choose-one":"Elige uno",
|
||||
"choose-one": "Elige uno",
|
||||
"search-templates": "Buscar plantillas…",
|
||||
"search-documents": "Buscar documentos…",
|
||||
"search-contacts": "Buscar contactos…",
|
||||
"invalid-email-found": "Correo electrónico no válido encontrado: {{email}}",
|
||||
"duplicate-email-found": "Correo electrónico duplicado encontrado: {{email}}",
|
||||
"vertical": "Vertical",
|
||||
"add-role-alert": "Por favor, agregue al menos un rol",
|
||||
"edit-draft": "Editar borrador",
|
||||
"horizontal": "Horizontal",
|
||||
"billing": "Facturación",
|
||||
"console": "Consola"
|
||||
}
|
||||
"console": "Consola",
|
||||
"prefill-widget": "Widgets de Relleno Previo",
|
||||
"action-prohibited": "Esta acción no está permitida para su dominio de correo electrónico. Por favor, contacte con su administrador para obtener ayuda.",
|
||||
"must-have-at-least-one-vacant-role": "Debe dejar al menos un rol sin asignar antes de establecer un template como 'public'.",
|
||||
"remove-duplicate": "Por favor, elimina la opción duplicada",
|
||||
"prefill-bulk-error": "El envío masivo no está permitido cuando se han agregado widgets de pre-rellenado. Por favor, elimine los widgets de pre-rellenado para continuar.",
|
||||
"session-expired-title": "Sesión expirada",
|
||||
"access-denied": "Acceso denegado",
|
||||
"upgrade": "Actualizar",
|
||||
"do-not-access-app": "No tienes acceso a esta aplicación.",
|
||||
"dont-have-access": "No tienes acceso.",
|
||||
"valid-email-alert": "Por favor ingresa una dirección de correo electrónico válida.",
|
||||
"otp-not-validate": "OTP no es válido.",
|
||||
"domain-not-allowed": "Este dominio no está permitido",
|
||||
"atleast-one-recipient-alert": "¡Por favor agrega al menos un destinatario!",
|
||||
"incorrect-password-or-decryption-failed": "Contraseña incorrecta o fallo de descifrado.",
|
||||
"incorrect-password-for-file": "Contraseña incorrecta para el archivo: {{file}}",
|
||||
"error-uploading-pdf": "Error al subir PDF.",
|
||||
"provide-password": "Por favor proporciona la contraseña.",
|
||||
"only-pdf-allowed": "Solo se permiten archivos PDF.",
|
||||
"invalid-username-password-region": "Usuario/contraseña o región inválidos.",
|
||||
"pfx-extension-alert": "Por favor sube un archivo con extensión .pfx.",
|
||||
"email-already-exist": "El correo electrónico ya existe",
|
||||
"branding": "Marca",
|
||||
"branding-help": "El branding permite el white labelling de su aplicación",
|
||||
"custom-sub-domain": "Subdominio personalizado",
|
||||
"app-name": "Nombre de la aplicación",
|
||||
"provide-domain-name": "Proporcione su nombre de dominio",
|
||||
"provide-app-name": "Proporcione el nombre de su aplicación",
|
||||
"logo": "Logo",
|
||||
"upload-app-logo": "Suba el logotipo de su aplicación",
|
||||
"prefill-unfilled-widget": "Los siguientes campos obligatorios no pueden quedar vacíos: {{emptyWidget}}. Por favor, complételos para continuar.",
|
||||
"Dashboard": "Panel",
|
||||
"Analytics": "Analítica",
|
||||
"Templates": "Plantillas",
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In Progress": "En progreso",
|
||||
"Completed": "Completado",
|
||||
"Drafts": "Borradores",
|
||||
"Declined": "Rechazados",
|
||||
"Expired": "Expirados",
|
||||
"Contactbook": "Agenda de contactos",
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferencias",
|
||||
"Teams": "Equipos",
|
||||
"Users": "Usuarios",
|
||||
"Drive": "Drive",
|
||||
"Branding": "Marca",
|
||||
"Mail": "Correo",
|
||||
"Storage": "Almacenamiento",
|
||||
"Signing certificate": "Certificado de firma",
|
||||
"General": "General",
|
||||
"Organizations": "Organizaciones",
|
||||
"OrgAdmins": "OrgAdmins",
|
||||
"Debug Pdf": "Depurar PDF",
|
||||
"New Document": "Nuevo documento",
|
||||
"subscription": "Suscripción",
|
||||
"Draft document": "Borrador de documento",
|
||||
"Draft template": "Borrador de plantilla",
|
||||
"Public sign": "Firma pública",
|
||||
"Signup": "Registrarse",
|
||||
"delete-contact": "Eliminar contacto",
|
||||
"total-records-found": "Total de registros encontrados: {{count}}",
|
||||
"Invalid-records-found": "Registros inválidos encontrados: {{records}}",
|
||||
"previous": "Anterior",
|
||||
"page-n-of-n": "Página {{currentPage}} de {{totalPages}}",
|
||||
"import": "Importar",
|
||||
"search": "Buscar",
|
||||
"viewed-on": "Visto el: {{ViewedOn}}",
|
||||
"signed-on": "Firmado el: {{SignedOn}}",
|
||||
"hide": "Ocultar",
|
||||
"show-more": "Mostrar más",
|
||||
"browse-or-drag-to-replace-existing-file": "Busque o arrastre y suelte un nuevo archivo para reemplazar el existente",
|
||||
"optional-details": "Detalles opcionales",
|
||||
"hide-optional-details": "ocultar detalles opcionales",
|
||||
"mail-adapter-subscription-alert": "Bitte upgraden Sie auf den Professional- oder Team-Plan, um ein benutzerdefiniertes SMTP einzurichten.",
|
||||
"connect-to-mail": "Conectar con Gmail",
|
||||
"custom-smtp": "SMTP personalizado",
|
||||
"default-smtp": "SMTP predeterminado de {{appName}}",
|
||||
"host": "Host",
|
||||
"port": "Puerto",
|
||||
"sender-email": "Correo del remitente",
|
||||
"username": "Nombre de usuario",
|
||||
"use-default-mail-adapter": "¿Está seguro de que desea usar los servidores de correo predeterminados de {{appName}} para enviar solicitudes de firma? Recomendamos usar su propio servidor de Gmail o SMTP para una mejor entregabilidad.",
|
||||
"verification-code-sent-registered-email": "Se ha enviado un código de verificación a su correo registrado <1>{{useremail}}</1>. Ingrese el código a continuación para confirmar su configuración.",
|
||||
"smpt-credentials": "Credenciales SMTP",
|
||||
"delete-account": "Eliminar cuenta",
|
||||
"delete-account-que": "¿Está seguro de que desea eliminar su cuenta?",
|
||||
"delete-account-que-user": "Está a punto de eliminar permanentemente este usuario y todos los datos asociados. Esta acción no se puede deshacer.",
|
||||
"user-deleted-successfully": "Usuario y todos los datos asociados eliminados correctamente.",
|
||||
"account-deletion-request-sent-via-mail": "Le hemos enviado por correo electrónico un enlace de confirmación. Apruebe la solicitud para completar la eliminación de su cuenta.",
|
||||
"type-exact-email-delete": "Escriba el correo electrónico exacto para habilitar la eliminación",
|
||||
"email-does-not-match": "El correo electrónico no coincide.",
|
||||
"please-type-to-confirm": "Por favor, escriba <1>{{userEmail}}</1> para confirmar:",
|
||||
"email-settings": "Configuración de correo electrónico",
|
||||
"email-settings-help": "Para garantizar una mejor entregabilidad en la bandeja de entrada de sus correos electrónicos de solicitud de firma, puede conectar su propio proveedor de correo electrónico. Elija una de las siguientes opciones:",
|
||||
"connect-to-gmail": "Conectar con Gmail: ",
|
||||
"connect-to-gmail-help": "Utilice su cuenta de Gmail para enviar correos electrónicos de solicitud de firma directamente desde su propia bandeja de entrada, mejorando la tasa de entrega y la fiabilidad.",
|
||||
"connect-to-smtp": "SMTP personalizado: ",
|
||||
"connect-to-smtp-help": "Utilice su propio servidor SMTP para enviar correos electrónicos a través de su dominio. Esta opción le brinda control total sobre su infraestructura de correo electrónico, mejorando la entregabilidad y la coherencia de la marca.",
|
||||
"connect-to-default": "Si lo prefiere, también puede usar los servidores de correo predeterminados de {{appName}}, aunque recomendamos usar los suyos para obtener mejores resultados.",
|
||||
"email-settings-redirect-message": "Esta configuración se ha movido de la consola a Configuración del menú principal → Preferencias. Esta página se eliminará en próximas versiones.",
|
||||
"go-to-preferences-menu": "Ir al menú de preferencias",
|
||||
"document-download-filename-format": "Formato de nombre de archivo para la descarga del documento",
|
||||
"preview": "Vista previa: ",
|
||||
"download-filename-format-help": "Elija cómo se nombran los archivos PDF descargados. Su selección se guarda en su perfil y se usa en toda la aplicación.",
|
||||
"delete-action-prohibited": "Esta acción no está permitida. Por favor, póngase en contacto con su administrador para solicitar la eliminación de la cuenta.",
|
||||
"not-verified": "No verificado",
|
||||
"verified": "Verificado",
|
||||
"expires": "Expira",
|
||||
"fix-resend-error": "No puede corregir y reenviar este documento porque contiene widgets predefinidos.",
|
||||
"duplicate-template-error": "No puede duplicar esta plantilla porque contiene widgets predefinidos.",
|
||||
"save-as-template-error": "Este documento no se puede guardar como plantilla porque contiene widgets predefinidos.",
|
||||
"redirecting-you-in": "Redirigiéndolo en {{redirectTimeLeft}} seg...",
|
||||
"pdf-tools-tour": "Haga clic en estos botones para agregar, eliminar, reorganizar, rotar y acercar/alejar páginas.",
|
||||
"widgets": "Widgets",
|
||||
"prefill-tour": "Use esta opción para ingresar información por adelantado antes de enviar el documento a los destinatarios.",
|
||||
"empty-prefill-error": "Los siguientes campos obligatorios no pueden quedar vacíos:",
|
||||
"please-fill-out": "Por favor, complételos para continuar.",
|
||||
"custom-signing-certificate": "Certificado de firma personalizado",
|
||||
"signing-certificate-help": "Puede cargar su propio certificado de firma de documentos, que se utilizará para firmar todos sus documentos así como los certificados de finalización. El archivo del certificado debe ser un certificado P12 en formato PFX.",
|
||||
"certificate-file-p12-in-PFX-format": "Archivo de certificado (certificado p12 en formato PFX)",
|
||||
"password-of-pfx-file": "Ingrese la contraseña del archivo pfx",
|
||||
"update": "Actualizar",
|
||||
"use-default-certificate": "Usar el certificado predeterminado de {{appName}}",
|
||||
"upgrade-to-team-plan": "Actualizar al plan de equipo",
|
||||
"setup-file-storage": "Configurar almacenamiento de archivos",
|
||||
"save-and-activate": "Guardar y activar",
|
||||
"logging-out-to-apply-settings": "Se está cerrando la sesión para aplicar la nueva configuración",
|
||||
"reconnect-to-default": "Volver a conectar con {{appName}}",
|
||||
"active-file-adapter": "Adaptador de archivos activo",
|
||||
"file-adapter-unique-name": "Nombre único del adaptador de archivos",
|
||||
"unique-name-for-file-adapter": "Ingrese un nombre único para el adaptador de archivos",
|
||||
"storage-provider": "Proveedor de almacenamiento",
|
||||
"enter-bucket-name": "Ingrese el nombre del bucket",
|
||||
"enter-space-name": "Ingrese el nombre del espacio",
|
||||
"enter-region-of-bucket": "Ingrese la región del bucket",
|
||||
"enter-region-of-space": "Ingrese la región del espacio",
|
||||
"enter-access-key": "Ingrese la clave de acceso",
|
||||
"enter-secret-access-key": "Ingrese la clave secreta de acceso",
|
||||
"otp-email": "Hemos enviado un código de verificación",
|
||||
"save-as-temp-warn": "Nota: Este documento incluye widgets prellenados, que se eliminarán automáticamente ya que ya están incorporados en el documento base.",
|
||||
"edit-document": "Editar documento",
|
||||
"modify": "Modificar",
|
||||
"merge-certificate-to-pdf": "Combinar certificado con PDF",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "Esto garantizará que el certificado de finalización esté incluido en el documento PDF final. Sin embargo, tenga en cuenta que una vez combinado, el certificado no se podrá separar del documento principal.",
|
||||
"p2": "Si elige no combinar, el certificado de finalización se proporcionará como un archivo PDF separado junto con el documento firmado."
|
||||
},
|
||||
"read-only-date-error": "El widget de fecha de solo lectura debe tener una fecha predeterminada.",
|
||||
"set-date": "Establecer fecha",
|
||||
"set-today": "Fecha de firma",
|
||||
"enter-name": "Ingrese nombre",
|
||||
"enter-email": "Ingrese correo electrónico",
|
||||
"subscribe-to-opensign-msg": "Suscríbase a {{appName}} y disfrute de firmas digitales gratuitas ilimitadas.",
|
||||
"duplicate-subscribe-msg": "Duplicar le permite crear una copia exacta de la plantilla seleccionada, lo que le permite reutilizarla o modificarla sin afectar al original.",
|
||||
"save-as-template-msg": "Guarde este documento como una plantilla reutilizable que podrá usar nuevamente para futuros documentos.",
|
||||
"public-credit-alert": "Firmar a través de este enlace consumirá sus créditos de correo electrónico gratuitos. Como usuario gratuito, se le asignan 15 créditos de correo electrónico por mes para evitar que los spammers abusen de nuestros sistemas. Para disfrutar de límites más altos y acceso ininterrumpido, suscríbase a los planes de pago de <1>OpenSign™.</1>",
|
||||
"know-more-about": "Saber más sobre",
|
||||
"free-unlimited-signatures": "¿Firmas ilimitadas gratis",
|
||||
"email-quota-updated": "La cuota diaria de correos se actualizó correctamente.",
|
||||
"password-has-been-reset": "La contraseña ha sido restablecida.",
|
||||
"Unauthorized to reset your own password.": "No está autorizado para restablecer su propia contraseña.",
|
||||
"User not found or not allowed.": "Usuario no encontrado o no autorizado.",
|
||||
"User not found.": "Usuario no encontrado.",
|
||||
"Admin user tenant not found.": "Inquilino de usuario administrador no encontrado.",
|
||||
"User is not authenticated.": "El usuario no está autenticado.",
|
||||
"Please provide required parameters.": "Por favor, proporcione los parámetros requeridos.",
|
||||
"reset-password": "Restablecer contraseña de usuario",
|
||||
"enter-strong-password": "Ingrese una contraseña segura o genere una automáticamente.",
|
||||
"autogenerate": "Generar automáticamente",
|
||||
"extend": "Extender",
|
||||
"console-general-help": "Para permitir que los usuarios individuales personalicen sus propias plantillas de correo electrónico, vaya a",
|
||||
"console-mail-help": "Para actualizar las plantillas de correo electrónico de toda la empresa, vaya a",
|
||||
"admin-setting-disabled": "Esta configuración está deshabilitada. Comuníquese con el administrador para obtener ayuda.",
|
||||
"company-email-settings": "Configuración de correo electrónico de la empresa",
|
||||
"allow-email-template-individually": "Permitir la personalización individual de plantillas de correo electrónico para los usuarios ",
|
||||
"user-email-template-general-help": "Si se desactiva, los usuarios no podrán personalizar sus plantillas de correo electrónico.",
|
||||
"disable-individual-user-smtp": "desactivar la configuración smtp individual del usuario",
|
||||
"enable-individual-user-smtp": "activar la configuración smtp individual del usuario",
|
||||
"user-smtp-general-help": "Cuando está desactivado, los usuarios no podrán configurar su propio SMTP. Todos los correos utilizarán la configuración SMTP global.",
|
||||
"global-smtp-settings": "Configuración SMTP Global",
|
||||
"global-smtp-settings-help-p1": "Estas configuraciones se aplican globalmente en toda su cuenta de {{appName}}. Si un usuario no ha configurado sus propios ajustes de entrega de correo, el sistema utilizará automáticamente la configuración SMTP global definida aquí.",
|
||||
"global-smtp-settings-help-p2": "Para garantizar una mejor entregabilidad de todos los correos de solicitud de firma, puede conectar su proveedor de correo preferido. Elija una de las siguientes opciones:",
|
||||
"global-connect-to-gmail-help": "Use una cuenta de Gmail para enviar correos de solicitud de firma directamente desde la bandeja de entrada de su organización. Esto mejora la entregabilidad, la reputación y la fiabilidad.",
|
||||
"global-connect-to-smtp-help": "Use su propio servidor SMTP para enviar correos a través de su dominio. Esto proporciona control total sobre su infraestructura de correo, mejora la entregabilidad y mantiene la coherencia de marca.",
|
||||
"global-smtp-settings-help-p3": "Si no se configuran ajustes a nivel de usuario, se aplicarán automáticamente estos ajustes globales."
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"dark-mode": "Mode sombre",
|
||||
"name": "Nom et Prénom",
|
||||
"phone": "Téléphone",
|
||||
"phone-optional": "(facultatif)",
|
||||
"phone-optional": "Optionnel",
|
||||
"email": "E-mail",
|
||||
"company": "Organisation",
|
||||
"job-title": "Votre Fonction",
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "Nom et Prénom",
|
||||
"Date": "Date"
|
||||
},
|
||||
"folder": "Dossier",
|
||||
"pdf": "Pdf",
|
||||
"context-menu": {
|
||||
"Download": "Télécharger",
|
||||
"Rename": "Renommer",
|
||||
@@ -43,13 +45,17 @@
|
||||
"upgrade-now": "Mettre à jour maintenant",
|
||||
"contact-now": "Contacter maintenant",
|
||||
"upgrade-to": "Mettre à niveau vers",
|
||||
"pro": "PRO",
|
||||
"plan": "Offre",
|
||||
"connect": "Connecter",
|
||||
"connect-to-g-drive": "Se connecter à Google Drive",
|
||||
"reconnect-to-g-drive": "Se reconnecter à Google Drive",
|
||||
"gdrive-info-connect": "Lorsque Google Drive est connecté, le document complété sera enregistré dans le dossier {{appName}} sur Google Drive.",
|
||||
"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-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",
|
||||
"tour-content": "Ne plus afficher",
|
||||
"pro": "PRO",
|
||||
"team": "Équipe",
|
||||
"docs": "Documents",
|
||||
"session-expired": "Votre session a expiré.",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "Content de te revoir!",
|
||||
"Login-to-your-account": "Se connecter à son compte",
|
||||
"password": "Mot de passe",
|
||||
"forgot-password": "Mot de passe oublié?",
|
||||
"forgot-password": "Mot de passe oublié",
|
||||
"loading": "Chargement...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Connectez-vous avec SSO",
|
||||
"login-page": "Page de connexion",
|
||||
"or": "OU",
|
||||
"signup-page": "Page d'inscription",
|
||||
"password-match-length": "Le nouveau mot de passe et la confirmation doivent correspondre",
|
||||
"password-length": "Le mot de passe doit être de plus de 8 caractères",
|
||||
"password-case": "Le mot de passe doit contenir une lettre majuscule, minuscule et un chiffre ",
|
||||
"password-special-char": "Le mot de passe doit contenir un caractère spécial",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "Équipe",
|
||||
"Team-update-successfully": "Équipe mise à jour avec succès.",
|
||||
"Users": "Utilisateurs"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "SMTP Global"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Besoin de votre signature",
|
||||
@@ -155,10 +163,24 @@
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux",
|
||||
"Expiry-date": "Fecha de caducidad"
|
||||
"Expiry-date": "Fecha de caducidad",
|
||||
"Company": "Organisation",
|
||||
"JobTitle": "Votre Fonction",
|
||||
"Time to complete (Days)": "Temps pour terminer (jours)",
|
||||
"Auto reminder": "Rappel automatique",
|
||||
"Remind once in every (Days)": "Rappeler une fois tous les (jours)",
|
||||
"Enable OTP verification": "Activer la vérification OTP",
|
||||
"Enable Tour": "Activer la visite",
|
||||
"Notify on signatures": "Notifier lors des signatures",
|
||||
"Allow modifications": "Autoriser les modifications",
|
||||
"Redirect url": "URL de redirection",
|
||||
"Created Date": "Date de création",
|
||||
"Updated Date": "Date de mise à jour",
|
||||
"Expiry Date": "Date d'expiration"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
"Sign": "SIGNER",
|
||||
"Resend": "Relancer",
|
||||
"Rename": "Renommer",
|
||||
"Revoke": "Révoquer",
|
||||
@@ -180,7 +202,8 @@
|
||||
"daily-mail-quota": "Quota d'e-mails quotidien",
|
||||
"Save as template": "Enregistrer comme modèle",
|
||||
"Fix & resend": "Corriger et renvoyer",
|
||||
"Kiosk Mode": "Mode Kiosque"
|
||||
"Kiosk Mode": "Mode Kiosque",
|
||||
"Reset password": "Réinitialiser le mot de passe"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
@@ -192,16 +215,15 @@
|
||||
"Contactbook": "Il s'agit d'une liste de contacts/signataires que vous avez ajoutés. Ceux-ci apparaîtront sous forme de suggestions lorsque vous tenterez d'ajouter des signataires à un nouveau document.",
|
||||
"Templates": "Il s'agit d'une liste de modèles à votre disposition pour créer des documents. Vous pouvez cliquer sur le bouton 'Utiliser' pour créer un nouveau document à l'aide d'un modèle, modifier le document et ajouter des signataires à l'étape suivante."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "signez vous-même",
|
||||
"Request Signatures": "Demander des signatures",
|
||||
"New Template": "Nouveau modèle"
|
||||
},
|
||||
"Sign Yourself": "signez vous-même",
|
||||
"Request Signatures": "Demander des signatures",
|
||||
"New Template": "Nouveau modèle",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "fichier sélectionné",
|
||||
"template-title": "Titre du modèle",
|
||||
"document-title": "Titre du document",
|
||||
"title": "Titre",
|
||||
"description": "Description",
|
||||
"time-to-complete": "Temps de réalisation (jours)",
|
||||
"send-in-order": "Envoyer dans l'ordre ?",
|
||||
@@ -241,7 +263,6 @@
|
||||
"submit": "Soumettre",
|
||||
"reset": "Réinitialiser",
|
||||
"my-signature": "Ma signature",
|
||||
"initial-alert": "Mon initiale introuvable",
|
||||
"signature": "Signature",
|
||||
"upload-image": "Télécharger une image",
|
||||
"clear": "Vider",
|
||||
@@ -280,7 +301,7 @@
|
||||
"deactivate": "désactiver",
|
||||
"this-user": "cet utilisateur ",
|
||||
"delete-user": "Supprimer l'utilisateur",
|
||||
"delete": "supprimer",
|
||||
"delete": "Supprimer",
|
||||
"add-user": "Ajouter un utilisateur",
|
||||
"password-generateed": "Le mot de passe ne sera généré qu'une seule fois ; assurez-vous de le copier.",
|
||||
"Team status": "Statut de l'équipe",
|
||||
@@ -367,7 +388,8 @@
|
||||
"dropdown": "dérouler",
|
||||
"radio button": "bouton radio",
|
||||
"image": "image",
|
||||
"email": "e-mail"
|
||||
"email": "e-mail",
|
||||
"number": "nombre"
|
||||
},
|
||||
"fields": "Des champs",
|
||||
"recipients": "Destinataires",
|
||||
@@ -379,6 +401,7 @@
|
||||
"certificate": "Certificat",
|
||||
"decline": "refusé",
|
||||
"finish": "terminé",
|
||||
"done": "Terminé",
|
||||
"mail": "Mail",
|
||||
"sign-now": "Signez maintenant",
|
||||
"successfully-signed": "Signé avec succès !",
|
||||
@@ -388,6 +411,10 @@
|
||||
"Email-verified-alert-1": "L'e-mail est vérifié.",
|
||||
"Email-verified-alert-2": "L'e-mail est déjà vérifié.",
|
||||
"upload-stamp-image": "Télécharger l'image du tampon",
|
||||
"draw-signature": "Dessiner la signature",
|
||||
"draw-initials": "Dessiner les initiales",
|
||||
"enter-text": "Saisir du texte",
|
||||
"enter-widgettype": "Saisir {{widgetType}}",
|
||||
"draw": "Dessiner",
|
||||
"type": "Taper",
|
||||
"color-type": {
|
||||
@@ -400,10 +427,10 @@
|
||||
"upload": "Télécharger",
|
||||
"initial-teb": "Initiales",
|
||||
"signature-tab": "Signature",
|
||||
"your-signature": "Votre signature",
|
||||
"initial-alert": "Mon initiale est introuvable",
|
||||
"copy-title": "Copier le widget vers",
|
||||
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
||||
"reset-password": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
|
||||
"reset-password-alert-1": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
|
||||
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
|
||||
"reset-password-alert-3": "Réinitialisez votre mot de passe",
|
||||
"faild-animation": "Échec du chargement de l'animation",
|
||||
@@ -419,6 +446,19 @@
|
||||
"maximum-check": "Contrôle maximum",
|
||||
"cell-count": "numération cellulaire",
|
||||
"default-value": "Valeur par défaut",
|
||||
"formula": "Formule",
|
||||
"formula-placeholder": "Créez une formule à l'aide des widgets numériques",
|
||||
"formula-no-widgets": "Aucun autre widget numérique n'est encore attribué à ce signataire.",
|
||||
"supported-operators": "Opérateurs pris en charge :",
|
||||
"formula-helper": "Cliquez sur une puce de widget numérique pour l'insérer (ex. {{number-1}} + {{number-2}}).",
|
||||
"formula-error-letter": "Encadrez les noms de widget avec des doubles accolades (ex. {{number-1}}).",
|
||||
"formula-error-characters": "En dehors des accolades, seuls les chiffres et +, -, *, /, (, ) sont autorisés.",
|
||||
"formula-error-braces": "Chaque {{ ouvrante doit avoir une }} correspondante.",
|
||||
"formula-error-operators": "Lorsque vous utilisez des opérateurs ou des parenthèses, ajoutez au moins deux valeurs.",
|
||||
"formula-error-leading-operator": "Les formules ne peuvent pas commencer par des opérateurs.",
|
||||
"decimal-places": "Décimales",
|
||||
"number-of-decimal-places": "Nombre de décimales",
|
||||
"decimal-places-helper": "Définir sur 0 pour les nombres entiers",
|
||||
"select": "Sélectionner",
|
||||
"read-only": "Est en lecture seule",
|
||||
"hide-labels": "Masquer les étiquettes",
|
||||
@@ -465,13 +505,13 @@
|
||||
"add-recipients": "Ajouter des destinataires",
|
||||
"loading-mssg": "Cela pourrait prendre du temps",
|
||||
"send-mail": "Envoyer un mail",
|
||||
"signature-field-widget": "Au moins un champ de signature doit être ajouté pour chaque utilisateur. Vous n'avez pas ajouté de champs de signature pour {{signersName}}",
|
||||
"signature-field-widget": "Chaque utilisateur doit avoir au moins un widget de signature. Vous n’avez pas ajouté de widget de signature pour {{signersName}}.",
|
||||
"placeholder-alert-1": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
|
||||
"placeholder-alert-2": "Veuillez confirmer que vous avez rempli le champ de texte.",
|
||||
"placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ",
|
||||
"placeholder-alert-4": "Vous avez envoyé avec succès des mails à tous les",
|
||||
"placeholder-mail-alert": "Vous avez envoyé un e-mail avec succès à {{name}}. Les signataires suivants recevront un e-mail une fois que {{name}} aura signé le document.",
|
||||
"placeholder-mail-alert-you": "Les signataires suivants recevront un e-mail dès que vous signez le document.",
|
||||
"placeholder-mail-alert-you": "Les signataires suivants recevront un e-mail une fois que vous aurez signé le document.",
|
||||
"placeholder-alert-5": "Voulez-vous signer des documents maintenant ?",
|
||||
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !",
|
||||
"placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
|
||||
@@ -600,7 +640,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.",
|
||||
"home-layout-2": "Pour télécharger des documents à auto-signature ou pour demander la signature d'autres personnes, sélectionnez simplement les boutons correspondants.",
|
||||
"home-layout-3": "Vous êtes prêt à commencer à utiliser {{appName}} ! Si vous avez besoin d'aide, n'hésitez pas à nous contacter.",
|
||||
"home-layout-3": "Vous êtes prêt à commencer à utiliser {{appName}} !",
|
||||
"home-layout-4": "Ajoutez-nous une étoile sur",
|
||||
"generate-token": "Effectuez la mise à niveau maintenant pour générer un jeton API de production.",
|
||||
"opensign-drive-1": "Cliquez sur les liens du fil d'Ariane pour naviguer facilement dans la hiérarchie des dossiers et afficher les documents dans chaque dossier.",
|
||||
"opensign-drive-2": "Cliquez sur le bouton Ajouter pour créer un nouveau dossier ou document.",
|
||||
@@ -610,34 +651,32 @@
|
||||
"opensign-drive-6": "Faites un clic droit sur un document pour voir les options telles que Télécharger, Renommer, Déplacer et Supprimer. Cliquez sur le document pour l'ouvrir.",
|
||||
"opensign-drive-7": "Faites un clic droit sur n'importe quel dossier pour voir les options. Choisissez 'Renommer' pour changer le nom du dossier ou cliquez sur le dossier pour naviguer dans son contenu.",
|
||||
"pdf-request-file-1": "Liste des signataires qui doivent encore signer le document.",
|
||||
"pdf-request-file-2": "Cliquez sur l'un des espaces réservés apparaissant sur le document pour signer. Vous verrez alors des options pour dessiner votre signature, la saisir ou télécharger une image.",
|
||||
"pdf-request-file-3": "Cliquez sur les boutons Refuser ou Terminer pour parcourir votre document. Utilisez le menu à points de suspension pour des options supplémentaires, notamment le bouton Télécharger.",
|
||||
"pdf-request-file-2": "Cliquez sur l'un des champs figurant dans le document pour commencer. Vous pourrez ensuite remplir les informations requises.",
|
||||
"pdf-request-file-3": "Une fois que vous avez rempli tous les champs obligatoires, cliquez sur « Terminer ». Vous pourrez ensuite télécharger le document signé. Selon les paramètres de l'expéditeur, vous pourriez recevoir une copie du document complété avec le certificat de finalisation une fois que tous les destinataires auront signé.",
|
||||
"pdf-request-file-4": "Liste des signataires ayant déjà signé le document.",
|
||||
"pdf-request-file-5": "Vous pouvez cliquer sur 'Signer automatiquement tout' pour signer automatiquement à tous les emplacements que vous êtes censé signer. Assurez-vous de lire correctement le document avant de cliquer sur ce bouton.",
|
||||
"pdf-request-file-6": "Veuillez remplir les champs à la page {{pagenumbers}}, tous mis en évidence avec la même couleur pour une identification facile.",
|
||||
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra de la même couleur que le nom du destinataire une fois que vous le déposerez sur le document.",
|
||||
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un widget. Le widget apparaîtra dans la même couleur que le nom du destinataire une fois déposé sur le document.",
|
||||
"placeholder-sign-2": "Cliquer sur le bouton 'Ajouter des destinataires' vous permettra d'ajouter plus de signataires.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un widget pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "La zone de contenu du PDF affiche déjà les widgets existants du modèle. Pour plus de commodité, ces widgets correspondront à la couleur du nom du destinataire, ce qui les rend facilement identifiables.",
|
||||
"placeholder-sign-6": "En cliquant sur 'Suivant', le document sera enregistré. À l'étape suivante, vous pouvez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"report-1": "Cliquez sur le bouton 'Ajouter' pour créer un nouveau modèle. Les modèles sont des documents réutilisables conçus pour générer rapidement de nouveaux documents avec la même structure et différents signataires. Par exemple, un modèle RH pour l'intégration peut avoir des rôles prédéfinis tels que 'Responsable RH' et ' Nouvel employé'. Chaque fois que vous utilisez le modèle, vous pouvez attribuer le rôle 'Nouvel employé' à différents membres du personnel entrants, tandis que le rôle 'Responsable RH' reste constant, facilitant ainsi un processus d'intégration fluide pour chaque recrue",
|
||||
"redirect": "Cliquez sur le bouton 'Utiliser' pour créer un nouveau document à partir d'un modèle existant.",
|
||||
"bulksend": "Pour envoyer rapidement plusieurs documents en utilisant un modèle existant en créant simplement les adresses e-mail des destinataires, cliquez sur le bouton 'Envoi en masse'.",
|
||||
"option": "Ce menu révèle plus d'options telles que Modifier et Supprimer. Utilisez le bouton « Modifier » pour ajouter des rôles de signataire, modifier les champs et mettre à jour votre modèle. Les modifications s'appliqueront à tous les futurs documents créés à partir de ce modèle mais n'affecteront pas les documents existants. Utilisez le bouton Supprimer, vous pouvez supprimer le modèle ",
|
||||
"signyour-self-1": "Sélectionnez et faites glisser vos widgets préférés sur le PDF pour personnaliser votre document avant de le signer. Choisissez les endroits parfaits pour chaque modification afin d'adapter le document à vos besoins.",
|
||||
"bulksend": "Pour envoyer rapidement plusieurs documents en utilisant un modèle existant, saisissez simplement les adresses e-mail des destinataires et cliquez sur le bouton 'Envoi groupé'. Vous pouvez envoyer jusqu'à 50 destinataires.",
|
||||
"option": "Ce menu affiche plus d'options comme Modifier, Supprimer, Renommer, Dupliquer, Partager, etc. <1>Cliquez ici</1> pour en savoir plus sur toutes les options disponibles. <3>Remarque : Les modifications apportées à un modèle existant s'appliqueront à tous les futurs documents créés à partir de ce modèle, mais n'affecteront pas les documents déjà envoyés.</3>",
|
||||
"signyour-self-1": "Sélectionnez ou faites glisser vos widgets préférés sur le PDF pour personnaliser votre document avant de le signer. Choisissez les emplacements parfaits pour chaque widget afin d'adapter le document à vos besoins.",
|
||||
"signyour-self-2": "Faites glisser et déposer n'importe où dans cette zone. Vous pourrez la redimensionner et la déplacer plus tard.",
|
||||
"template-placeholder-1": "Cliquer sur le bouton 'Ajouter un rôle' vous permettra d'ajouter différents rôles de signataire. Vous pourrez attacher des utilisateurs à chaque rôle dans les étapes suivantes.",
|
||||
"template-placeholder-2": "Une fois les rôles ajoutés, sélectionnez un rôle dans la liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra dans la même couleur que le nom du rôle une fois que vous l'aurez déposé sur le document.",
|
||||
"template-placeholder-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"template-placeholder-4": "Faites glisser l'espace réservé pour un rôle n'importe où sur le document. N'oubliez pas qu'il apparaîtra dans la même couleur que le nom du destinataire pour une référence facile.",
|
||||
"template-placeholder-5": "Cliquer sur 'Suivant' stockera le modèle actuel. Après l'enregistrement, vous serez invité à créer un nouveau document à partir de ce modèle si vous le souhaitez.",
|
||||
"template-placeholder-2": "Après avoir ajouté des rôles, choisissez-en un dans la liste pour placer un widget pour ce destinataire. Vous pouvez cliquer sur le widget ou le faire glisser sur le document. Une fois placé, le widget s'affichera dans la même couleur que le rôle sélectionné.",
|
||||
"template-placeholder-3": "Cliquer sur 'Suivant' stockera le modèle actuel. Après l'enregistrement, vous serez invité à créer un nouveau document à partir de ce modèle si vous le souhaitez.",
|
||||
"webhook-1": "Mettez à niveau maintenant pour définir le webhook",
|
||||
"Need your Signature": "En cliquant sur cette carte, vous accéderez à la liste des documents en attente de révision.",
|
||||
"Out for signatures": "En cliquant sur cette carte, vous accéderez à une liste de documents en attente de signature.",
|
||||
"Recent signature requests": "Voici une liste de documents qui attendent votre signature.",
|
||||
"Recently sent for signatures": "Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
|
||||
"Drafts": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
"Drafts": "Ce sont des documents que vous avez commencés mais que vous n'avez pas finalisés pour l'envoi.",
|
||||
"public-template": "Cette vidéo montre comment configurer votre profil public personnalisé, tel que 'https://opensign.me/your-username'. Vous apprendrez également comment personnaliser votre slogan et rendre vos modèles disponibles pour la signature publique.",
|
||||
"allowModify-widgets": "Vous pouvez faire glisser et déposer n'importe lequel de ces champs sur le document, en plus des champs déjà désignés pour vous par le créateur du document."
|
||||
},
|
||||
@@ -696,7 +735,7 @@
|
||||
"quota-mail-info": "Pour maintenir la qualité du service et prévenir le spam, OpenSign permet jusqu'à 15 e-mails par mois avec le plan gratuit. Passez à l'offre supérieure pour un envoi illimité d'e-mails.",
|
||||
"quota-mail-reset": "Les crédits de votre email de demande de signature seront réinitialisés le",
|
||||
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
|
||||
"quota-mail-tip-tip": "Astuce: Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement le lien de demande de signature.",
|
||||
"quota-mail-tip": "Astuce: Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement le lien de demande de signature.",
|
||||
"quota-mail-head": "Quota atteint",
|
||||
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month": "envoyé ce mois-ci",
|
||||
@@ -728,7 +767,7 @@
|
||||
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
|
||||
"add-user-template": "Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
|
||||
"pdf-uncompatible": "PDF n'est pas compatible, veuillez contacter {{appName}}",
|
||||
"text-field-tour": "Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
|
||||
"text-field-tour": "Les champs 'Pré-remplissage' doivent être remplis avant l'envoi du document. Si vous avez besoin de l'intervention des signataires, utilisez plutôt les champs réservés aux signataires.",
|
||||
"attach-signer-tour": "Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
|
||||
"allowed-signature-types": "Types de signature autorisés",
|
||||
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
|
||||
@@ -776,7 +815,7 @@
|
||||
"agree-p1": "Je confirme avoir lu et compris les ",
|
||||
"agree-p2": "Divulgation des enregistrements électroniques et des signatures",
|
||||
"agree-p3": "et consentez à l'utilisation d'enregistrements et de signatures électroniques.",
|
||||
"agrre-button": " Accepter et continuer ",
|
||||
"agrre-button": "Je confirme et j'accepte de continuer",
|
||||
"term-cond-title": "Termes et conditions",
|
||||
"term-cond-h": "DIVULGATION D'ENREGISTREMENT ÉLECTRONIQUE ET DE SIGNATURE",
|
||||
"term-cond-p1": "Cette divulgation d'enregistrement électronique et de signature (' Divulgation ') est un accord entre le créateur du document (' Expéditeur ') et le signataire (' Vous '), facilité via la plateforme {{appName}} ( Plateforme ). En signant des documents via {{appName}}, vous acceptez les conditions décrites dans cette divulgation. Veuillez la lire attentivement avant de continuer.",
|
||||
@@ -879,6 +918,10 @@
|
||||
"thanks-for-feedback": "Merci pour votre retour 🙏",
|
||||
"share-your-feedback": "Partagez votre avis",
|
||||
"share-your-review": "Partagez votre avis",
|
||||
"please-select-rating": "Veuillez sélectionner une note",
|
||||
"feedback-optional": "Retour (optionnel)",
|
||||
"feedback-saved": "Le retour a été enregistré.",
|
||||
"feedback-save-error": "Impossible d'enregistrer le retour, veuillez réessayer.",
|
||||
"date-format": "Format de date",
|
||||
"document-deleted": "Le document a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur.",
|
||||
"save-as-template-?": "Êtes-vous sûr de vouloir enregistrer ce document comme modèle ?",
|
||||
@@ -988,7 +1031,7 @@
|
||||
"finish-mssg": "Êtes-vous sûr de vouloir terminer le document ?",
|
||||
"review": "Revoir",
|
||||
"next-field": "Champ suivant",
|
||||
"required-mssg":"{{leftRequiredWidget}} champs sur {{totalWidget}} restants",
|
||||
"required-mssg": "{{leftRequiredWidget}} champs sur {{totalWidget}} restants",
|
||||
"verify-document": "Vérifier le document",
|
||||
"verify-document-signature": "Vérifier la signature du document",
|
||||
"select-pdf-document": "Sélectionner le document PDF",
|
||||
@@ -1032,14 +1075,204 @@
|
||||
"not-calculated": "Non calculé",
|
||||
"not-found-in-signature": "Introuvable dans la signature",
|
||||
"readonly-error": "Le widget en lecture seule {{widgetName}} doit avoir une valeur par défaut ou vous pouvez le rendre facultatif.",
|
||||
"choose-one":"Choisissez-en un",
|
||||
"choose-one": "Choisissez-en un",
|
||||
"search-templates": "Rechercher des modèles…",
|
||||
"search-documents": "Rechercher des documents…",
|
||||
"search-contacts": "Rechercher des contacts…",
|
||||
"add-role-alert": "Veuillez ajouter au moins un rôle",
|
||||
"edit-draft": "Modifier le brouillon",
|
||||
"invalid-email-found": "Adresse e-mail invalide trouvée : {{email}}",
|
||||
"duplicate-email-found": "Adresse e-mail en double trouvée : {{email}}",
|
||||
"vertical": "Vertical",
|
||||
"horizontal": "Horizontal",
|
||||
"billing": "Facturation",
|
||||
"console": "Console"
|
||||
}
|
||||
"console": "Console",
|
||||
"prefill-widget": "Widgets de Préremplissage",
|
||||
"action-prohibited": "Cette action n'est pas autorisée pour votre domaine de messagerie. Veuillez contacter votre administrateur pour obtenir de l'aide.",
|
||||
"must-have-at-least-one-vacant-role": "Vous devez laisser au moins un rôle non attribué avant de définir un template comme 'public'.",
|
||||
"remove-duplicate": "Veuillez supprimer l'option en double",
|
||||
"prefill-bulk-error": "L'envoi en masse n'est pas autorisé lorsque des widgets de pré-remplissage sont ajoutés. Veuillez supprimer les widgets de pré-remplissage pour continuer.",
|
||||
"session-expired-title": "Session expirée",
|
||||
"access-denied": "Accès refusé",
|
||||
"upgrade": "Mettre à jour",
|
||||
"do-not-access-app": "Vous n'avez pas accès à cette application.",
|
||||
"dont-have-access": "Vous n'avez pas accès.",
|
||||
"valid-email-alert": "Veuillez saisir une adresse e-mail valide.",
|
||||
"otp-not-validate": "OTP non valide.",
|
||||
"domain-not-allowed": "Ce domaine n'est pas autorisé",
|
||||
"atleast-one-recipient-alert": "Veuillez ajouter au moins un destinataire !",
|
||||
"incorrect-password-or-decryption-failed": "Mot de passe incorrect ou échec du déchiffrement.",
|
||||
"incorrect-password-for-file": "Mot de passe incorrect pour le fichier : {{file}}",
|
||||
"error-uploading-pdf": "Erreur lors du téléchargement du PDF.",
|
||||
"provide-password": "Veuillez fournir le mot de passe.",
|
||||
"only-pdf-allowed": "Seuls les fichiers PDF sont autorisés.",
|
||||
"invalid-username-password-region": "Nom d'utilisateur/mot de passe ou région invalide.",
|
||||
"pfx-extension-alert": "Veuillez télécharger un fichier avec l'extension .pfx.",
|
||||
"email-already-exist": "L'e-mail existe déjà",
|
||||
"branding": "Image de marque",
|
||||
"branding-help": "Le branding permet le white labelling de votre application",
|
||||
"custom-sub-domain": "Sous-domaine personnalisé",
|
||||
"app-name": "Nom de l'application",
|
||||
"provide-domain-name": "Fournissez votre nom de domaine",
|
||||
"provide-app-name": "Fournissez le nom de votre application",
|
||||
"logo": "Logo",
|
||||
"upload-app-logo": "Téléchargez le logo de votre application",
|
||||
"prefill-unfilled-widget": "Les champs obligatoires suivants ne peuvent pas être vides : {{emptyWidget}}. Veuillez les remplir pour continuer.",
|
||||
"Dashboard": "Tableau de bord",
|
||||
"Analytics": "Analytique",
|
||||
"Templates": "Modèles",
|
||||
"Need your sign": "Besoin de votre signature",
|
||||
"In Progress": "En cours",
|
||||
"Completed": "Complété",
|
||||
"Drafts": "Brouillons",
|
||||
"Declined": "Signature refusé",
|
||||
"Expired": "Expiré",
|
||||
"Contactbook": "Carnet de contacts",
|
||||
"My Signature": "Ma signature",
|
||||
"API Token": "Jeton API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Préférences",
|
||||
"Teams": "Équipe",
|
||||
"Users": "Utilisateurs",
|
||||
"Drive": "Lecteur",
|
||||
"Branding": "Image de marque",
|
||||
"Mail": "Courrier",
|
||||
"Storage": "Stockage",
|
||||
"Signing certificate": "Certificat de signature",
|
||||
"General": "Général",
|
||||
"Organizations": "Organisations",
|
||||
"OrgAdmins": "OrgAdmins",
|
||||
"Debug Pdf": "Déboguer le PDF",
|
||||
"New Document": "Nouveau document",
|
||||
"subscription": "Abonnement",
|
||||
"Draft document": "Brouillon de document",
|
||||
"Draft template": "Brouillon de modèle",
|
||||
"Public sign": "Signature publique",
|
||||
"Signup": "Inscription",
|
||||
"delete-contact": "Supprimer le contact",
|
||||
"total-records-found": "Nombre total d'enregistrements trouvés : {{count}}",
|
||||
"Invalid-records-found": "Enregistrements invalides trouvés : {{records}}",
|
||||
"previous": "Précédent",
|
||||
"page-n-of-n": "Page {{currentPage}} sur {{totalPages}}",
|
||||
"import": "Importer",
|
||||
"search": "Rechercher",
|
||||
"viewed-on": "Consulté le : {{ViewedOn}}",
|
||||
"signed-on": "Signé le : {{SignedOn}}",
|
||||
"hide": "Masquer",
|
||||
"show-more": "Afficher plus",
|
||||
"browse-or-drag-to-replace-existing-file": "Parcourez ou faites glisser un nouveau fichier pour remplacer l'existant",
|
||||
"optional-details": "Détails facultatifs",
|
||||
"hide-optional-details": "masquer les détails facultatifs",
|
||||
"mail-adapter-subscription-alert": "Veuillez passer au plan Professionnel ou Équipe pour configurer un SMTP personnalisé.",
|
||||
"connect-to-mail": "Se connecter à Gmail",
|
||||
"custom-smtp": "SMTP personnalisé",
|
||||
"default-smtp": "SMTP par défaut de {{appName}}",
|
||||
"host": "Hôte",
|
||||
"port": "Port",
|
||||
"sender-email": "Email de l'expéditeur",
|
||||
"username": "Nom d'utilisateur",
|
||||
"use-default-mail-adapter": "Voulez-vous vraiment utiliser les serveurs de messagerie par défaut de {{appName}} pour envoyer vos demandes de signature ? Nous vous recommandons d'utiliser votre propre serveur Gmail ou SMTP pour une meilleure délivrabilité.",
|
||||
"verification-code-sent-registered-email": "Un code de vérification a été envoyé à votre email enregistré <1>{{useremail}}</1>. Veuillez entrer le code ci-dessous pour confirmer vos paramètres.",
|
||||
"smpt-credentials": "Identifiants SMTP",
|
||||
"delete-account": "Supprimer le compte",
|
||||
"delete-account-que": "Êtes-vous sûr de vouloir supprimer votre compte ?",
|
||||
"delete-account-que-user": "Vous êtes sur le point de supprimer définitivement cet utilisateur et toutes les données associées. Cette action est irréversible.",
|
||||
"user-deleted-successfully": "L'utilisateur et toutes les données associées ont été supprimés avec succès.",
|
||||
"account-deletion-request-sent-via-mail": "Nous vous avons envoyé par e-mail un lien de confirmation. Approuvez la demande pour finaliser la suppression de votre compte.",
|
||||
"type-exact-email-delete": "Saisissez l'adresse e-mail exacte pour activer la suppression",
|
||||
"email-does-not-match": "L'adresse e-mail ne correspond pas.",
|
||||
"please-type-to-confirm": "Veuillez saisir <1>{{userEmail}}</1> pour confirmer:",
|
||||
"email-settings": "Paramètres de messagerie",
|
||||
"email-settings-help": "Pour assurer une meilleure délivrabilité dans la boîte de réception de vos e-mails de demande de signature, vous pouvez connecter votre propre fournisseur de messagerie. Choisissez l’une des options suivantes :",
|
||||
"connect-to-gmail": "Se connecter à Gmail: ",
|
||||
"connect-to-gmail-help": "Utilisez votre compte Gmail pour envoyer des e-mails de demande de signature directement depuis votre propre boîte de réception, améliorant ainsi les taux de délivrabilité et la fiabilité.",
|
||||
"connect-to-smtp": "SMTP personnalisé: ",
|
||||
"connect-to-smtp-help": "Utilisez votre propre serveur SMTP pour envoyer des e-mails via votre domaine. Cette option vous donne un contrôle total sur votre infrastructure de messagerie, améliorant la délivrabilité et la cohérence de la marque.",
|
||||
"connect-to-default": "Si vous le souhaitez, vous pouvez également utiliser les serveurs de messagerie par défaut de {{appName}}, bien que nous recommandions d’utiliser les vôtres pour des résultats optimaux.",
|
||||
"email-settings-redirect-message": "Ce paramètre a été déplacé de la console vers Paramètres du menu principal → Préférences. Cette page sera supprimée dans les prochaines versions.",
|
||||
"go-to-preferences-menu": "Aller au menu Préférences",
|
||||
"document-download-filename-format": "Format du nom de fichier pour le téléchargement du document",
|
||||
"preview": "Aperçu : ",
|
||||
"download-filename-format-help": "Choisissez comment les fichiers PDF téléchargés sont nommés. Votre sélection est enregistrée dans votre profil et utilisée dans toute l'application.",
|
||||
"delete-action-prohibited": "Cette action n'est pas autorisée. Veuillez contacter votre administrateur pour demander la suppression du compte.",
|
||||
"not-verified": "Non vérifié",
|
||||
"verified": "Vérifié",
|
||||
"expires": "Expire",
|
||||
"fix-resend-error": "Vous ne pouvez pas corriger et renvoyer ce document car il contient des widgets préremplis.",
|
||||
"duplicate-template-error": "Vous ne pouvez pas dupliquer ce modèle car il contient des widgets préremplis.",
|
||||
"save-as-template-error": "Ce document ne peut pas être enregistré comme modèle car il contient des widgets préremplis.",
|
||||
"redirecting-you-in": "Redirection dans {{redirectTimeLeft}} s...",
|
||||
"pdf-tools-tour": "Cliquez sur ces boutons pour ajouter, supprimer, réorganiser, faire pivoter et zoomer les pages.",
|
||||
"widgets": "Widgets",
|
||||
"prefill-tour": "Utilisez cette option pour saisir des informations à l'avance avant d'envoyer le document aux destinataires.",
|
||||
"empty-prefill-error": "Les champs obligatoires suivants ne peuvent pas être laissés vides :",
|
||||
"please-fill-out": "Veuillez les remplir pour continuer.",
|
||||
"custom-signing-certificate": "Certificat de signature personnalisé",
|
||||
"signing-certificate-help": "Vous pouvez télécharger votre propre certificat de signature de documents, qui sera utilisé pour signer tous vos documents ainsi que les certificats d’achèvement. Le fichier du certificat doit être un certificat P12 au format PFX.",
|
||||
"certificate-file-p12-in-PFX-format": "Fichier de certificat (certificat p12 au format PFX)",
|
||||
"password-of-pfx-file": "Entrez le mot de passe du fichier pfx",
|
||||
"update": "Mettre à jour",
|
||||
"use-default-certificate": "Utiliser le certificat par défaut de {{appName}}",
|
||||
"upgrade-to-team-plan": "Passer au plan Équipe",
|
||||
"setup-file-storage": "Configurer le stockage de fichiers",
|
||||
"save-and-activate": "Enregistrer et activer",
|
||||
"logging-out-to-apply-settings": "Vous êtes déconnecté pour appliquer les nouveaux paramètres",
|
||||
"reconnect-to-default": "Se reconnecter à {{appName}}",
|
||||
"active-file-adapter": "Adaptateur de fichiers actif",
|
||||
"file-adapter-unique-name": "Nom unique de l'adaptateur de fichiers",
|
||||
"unique-name-for-file-adapter": "Entrez un nom unique pour l'adaptateur de fichiers",
|
||||
"storage-provider": "Fournisseur de stockage",
|
||||
"enter-bucket-name": "Entrez le nom du bucket",
|
||||
"enter-space-name": "Entrez le nom de l'espace",
|
||||
"enter-region-of-bucket": "Entrez la région du bucket",
|
||||
"enter-region-of-space": "Entrez la région de l'espace",
|
||||
"enter-access-key": "Entrez la clé d'accès",
|
||||
"enter-secret-access-key": "Entrez la clé d'accès secrète",
|
||||
"otp-email": "Nous avons envoyé un code de vérification",
|
||||
"save-as-temp-warn": "Remarque : Ce document contient des widgets préremplis, qui seront automatiquement supprimés car ils sont déjà intégrés dans le document de base.",
|
||||
"edit-document": "Modifier le document",
|
||||
"modify": "Modifier",
|
||||
"merge-certificate-to-pdf": "Fusionner le certificat avec le PDF",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "Cela garantira que le certificat de finalisation est inclus dans le document PDF final. Cependant, veuillez noter qu'une fois fusionné, le certificat ne peut plus être séparé du document principal.",
|
||||
"p2": "Si vous choisissez de ne pas fusionner, le certificat de finalisation sera fourni sous forme de fichier PDF distinct accompagné du document signé."
|
||||
},
|
||||
"read-only-date-error": "Le widget de date en lecture seule doit avoir une date par défaut.",
|
||||
"set-date": "Définir la date",
|
||||
"set-today": "Date de signature",
|
||||
"enter-name": "Saisir le nom",
|
||||
"enter-email": "Saisir l'e-mail",
|
||||
"subscribe-to-opensign-msg": "Abonnez-vous à {{appName}} et profitez de signatures numériques gratuites illimitées.",
|
||||
"duplicate-subscribe-msg": "Dupliquer vous permet de créer une copie exacte du modèle sélectionné, que vous pouvez réutiliser ou modifier sans affecter l'original.",
|
||||
"save-as-template-msg": "Enregistrez ce document comme modèle réutilisable que vous pourrez utiliser à nouveau pour de futurs documents.",
|
||||
"public-credit-alert": "Signer via ce lien consomme vos crédits e-mail gratuits. En tant qu'utilisateur gratuit, vous disposez de 15 crédits e-mail par mois afin d’empêcher les spammeurs d’abuser de nos systèmes. Pour profiter de limites plus élevées et d'un accès ininterrompu, abonnez-vous aux forfaits payants de <1>OpenSign™.</1>",
|
||||
"know-more-about": "En savoir plus sur",
|
||||
"free-unlimited-signatures": "Signatures illimitées gratuites",
|
||||
"email-quota-updated": "Quota quotidien d'e-mails mis à jour avec succès.",
|
||||
"password-has-been-reset": "Le mot de passe a été réinitialisé.",
|
||||
"Unauthorized to reset your own password.": "Non autorisé à réinitialiser votre propre mot de passe.",
|
||||
"User not found or not allowed.": "Utilisateur introuvable ou non autorisé.",
|
||||
"User not found.": "Utilisateur introuvable.",
|
||||
"Admin user tenant not found.": "Locataire administrateur introuvable.",
|
||||
"User is not authenticated.": "L'utilisateur n'est pas authentifié.",
|
||||
"Please provide required parameters.": "Veuillez fournir les paramètres requis.",
|
||||
"reset-password": "Réinitialiser le mot de passe utilisateur",
|
||||
"enter-strong-password": "Entrez un mot de passe sécurisé ou générez-en un automatiquement.",
|
||||
"autogenerate": "Générer automatiquement",
|
||||
"extend": "Prolonger",
|
||||
"console-general-help": "Pour permettre aux utilisateurs individuels de personnaliser leurs propres modèles d'e-mails, accédez à",
|
||||
"console-mail-help": "Pour mettre à jour les modèles d'e-mails à l'échelle de l'entreprise, allez dans",
|
||||
"admin-setting-disabled": "Ce paramètre est désactivé. Veuillez contacter l'administrateur pour obtenir de l'aide.",
|
||||
"company-email-settings": "Paramètres de messagerie de l'entreprise",
|
||||
"allow-email-template-individually": "Autoriser la personnalisation individuelle des modèles d'e-mails pour chaque utilisateur ",
|
||||
"user-email-template-general-help": "Si cette option est désactivée, les utilisateurs ne pourront plus personnaliser leurs modèles d'e-mails.",
|
||||
"disable-individual-user-smtp": "désactiver les paramètres smtp individuels de l'utilisateur",
|
||||
"enable-individual-user-smtp": "activer les paramètres smtp individuels de l'utilisateur",
|
||||
"user-smtp-general-help": "Lorsqu'elle est désactivée, les utilisateurs ne pourront pas configurer leur propre SMTP. Tous les e-mails utiliseront la configuration SMTP globale.",
|
||||
"global-smtp-settings": "Paramètres SMTP globaux",
|
||||
"global-smtp-settings-help-p1": "Ces paramètres s'appliquent globalement à l'ensemble de votre compte {{appName}}. Si un utilisateur n'a pas configuré ses propres paramètres de messagerie, le système utilisera automatiquement les paramètres SMTP globaux définis ici.",
|
||||
"global-smtp-settings-help-p2": "Pour assurer une meilleure délivrabilité de toutes les demandes de signature, vous pouvez connecter votre fournisseur de messagerie préféré. Choisissez l'une des options suivantes :",
|
||||
"global-connect-to-gmail-help": "Utilisez un compte Gmail pour envoyer des demandes de signature directement depuis la boîte de réception de votre organisation. Cela améliore la délivrabilité, la réputation et la fiabilité.",
|
||||
"global-connect-to-smtp-help": "Utilisez votre propre serveur SMTP pour envoyer des e-mails via votre domaine. Cela offre un contrôle total sur votre infrastructure de messagerie, améliore la délivrabilité et garantit une cohérence de marque.",
|
||||
"global-smtp-settings-help-p3": "Si aucun paramètre utilisateur individuel n'est configuré, ces paramètres globaux seront appliqués automatiquement."
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "नाम",
|
||||
"Date": "दिनांक"
|
||||
},
|
||||
"folder": "फ़ोल्डर",
|
||||
"pdf": "पीडीएफ़",
|
||||
"context-menu": {
|
||||
"Download": "डाउनलोड करें",
|
||||
"Rename": "नाम बदलें",
|
||||
@@ -44,6 +46,10 @@
|
||||
"contact-now": "अभी संपर्क करें",
|
||||
"upgrade-to": "इसमें अपग्रेड करें",
|
||||
"plan": "योजना",
|
||||
"connect": "कनेक्ट करें",
|
||||
"connect-to-g-drive": "Google Drive से कनेक्ट करें",
|
||||
"reconnect-to-g-drive": "Google Drive से फिर से कनेक्ट करें",
|
||||
"gdrive-info-connect": "जब Google Drive जुड़ा होता है, तो पूर्ण दस्तावेज़ {{appName}} फ़ोल्डर में Google Drive पर सहेजा जाएगा।",
|
||||
"subscription-renew-warning": "आपकी सदस्यता {{remainingDays}} दिनों में समाप्त हो जाएगी। कृपया अपनी सदस्यता नवीनीकृत करें।",
|
||||
"subscribe-card-teamplan": "सहयोग की पूरी शक्ति को अनलॉक करें! असीमित संगठन, टीम और पदानुक्रम बनाएं। टीमों में टेम्पलेट को निर्बाध रूप से साझा करें और कस्टम उपयोगकर्ता भूमिकाएँ असाइन करें। आज ही अपने वर्कफ़्लो को उन्नत करें!",
|
||||
"subscribe-card-plan": "केवल {{premiumPrice}}/माह से शुरू होने वाली प्रीमियम सुविधाओं को अनलॉक करें। अपने शामिल प्रीमियम क्रेडिट के बाद प्रति अतिरिक्त क्रेडिट केवल {{addonPrice}} पर बढ़ी हुई प्रदर्शन का आनंद लें।",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "वापसी पर स्वागत है!",
|
||||
"Login-to-your-account": "अपने खाते में प्रवेश करे",
|
||||
"password": "पासवर्ड",
|
||||
"forgot-password": "पासवर्ड भूल गए?",
|
||||
"forgot-password": "पासवर्ड भूल गए",
|
||||
"loading": "लोड हो रहा है...",
|
||||
"of": "का",
|
||||
"sign-SSO": "SSO के साथ साइन इन करें",
|
||||
"login-page": "लॉगिन पृष्ठ",
|
||||
"or": "या",
|
||||
"signup-page": "साइनअप पृष्ठ",
|
||||
"password-match-length": "नया पासवर्ड और पुष्टि पासवर्ड मेल खाना चाहिए",
|
||||
"password-length": "पासवर्ड 8 वर्ण लंबा होना चाहिए",
|
||||
"password-case": "पासवर्ड में अपरकेस अक्षर, लोअरकेस अक्षर, अंक होना चाहिए",
|
||||
"password-special-char": " पासवर्ड में विशेष वर्ण होना चाहिए",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "टीमें",
|
||||
"Team-update-successfully": "टीम सफलतापूर्वक अपडेट की गई।",
|
||||
"Users": "उपयोगकर्ता"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "ग्लोबल SMTP"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "आपके हस्ताक्षर की आवश्यकता है",
|
||||
@@ -159,7 +167,8 @@
|
||||
"daily-mail-quota": "दैनिक ईमेल कोटा",
|
||||
"Save as template": "टेम्पलेट के रूप में सहेजें",
|
||||
"Fix & resend": "ठीक करें और पुनः भेजें",
|
||||
"Kiosk Mode": "कियोस्क मोड"
|
||||
"Kiosk Mode": "कियोस्क मोड",
|
||||
"Reset password": "पासवर्ड रीसेट करें"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "क्रमांक",
|
||||
@@ -181,7 +190,20 @@
|
||||
"created-date": "निर्माण तिथि",
|
||||
"Type": "प्रकार",
|
||||
"Logs": "लॉग",
|
||||
"Expiry-date": "समाप्ति तिथि"
|
||||
"Expiry-date": "समाप्ति तिथि",
|
||||
"Company": "कंपनी",
|
||||
"JobTitle": "पद",
|
||||
"Time to complete (Days)": "पूरा करने का समय (दिन)",
|
||||
"Auto reminder": "स्वचालित अनुस्मारक",
|
||||
"Remind once in every (Days)": "हर (दिन) में एक बार याद दिलाएं",
|
||||
"Enable OTP verification": "OTP सत्यापन सक्षम करें",
|
||||
"Enable Tour": "टूर सक्षम करें",
|
||||
"Notify on signatures": "हस्ताक्षर पर सूचित करें",
|
||||
"Allow modifications": "संशोधन की अनुमति दें",
|
||||
"Redirect url": "रीडायरेक्ट URL",
|
||||
"Created Date": "निर्माण तिथि",
|
||||
"Updated Date": "अद्यतन तिथि",
|
||||
"Expiry Date": "समाप्ति तिथि"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
|
||||
@@ -193,16 +215,15 @@
|
||||
"Contactbook": "यह आपके द्वारा जोड़े गए संपर्कों/हस्ताक्षरकर्ताओं की सूची है। जब आप किसी नए दस्तावेज़ में हस्ताक्षरकर्ताओं को जोड़ने का प्रयास करेंगे तो ये सुझावों के रूप में दिखाई देंगे।",
|
||||
"Templates": "यह उन टेम्पलेट्स की सूची है जो दस्तावेज़ बनाने के लिए आपके लिए उपलब्ध हैं। आप टेम्पलेट का उपयोग करके एक नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक कर सकते हैं, दस्तावेज़ को संशोधित कर सकते हैं और अगले चरण में हस्ताक्षरकर्ताओं को जोड़ सकते हैं।"
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "स्वयं हस्ताक्षर करें",
|
||||
"Request Signatures": "हस्ताक्षर का अनुरोध करें",
|
||||
"New Template": "नया टेम्पलेट"
|
||||
},
|
||||
"Sign Yourself": "स्वयं हस्ताक्षर करें",
|
||||
"Request Signatures": "हस्ताक्षर का अनुरोध करें",
|
||||
"New Template": "नया टेम्पलेट",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "फ़ाइल चयनित",
|
||||
"template-title": "टेम्पलेट शीर्षक",
|
||||
"document-title": "दस्तावेज़ शीर्षक",
|
||||
"title": "शीर्षक",
|
||||
"description": "विवरण",
|
||||
"time-to-complete": "पूरा करने का समय (दिन)",
|
||||
"send-in-order": "क्रम से भेजें",
|
||||
@@ -367,7 +388,8 @@
|
||||
"dropdown": "ड्रॉपडाउन",
|
||||
"radio button": "रेडियो बटन",
|
||||
"image": "छवि",
|
||||
"email": "ईमेल"
|
||||
"email": "ईमेल",
|
||||
"number": "संख्या"
|
||||
},
|
||||
"fields": "क्षेत्र",
|
||||
"recipients": "प्राप्तकर्ता",
|
||||
@@ -379,6 +401,7 @@
|
||||
"certificate": "प्रमाण पत्र",
|
||||
"decline": "अस्वीकार करें",
|
||||
"finish": "समाप्त करें",
|
||||
"done": "हो गया",
|
||||
"mail": "मेल",
|
||||
"sign-now": "अभी हस्ताक्षर करें",
|
||||
"successfully-signed": "सफलतापूर्वक हस्ताक्षर किए गए!",
|
||||
@@ -388,6 +411,10 @@
|
||||
"Email-verified-alert-1": "ईमेल सत्यापित है।",
|
||||
"Email-verified-alert-2": "ईमेल पहले से ही सत्यापित है।",
|
||||
"upload-stamp-image": "मोहर छवि अपलोड करें",
|
||||
"draw-signature": "हस्ताक्षर बनाएं",
|
||||
"draw-initials": "इनिशियल्स बनाएं",
|
||||
"enter-text": "पाठ दर्ज करें",
|
||||
"enter-widgettype": "{{widgetType}} दर्ज करें",
|
||||
"draw": "आरेखित करें",
|
||||
"type": "टाइप",
|
||||
"color-type": {
|
||||
@@ -419,6 +446,19 @@
|
||||
"maximum-check": "अधिकतम जाँच",
|
||||
"cell-count": "सेल्स संख्या",
|
||||
"default-value": "डिफ़ॉल्ट मान",
|
||||
"formula": "सूत्र",
|
||||
"formula-placeholder": "संख्या विजेट्स का उपयोग करके एक सूत्र बनाएं",
|
||||
"formula-no-widgets": "इस हस्ताक्षरकर्ता को अभी तक कोई अन्य संख्या विजेट असाइन नहीं किया गया है।",
|
||||
"supported-operators": "समर्थित ऑपरेटर:",
|
||||
"formula-helper": "सूची में से संख्या विजेट चिप पर क्लिक करके उसे जोड़ें (उदा. {{number-1}} + {{number-2}})।",
|
||||
"formula-error-letter": "विजेट नामों को दोहरी घुंघराली ब्रैकेट में रखें (उदा. {{number-1}})।",
|
||||
"formula-error-characters": "ब्रैकेट के बाहर केवल संख्याएँ और +, -, *, /, (, ) की अनुमति है।",
|
||||
"formula-error-braces": "हर खुलने वाली {{ के लिए एक मिलान करने वाली }} होना चाहिए।",
|
||||
"formula-error-operators": "ऑपरेटर या कोष्ठक का उपयोग करते समय कम से कम दो मान जोड़ें।",
|
||||
"formula-error-leading-operator": "सूत्र ऑपरेटर से शुरू नहीं हो सकते।",
|
||||
"decimal-places": "दशमलव स्थान",
|
||||
"number-of-decimal-places": "दशमलव स्थानों की संख्या",
|
||||
"decimal-places-helper": "पूर्णांक के लिए 0 पर सेट करें",
|
||||
"select": "चुनें",
|
||||
"read-only": "सिर्फ पढ़ने के लिए है",
|
||||
"hide-labels": "लेबल छिपाएँ",
|
||||
@@ -465,13 +505,13 @@
|
||||
"add-recipients": "प्राप्तकर्ता जोड़ें",
|
||||
"loading-mssg": "इसमें कुछ समय लग सकता है",
|
||||
"send-mail": "मेल भेजें",
|
||||
"signature-field-widget": "प्रत्येक उपयोगकर्ता के लिए कम से कम एक हस्ताक्षर फ़ील्ड जोड़ा जाना चाहिए। आपने {{signersName}} के लिए हस्ताक्षर फ़ील्ड नहीं जोड़े हैं",
|
||||
"signature-field-widget": "प्रत्येक उपयोगकर्ता के पास कम से कम एक हस्ताक्षर विजेट होना चाहिए। आपने {{signersName}} के लिए हस्ताक्षर विजेट नहीं जोड़ा है।",
|
||||
"placeholder-alert-1": "कृपया सुनिश्चित करें कि सभी प्राप्तकर्ताओं के लिए कम से कम एक हस्ताक्षर विजेट जोड़ा गया है।",
|
||||
"placeholder-alert-2": "कृपया पुष्टि करें कि आपने टेक्स्ट फ़ील्ड भर दिया है।",
|
||||
"placeholder-alert-3": " क्या आप वाकई इस दस्तावेज़ को हस्ताक्षर के लिए भेजना चाहते हैं?",
|
||||
"placeholder-alert-4": "आपने सभी प्राप्तकर्ताओं को सफलतापूर्वक मेल भेज दिए हैं!",
|
||||
"placeholder-mail-alert": "आपने {{name}} को सफलतापूर्वक ईमेल भेज दिया है। {{name}} द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे",
|
||||
"placeholder-mail-alert-you": "आपके द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे।",
|
||||
"placeholder-mail-alert-you": "आपके दस्तावेज़ पर हस्ताक्षर करने के बाद अगले हस्ताक्षरकर्ता को ईमेल प्राप्त होगा।",
|
||||
"placeholder-alert-5": "क्या आप अभी दस्तावेज़ पर हस्ताक्षर करना चाहते हैं?",
|
||||
"placeholder-alert-6": "मेल भेजने के लिए कृपया मेल एडॉप्टर सेटअप करें!",
|
||||
"placeholder-alert-7": "प्लेसहोल्डर जोड़ने के लिए कृपया हस्ताक्षरकर्ता चुनें!",
|
||||
@@ -600,7 +640,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "आप सफलतापूर्वक लॉग इन हो गए हैं! आइए एक नज़र डालते हैं।",
|
||||
"home-layout-2": "स्व-हस्ताक्षर के लिए दस्तावेज़ अपलोड करने या दूसरों के हस्ताक्षर का अनुरोध करने के लिए, बस संबंधित बटन चुनें।",
|
||||
"home-layout-3": "आप {{appName}} का उपयोग शुरू करने के लिए तैयार हैं! यदि आपको सहायता की आवश्यकता है तो हमसे संपर्क करने में संकोच न करें।",
|
||||
"home-layout-3": "आप {{appName}} का उपयोग शुरू करने के लिए तैयार हैं!",
|
||||
"home-layout-4": "हमें स्टार दें",
|
||||
"generate-token": "उत्पादन एपीआई टोकन उत्पन्न करने के लिए अभी अपग्रेड करें।",
|
||||
"opensign-drive-1": "फ़ोल्डर पदानुक्रम के माध्यम से आसानी से नेविगेट करने और प्रत्येक फ़ोल्डर के भीतर दस्तावेज़ देखने के लिए ब्रेडक्रंब लिंक पर क्लिक करें।",
|
||||
"opensign-drive-2": "नया फ़ोल्डर या दस्तावेज़ बनाने के लिए जोड़ें बटन पर क्लिक करें।",
|
||||
@@ -610,34 +651,32 @@
|
||||
"opensign-drive-6": "डाउनलोड, नाम बदलें, स्थानांतरित करें और हटाएं जैसे विकल्प देखने के लिए किसी दस्तावेज़ पर राइट-क्लिक करें। इसे खोलने के लिए दस्तावेज़ पर क्लिक करें।",
|
||||
"opensign-drive-7": "विकल्प देखने के लिए किसी भी फ़ोल्डर पर राइट-क्लिक करें। फ़ोल्डर का नाम बदलने के लिए 'नाम बदलें' चुनें या इसकी सामग्री के माध्यम से नेविगेट करने के लिए फ़ोल्डर पर क्लिक करें।",
|
||||
"pdf-request-file-1": "उन हस्ताक्षरकर्ताओं की सूची जिन्हें अभी भी दस्तावेज़ पर हस्ताक्षर करने की आवश्यकता है।",
|
||||
"pdf-request-file-2": "हस्ताक्षर करने के लिए दस्तावेज़ पर दिखाई देने वाले किसी भी प्लेसहोल्डर पर क्लिक करें। फिर आपको अपना हस्ताक्षर खींचने, टाइप करने या एक छवि अपलोड करने के विकल्प दिखाई देंगे।",
|
||||
"pdf-request-file-3": "अपने दस्तावेज़ को नेविगेट करने के लिए अस्वीकार करें, या समाप्त करें बटन पर क्लिक करें। डाउनलोड बटन सहित अतिरिक्त विकल्पों के लिए दीर्घवृत्त मेनू का उपयोग करें।",
|
||||
"pdf-request-file-2": "शुरू करने के लिए दस्तावेज़ में दिखाई देने वाले किसी भी फ़ील्ड पर क्लिक करें। उसके बाद आप आवश्यक जानकारी भर सकेंगे।",
|
||||
"pdf-request-file-3": "एक बार जब आप सभी आवश्यक फ़ील्ड भर लें, तो 'समाप्त करें' पर क्लिक करें। उसके बाद आप हस्ताक्षरित दस्तावेज़ डाउनलोड कर सकेंगे। प्रेषक की सेटिंग्स के आधार पर, जब सभी प्राप्तकर्ता हस्ताक्षर कर लेंगे, तो आपको पूर्ण दस्तावेज़ की एक प्रति और पूर्णता प्रमाणपत्र प्राप्त हो सकता है।",
|
||||
"pdf-request-file-4": "उन हस्ताक्षरकर्ताओं की सूची जिन्होंने पहले ही दस्तावेज़ पर हस्ताक्षर कर दिए हैं।",
|
||||
"pdf-request-file-5": "आप उन सभी स्थानों पर स्वचालित रूप से हस्ताक्षर करने के लिए 'सभी पर स्वतः हस्ताक्षर करें' पर क्लिक कर सकते हैं, जिन पर आपके द्वारा हस्ताक्षर किए जाने हैं। सुनिश्चित करें कि आप इस बटन पर क्लिक करने से पहले दस्तावेज़ की ठीक से समीक्षा कर लें।",
|
||||
"pdf-request-file-6": "कृपया पृष्ठ संख्या {{pagenumbers}} पर फ़ील्ड पूरी करें, सभी आसान पहचान के लिए एक ही रंग में हाइलाइट किए गए हैं।",
|
||||
"placeholder-sign-1": "एक प्लेस-होल्डर जोड़ने के लिए इस सूची से एक प्राप्तकर्ता का चयन करें जहां उसे हस्ताक्षर करना है। प्लेसहोल्डर दस्तावेज़ पर छोड़ने के बाद प्राप्तकर्ता के नाम के समान रंग में दिखाई देगा।",
|
||||
"placeholder-sign-1": "इस सूची से एक प्राप्तकर्ता चुनें ताकि विजेट जोड़ सकें। दस्तावेज़ पर रखने के बाद विजेट उसी रंग में दिखाई देगा जो प्राप्तकर्ता के नाम का है।",
|
||||
"placeholder-sign-2": "'प्राप्तकर्ता जोड़ें' बटन पर क्लिक करने से आप अधिक हस्ताक्षरकर्ताओं को जोड़ सकेंगे।",
|
||||
"placeholder-sign-3": "दस्तावेज़ में अधिक प्राप्तकर्ताओं/हस्ताक्षरकर्ताओं को जोड़ने के लिए इस बटन पर क्लिक करें।",
|
||||
"placeholder-sign-4": "इसे दस्तावेज़ में जोड़ने के लिए किसी फ़ील्ड पर खींचें या क्लिक करें।",
|
||||
"placeholder-sign-5": "पीडीएफ सामग्री क्षेत्र पहले से ही टेम्पलेट के मौजूदा प्लेसहोल्डर प्रदर्शित करता है। आपकी सुविधा के लिए, ये प्लेसहोल्डर प्राप्तकर्ता के नाम के रंग से मेल खाएंगे, जिससे उन्हें आसानी से पहचाना जा सकेगा।",
|
||||
"placeholder-sign-4": "दस्तावेज़ में जोड़ने के लिए विजेट को खींचें या उस पर क्लिक करें।",
|
||||
"placeholder-sign-5": "PDF सामग्री क्षेत्र पहले से ही टेम्पलेट के मौजूदा विजेट दिखाता है। आपकी सुविधा के लिए, ये विजेट प्राप्तकर्ता के नाम के रंग से मेल खाएँगे ताकि वे आसानी से पहचाने जा सकें।",
|
||||
"placeholder-sign-6": "'अगला' पर क्लिक करने से दस्तावेज़ सहेजा जाएगा। अगले चरण में आप प्राप्तकर्ताओं को भेजे जाने वाले ईमेल को अनुकूलित कर सकते हैं या हस्ताक्षर लिंक कॉपी कर सकते हैं और उन्हें स्वयं प्राप्तकर्ताओं के साथ साझा कर सकते हैं।",
|
||||
"report-1": "नया टेम्पलेट बनाने के लिए 'जोड़ें' बटन पर क्लिक करें। टेम्पलेट पुन: प्रयोज्य दस्तावेज़ हैं जिन्हें समान संरचना और अलग-अलग हस्ताक्षरकर्ताओं के साथ नए दस्तावेज़ों को शीघ्रता से उत्पन्न करने के लिए डिज़ाइन किया गया है। उदाहरण के लिए, ऑनबोर्डिंग के लिए एक मानव संसाधन टेम्पलेट में 'मानव संसाधन प्रबंधक' और 'नया कर्मचारी' जैसी पूर्वनिर्धारित भूमिकाएँ हो सकती हैं। हर बार जब आप टेम्पलेट का उपयोग करते हैं, तो आप विभिन्न आने वाले कर्मचारियों को 'नया कर्मचारी' भूमिका सौंप सकते हैं, जबकि 'मानव संसाधन प्रबंधक' भूमिका स्थिर रहती है, जिससे प्रत्येक भर्ती के लिए एक सहज ऑनबोर्डिंग प्रक्रिया सुविधाजनक होती है। ",
|
||||
"redirect": "किसी मौजूदा टेम्पलेट से नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक करें।",
|
||||
"bulksend": "केवल प्राप्तकर्ता ईमेल पते बनाकर किसी मौजूदा टेम्पलेट का उपयोग करके कई दस्तावेज़ों को शीघ्रता से भेजने के लिए, 'थोक भेजें' बटन पर क्लिक करें।",
|
||||
"option": "यह मेनू संपादन और हटाएं जैसे अधिक विकल्प प्रकट करता है। हस्ताक्षरकर्ता भूमिकाएं जोड़ने, फ़ील्ड संशोधित करने और अपने टेम्पलेट को अपडेट करने के लिए 'संपादित करें' बटन का उपयोग करें। परिवर्तन इस टेम्पलेट से बनाए गए सभी भविष्य के दस्तावेज़ों पर लागू होंगे लेकिन मौजूदा दस्तावेज़ों को प्रभावित नहीं करेंगे। हटाएं बटन का उपयोग करके आप टेम्पलेट हटा सकते हैं। ",
|
||||
"signyour-self-1": "हस्ताक्षर करने से पहले अपने दस्तावेज़ को अनुकूलित करने के लिए अपने पसंदीदा विजेट चुनें और उन्हें पीडीएफ पर खींचें। अपनी आवश्यकताओं के अनुसार दस्तावेज़ को अनुकूलित करने के लिए प्रत्येक संशोधन के लिए सही स्थान चुनें।",
|
||||
"bulksend": "किसी मौजूदा टेम्पलेट का उपयोग करके कई दस्तावेज़ जल्दी भेजने के लिए, केवल प्राप्तकर्ताओं के ईमेल पते दर्ज करें और 'बल्क सेंड' बटन पर क्लिक करें। आप अधिकतम 50 प्राप्तकर्ताओं को भेज सकते हैं।",
|
||||
"option": "इस मेनू में और विकल्प दिखाई देंगे जैसे संपादित करें, हटाएं, नाम बदलें, डुप्लिकेट करें, साझा करें आदि। <1>यहां क्लिक करें</1> सभी उपलब्ध विकल्पों के बारे में और पढ़ने के लिए। <3>नोट: किसी मौजूदा टेम्पलेट में किए गए बदलाव उस टेम्पलेट से बनाए गए सभी भविष्य के दस्तावेज़ों पर लागू होंगे, लेकिन पहले से भेजे गए दस्तावेज़ों पर प्रभाव नहीं डालेंगे।</3>",
|
||||
"signyour-self-1": "हस्ताक्षर करने से पहले अपने दस्तावेज़ को अनुकूलित करने के लिए अपनी पसंद के विजेट चुनें या उन्हें PDF पर खींचें। अपनी आवश्यकता के अनुसार प्रत्येक विजेट को सही स्थान पर रखें।",
|
||||
"signyour-self-2": "इस क्षेत्र में कहीं भी खींचें और छोड़ें। आप इसे बाद में आकार बदल सकते हैं और स्थानांतरित कर सकते हैं।",
|
||||
"template-placeholder-1": "'भूमिका जोड़ें' बटन पर क्लिक करने से आप विभिन्न हस्ताक्षरकर्ता भूमिकाएँ जोड़ सकेंगे। आप बाद के चरणों में प्रत्येक भूमिका में उपयोगकर्ताओं को संलग्न कर सकते हैं।",
|
||||
"template-placeholder-2": "भूमिकाएँ जोड़ने के बाद, सूची से एक भूमिका चुनें ताकि एक प्लेस-होल्डर जोड़ा जा सके जहाँ उसे हस्ताक्षर करना है। प्लेसहोल्डर दस्तावेज़ पर छोड़ने के बाद भूमिका के नाम के समान रंग में दिखाई देगा।",
|
||||
"template-placeholder-3": "इसे दस्तावेज़ में जोड़ने के लिए किसी फ़ील्ड पर खींचें या क्लिक करें।",
|
||||
"template-placeholder-4": "किसी भूमिका के लिए प्लेसहोल्डर को दस्तावेज़ पर कहीं भी खींचें। याद रखें, यह आसान संदर्भ के लिए प्राप्तकर्ता के नाम के समान रंग में दिखाई देगा।",
|
||||
"template-placeholder-5": "'अगला' पर क्लिक करने से वर्तमान टेम्पलेट संग्रहीत हो जाएगा। सहेजने के बाद, यदि आप चाहें तो इस टेम्पलेट से एक नया दस्तावेज़ बनाने के लिए आपको संकेत दिया जाएगा।",
|
||||
"template-placeholder-2": "भूमिकाएँ जोड़ने के बाद, सूची में से एक चुनें ताकि उस प्राप्तकर्ता के लिए विजेट रखा जा सके। आप विजेट पर क्लिक कर सकते हैं या उसे दस्तावेज़ पर खींच सकते हैं। एक बार रखे जाने के बाद, विजेट चयनित भूमिका के समान रंग में दिखाई देगा।",
|
||||
"template-placeholder-3": "'अगला' पर क्लिक करने से वर्तमान टेम्पलेट संग्रहीत हो जाएगा। सहेजने के बाद, यदि आप चाहें तो इस टेम्पलेट से एक नया दस्तावेज़ बनाने के लिए आपको संकेत दिया जाएगा।",
|
||||
"webhook-1": "वेबहूक सेट करने के लिए अभी अपग्रेड करें",
|
||||
"Need your Signature": "इस कार्ड पर क्लिक करने से आप अपनी समीक्षा की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।",
|
||||
"Out for signatures": "इस कार्ड पर क्लिक करने से आप हस्ताक्षर की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।",
|
||||
"Recent signature requests": "यह उन दस्तावेज़ों की सूची है जो आपके हस्ताक्षर की प्रतीक्षा कर रहे हैं।",
|
||||
"Recently sent for signatures": "यह उन दस्तावेज़ों की सूची है जिन्हें आपने हस्ताक्षर के लिए अन्य पक्षों को भेजा है।",
|
||||
"Drafts": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
|
||||
"Drafts": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
|
||||
"public-template": "यह वीडियो दर्शाता है कि आप अपना व्यक्तिगत सार्वजनिक प्रोफ़ाइल कैसे सेट अप कर सकते हैं, जैसे 'https://opensign.me/your-username'। आप यह भी सीखेंगे कि अपनी टैगलाइन को कैसे अनुकूलित करें और अपने टेम्पलेट्स को सार्वजनिक हस्ताक्षर के लिए उपलब्ध कराएं।",
|
||||
"allowModify-widgets": "आप इन फ़ील्ड में से किसी को भी दस्तावेज़ पर खींच और छोड़ सकते हैं, इसके अतिरिक्त दस्तावेज़ निर्माता द्वारा आपके लिए पहले से निर्दिष्ट फ़ील्ड के। "
|
||||
},
|
||||
@@ -728,7 +767,7 @@
|
||||
"public-tour-message": "साझा करने योग्य लिंक उत्पन्न करने से पहले टेम्पलेट को सार्वजनिक होना चाहिए।",
|
||||
"add-user-template": "इसके लिए फ़ील्ड जोड़ने से पहले आपको एक भूमिका जोड़ने की आवश्यकता है।",
|
||||
"pdf-uncompatible": "यह पीडीएफ संगत नहीं है, कृपया {{appName}} से संपर्क करें",
|
||||
"text-field-tour": "दस्तावेज़ भेजे जाने से पहले 'टेक्स्ट' प्रकार के फ़ील्ड पहले से भरे जाने चाहिए। यदि आपको हस्ताक्षरकर्ताओं से इनपुट प्रदान करने की आवश्यकता है, तो इसके बजाय 'टेक्स्ट इनपुट' फ़ील्ड का उपयोग करें।",
|
||||
"text-field-tour": "दस्तावेज़ सबमिट करने से पहले 'प्रीफ़िल' फ़ील्ड को पहले से भरना होगा। यदि आपको हस्ताक्षरकर्ताओं से इनपुट की आवश्यकता है, तो इसके बजाय हस्ताक्षरकर्ता फ़ील्ड का उपयोग करें।",
|
||||
"attach-signer-tour": "आपको प्रत्येक भूमिका में एक हस्ताक्षरकर्ता संलग्न करने की आवश्यकता है। आप इस आइकन पर क्लिक करके ऐसा कर सकते हैं। एक बार जब आप एक हस्ताक्षरकर्ता का चयन कर लेते हैं तो यह उस भूमिका से जुड़े सभी फ़ील्ड से जुड़ जाएगा जो एक ही रंग में दिखाई देते हैं।",
|
||||
"allowed-signature-types": "अनुमत हस्ताक्षर प्रकार",
|
||||
"at-least-one-signature-type": "कम से कम एक हस्ताक्षर प्रकार सक्षम होना चाहिए।",
|
||||
@@ -776,7 +815,7 @@
|
||||
"agree-p1": "मैं पुष्टि करता हूं कि मैंने पढ़ लिया है और समझ लिया है ",
|
||||
"agree-p2": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण",
|
||||
"agree-p3": "और इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर का उपयोग करने के लिए सहमति।",
|
||||
"agrre-button": " सहमत हूँ और जारी रखें",
|
||||
"agrre-button": "मैं पुष्टि करता हूँ और आगे बढ़ने के लिए सहमत हूँ",
|
||||
"term-cond-title": "नियम और शर्तें",
|
||||
"term-cond-h": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण",
|
||||
"term-cond-p1": "यह इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण ('प्रकटीकरण') दस्तावेज़ निर्माता ('प्रेषक') और हस्ताक्षरकर्ता ('आप') के बीच एक समझौता है, जिसे {{appName}} प्लेटफ़ॉर्म ('प्लेटफ़ॉर्म') के माध्यम से सुगम बनाया गया है। {{appName}} के माध्यम से दस्तावेज़ों पर हस्ताक्षर करके, आप इस प्रकटीकरण में उल्लिखित शर्तों से सहमत होते हैं। कृपया आगे बढ़ने से पहले इसे ध्यान से पढ़ें।",
|
||||
@@ -879,6 +918,10 @@
|
||||
"thanks-for-feedback": "आपकी प्रतिक्रिया के लिए धन्यवाद 🙏",
|
||||
"share-your-feedback": "अपनी प्रतिक्रिया साझा करें",
|
||||
"share-your-review": "अपनी समीक्षा साझा करें",
|
||||
"please-select-rating": "कृपया एक रेटिंग चुनें",
|
||||
"feedback-optional": "प्रतिक्रिया (वैकल्पिक)",
|
||||
"feedback-saved": "प्रतिक्रिया सहेज ली गई है।",
|
||||
"feedback-save-error": "प्रतिक्रिया सहेजी नहीं जा सकी, कृपया पुनः प्रयास करें।",
|
||||
"date-format": "दिनांक प्रारूप",
|
||||
"document-deleted": "दस्तावेज़ हटा दिया गया है या आपके पास पहुंच नहीं है। कृपया प्रेषक से संपर्क करें।",
|
||||
"save-as-template-?": "क्या आप वाकई इस दस्तावेज़ को टेम्पलेट के रूप में सहेजना चाहते हैं?",
|
||||
@@ -1032,14 +1075,204 @@
|
||||
"not-calculated": "गणना नहीं की गई",
|
||||
"not-found-in-signature": "हस्ताक्षर में नहीं मिला",
|
||||
"readonly-error": "रीड-ओनली {{widgetName}} विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।",
|
||||
"choose-one":"एक का चयन",
|
||||
"choose-one": "एक का चयन",
|
||||
"search-templates": "टेम्पलेट खोजें…",
|
||||
"search-documents": "दस्तावेज़ खोजें…",
|
||||
"search-contacts": "संपर्क खोजें…",
|
||||
"add-role-alert": "कृपया कम से कम एक भूमिका जोड़ें",
|
||||
"edit-draft": "ड्राफ्ट संपादित करें",
|
||||
"invalid-email-found": "अमान्य ईमेल पाया गया: {{email}}",
|
||||
"duplicate-email-found": "डुप्लिकेट ईमेल पाया गया: {{email}}",
|
||||
"vertical": "वर्टिकल",
|
||||
"horizontal": "हॉरिज़ॉन्टल",
|
||||
"billing": "बिलिंग",
|
||||
"console": "कंसोल"
|
||||
}
|
||||
"console": "कंसोल",
|
||||
"prefill-widget": "पूर्व भराव विजेट्स",
|
||||
"action-prohibited": "यह क्रिया आपके ईमेल डोमेन के लिए अनुमत नहीं है। कृपया सहायता के लिए अपने व्यवस्थापक से संपर्क करें।",
|
||||
"must-have-at-least-one-vacant-role": "किसी टेम्पलेट को 'public' पर सेट करने से पहले कम से कम एक भूमिका खाली होनी चाहिए।",
|
||||
"remove-duplicate": "कृपया डुप्लिकेट विकल्प हटाएं",
|
||||
"prefill-bulk-error": "जब प्रीफ़िल विजेट जोड़े जाते हैं तो बल्क भेजना अनुमति नहीं है। कृपया आगे बढ़ने के लिए प्रीफ़िल विजेट हटा दें।",
|
||||
"session-expired-title": "सत्र समाप्त हो गया",
|
||||
"access-denied": "पहुंच अस्वीकृत",
|
||||
"upgrade": "अपग्रेड",
|
||||
"do-not-access-app": "आपको इस एप्लिकेशन तक पहुंच नहीं है।",
|
||||
"dont-have-access": "आपको पहुंच नहीं है।",
|
||||
"valid-email-alert": "कृपया एक मान्य ईमेल पता दर्ज करें।",
|
||||
"otp-not-validate": "ओटीपी मान्य नहीं है।",
|
||||
"domain-not-allowed": "यह डोमेन अनुमति नहीं है",
|
||||
"atleast-one-recipient-alert": "कृपया कम से कम एक प्राप्तकर्ता जोड़ें!",
|
||||
"incorrect-password-or-decryption-failed": "गलत पासवर्ड या डिक्रिप्शन विफल।",
|
||||
"incorrect-password-for-file": "फ़ाइल के लिए गलत पासवर्ड: {{file}}",
|
||||
"error-uploading-pdf": "PDF अपलोड करते समय त्रुटि।",
|
||||
"provide-password": "कृपया पासवर्ड प्रदान करें।",
|
||||
"only-pdf-allowed": "केवल PDF फ़ाइलें अनुमत हैं।",
|
||||
"invalid-username-password-region": "अमान्य उपयोगकर्ता नाम/पासवर्ड या क्षेत्र।",
|
||||
"pfx-extension-alert": ".pfx एक्सटेंशन वाली फ़ाइल अपलोड करें।",
|
||||
"email-already-exist": "ईमेल पहले से मौजूद है",
|
||||
"branding": "ब्रांडिंग",
|
||||
"branding-help": "ब्रांडिंग आपकी ऐप में वाइट लेबलिंग की सुविधा देती है",
|
||||
"custom-sub-domain": "कस्टम सब-डोमेन",
|
||||
"app-name": "ऐप का नाम",
|
||||
"provide-domain-name": "अपना डोमेन नाम दर्ज करें",
|
||||
"provide-app-name": "अपना ऐप नाम दर्ज करें",
|
||||
"logo": "लोगो",
|
||||
"upload-app-logo": "अपने ऐप का लोगो अपलोड करें",
|
||||
"prefill-unfilled-widget": "निम्न अनिवार्य फ़ील्ड खाली नहीं छोड़े जा सकते: {{emptyWidget}}। कृपया आगे बढ़ने के लिए इन्हें भरें।",
|
||||
"Dashboard": "डैशबोर्ड",
|
||||
"Analytics": "विश्लेषिकी",
|
||||
"Templates": "टेम्पलेट्स",
|
||||
"Need your sign": "आपके हस्ताक्षर की आवश्यकता है",
|
||||
"In Progress": "प्रगति में है",
|
||||
"Completed": "पूरा हुआ",
|
||||
"Drafts": "ड्राफ्ट",
|
||||
"Declined": "अस्वीकृत",
|
||||
"Expired": "समाप्त",
|
||||
"Contactbook": "संपर्क पुस्तिका",
|
||||
"My Signature": "मेरा हस्ताक्षर",
|
||||
"API Token": "एपीआई टोकन",
|
||||
"Webhook": "वेबहूक",
|
||||
"Preferences": "वरीयताएँ",
|
||||
"Teams": "टीमें",
|
||||
"Users": "उपयोगकर्ता",
|
||||
"Drive": "ड्राइव",
|
||||
"Branding": "ब्रांडिंग",
|
||||
"Mail": "मेल",
|
||||
"Storage": "भंडारण",
|
||||
"Signing certificate": "हस्ताक्षर प्रमाण पत्र",
|
||||
"General": "सामान्य",
|
||||
"Organizations": "संगठन",
|
||||
"OrgAdmins": "संगठन व्यवस्थापक",
|
||||
"Debug Pdf": "पीडीएफ़ डीबग करें",
|
||||
"New Document": "नया दस्तावेज़",
|
||||
"subscription": "सब्सक्रिप्शन",
|
||||
"Draft document": "ड्राफ्ट दस्तावेज़",
|
||||
"Draft template": "ड्राफ्ट टेम्पलेट",
|
||||
"Public sign": "पब्लिक साइन",
|
||||
"Signup": "साइन अप करें",
|
||||
"delete-contact": "संपर्क हटाएं",
|
||||
"total-records-found": "कुल रिकॉर्ड मिले: {{count}}",
|
||||
"Invalid-records-found": "अमान्य रिकॉर्ड मिले: {{records}}",
|
||||
"previous": "पिछला",
|
||||
"page-n-of-n": "पृष्ठ {{currentPage}} का {{totalPages}}",
|
||||
"import": "आयात करें",
|
||||
"search": "खोजें",
|
||||
"viewed-on": "देखा गया: {{ViewedOn}}",
|
||||
"signed-on": "हस्ताक्षर किया गया: {{SignedOn}}",
|
||||
"hide": "छिपाएं",
|
||||
"show-more": "और दिखाएं",
|
||||
"browse-or-drag-to-replace-existing-file": "नई फ़ाइल ब्राउज़ करें या खींचकर छोड़ें ताकि मौजूदा फ़ाइल को बदला जा सके",
|
||||
"optional-details": "वैकल्पिक विवरण",
|
||||
"hide-optional-details": "वैकल्पिक विवरण छिपाएं",
|
||||
"mail-adapter-subscription-alert": "कस्टम SMTP सेटअप करने के लिए कृपया प्रोफेशनल या टीम प्लान में अपग्रेड करें।",
|
||||
"connect-to-mail": "Gmail से कनेक्ट करें",
|
||||
"custom-smtp": "कस्टम SMTP",
|
||||
"default-smtp": "{{appName}} डिफ़ॉल्ट SMTP",
|
||||
"host": "होस्ट",
|
||||
"port": "पोर्ट",
|
||||
"sender-email": "प्रेषक ईमेल",
|
||||
"username": "यूज़रनेम",
|
||||
"use-default-mail-adapter": "क्या आप वाकई {{appName}} के डिफ़ॉल्ट मेल सर्वर का उपयोग करके सिग्नेचर अनुरोध भेजना चाहते हैं? बेहतर इनबॉक्स डिलीवरी के लिए हम अपने Gmail या SMTP सर्वर का उपयोग करने की सिफारिश करते हैं।",
|
||||
"verification-code-sent-registered-email": "आपके पंजीकृत ईमेल <1>{{useremail}}</1> पर एक सत्यापन कोड भेजा गया है। कृपया अपनी सेटिंग की पुष्टि के लिए नीचे कोड दर्ज करें।",
|
||||
"smpt-credentials": "SMTP क्रेडेंशियल्स",
|
||||
"delete-account": "खाता हटाएं",
|
||||
"delete-account-que": "क्या आप वाकई अपना खाता हटाना चाहते हैं?",
|
||||
"delete-account-que-user": "आप इस उपयोगकर्ता और सभी संबंधित डेटा को स्थायी रूप से हटाने वाले हैं। इस क्रिया को पूर्ववत नहीं किया जा सकता।",
|
||||
"user-deleted-successfully": "उपयोगकर्ता और सभी संबंधित डेटा सफलतापूर्वक हटाए गए।",
|
||||
"account-deletion-request-sent-via-mail": "हमने आपको एक पुष्टि लिंक ईमेल किया है। अपने खाते को हटाने की प्रक्रिया पूरी करने के लिए अनुरोध को स्वीकृत करें।",
|
||||
"type-exact-email-delete": "हटाने को सक्षम करने के लिए सटीक ईमेल टाइप करें",
|
||||
"email-does-not-match": "ईमेल मेल नहीं खाता।",
|
||||
"please-type-to-confirm": "कृपया पुष्टि करने के लिए <1>{{userEmail}}</1> टाइप करें:",
|
||||
"email-settings": "ईमेल सेटिंग्स",
|
||||
"email-settings-help": "अपने हस्ताक्षर अनुरोध ईमेल की बेहतर इनबॉक्स डिलीवरी सुनिश्चित करने के लिए, आप अपने स्वयं के ईमेल प्रदाता को कनेक्ट कर सकते हैं। निम्न विकल्पों में से एक चुनें:",
|
||||
"connect-to-gmail": "Gmail से कनेक्ट करें: ",
|
||||
"connect-to-gmail-help": "अपने Gmail खाते का उपयोग करके हस्ताक्षर अनुरोध ईमेल सीधे अपने इनबॉक्स से भेजें, जिससे डिलीवरी दर और विश्वसनीयता में सुधार होगा।",
|
||||
"connect-to-smtp": "कस्टम SMTP: ",
|
||||
"connect-to-smtp-help": "अपने डोमेन के माध्यम से ईमेल भेजने के लिए अपने कस्टम SMTP सर्वर का उपयोग करें। यह विकल्प आपको अपने ईमेल इंफ्रास्ट्रक्चर पर पूरा नियंत्रण देता है, जिससे डिलीवरी और ब्रांड की निरंतरता में सुधार होता है।",
|
||||
"connect-to-default": "यदि आप चाहें, तो आप {{appName}} के डिफ़ॉल्ट मेल सर्वर का भी उपयोग कर सकते हैं, हालांकि सर्वोत्तम परिणामों के लिए हम अपने सर्वर का उपयोग करने की सलाह देते हैं।",
|
||||
"email-settings-redirect-message": "यह सेटिंग कंसोल से मुख्य मेनू सेटिंग्स → प्रेफ़रेंस में स्थानांतरित कर दी गई है। यह पेज आने वाले संस्करणों में हटा दिया जाएगा।",
|
||||
"go-to-preferences-menu": "प्रेफ़रेंस मेनू पर जाएं",
|
||||
"document-download-filename-format": "दस्तावेज़ डाउनलोड फ़ाइलनाम प्रारूप",
|
||||
"preview": "पूर्वावलोकन: ",
|
||||
"download-filename-format-help": "डाउनलोड की गई PDF फ़ाइलों का नाम कैसे रखा जाए, चुनें। आपका चयन आपके प्रोफ़ाइल में सहेजा जाएगा और पूरे ऐप में उपयोग किया जाएगा।",
|
||||
"delete-action-prohibited": "यह कार्रवाई अनुमत नहीं है। कृपया अपना खाता हटाने के लिए अपने व्यवस्थापक से संपर्क करें।",
|
||||
"not-verified": "सत्यापित नहीं",
|
||||
"verified": "सत्यापित",
|
||||
"expires": "समाप्ति",
|
||||
"fix-resend-error": "आप इस दस्तावेज़ को ठीक करके पुनः नहीं भेज सकते क्योंकि इसमें प्रीफ़िल विजेट्स हैं।",
|
||||
"duplicate-template-error": "आप इस टेम्पलेट को डुप्लिकेट नहीं कर सकते क्योंकि इसमें प्रीफ़िल विजेट्स हैं।",
|
||||
"save-as-template-error": "इस दस्तावेज़ को टेम्पलेट के रूप में सहेजा नहीं जा सकता क्योंकि इसमें प्रीफ़िल विजेट्स हैं।",
|
||||
"redirecting-you-in": "{{redirectTimeLeft}} सेकंड में आपको रीडायरेक्ट किया जा रहा है...",
|
||||
"pdf-tools-tour": "पृष्ठ जोड़ने, हटाने, पुनः व्यवस्थित करने, घुमाने और ज़ूम करने के लिए इन बटनों पर क्लिक करें।",
|
||||
"widgets": "विजेट्स",
|
||||
"prefill-tour": "दस्तावेज़ को प्राप्तकर्ताओं को भेजने से पहले अग्रिम रूप से जानकारी दर्ज करने के लिए इस विकल्प का उपयोग करें।",
|
||||
"empty-prefill-error": "निम्न आवश्यक फ़ील्ड खाली नहीं छोड़े जा सकते:",
|
||||
"please-fill-out": "कृपया आगे बढ़ने के लिए इन्हें भरें।",
|
||||
"custom-signing-certificate": "कस्टम साइनिंग प्रमाणपत्र",
|
||||
"signing-certificate-help": "आप अपना स्वयं का दस्तावेज़ हस्ताक्षर प्रमाणपत्र अपलोड कर सकते हैं, जिसका उपयोग आपके सभी दस्तावेज़ों और पूर्णता प्रमाणपत्रों पर हस्ताक्षर करने के लिए किया जाएगा। प्रमाणपत्र फ़ाइल PFX फॉर्मेट में P12 प्रमाणपत्र होनी चाहिए।",
|
||||
"certificate-file-p12-in-PFX-format": "प्रमाणपत्र फ़ाइल (PFX फॉर्मेट में p12 प्रमाणपत्र)",
|
||||
"password-of-pfx-file": "pfx फ़ाइल का पासवर्ड दर्ज करें",
|
||||
"update": "अपडेट करें",
|
||||
"use-default-certificate": "डिफ़ॉल्ट {{appName}} प्रमाणपत्र का उपयोग करें",
|
||||
"upgrade-to-team-plan": "टीम प्लान में अपग्रेड करें",
|
||||
"setup-file-storage": "फ़ाइल स्टोरेज सेटअप करें",
|
||||
"save-and-activate": "सहेजें और सक्रिय करें",
|
||||
"logging-out-to-apply-settings": "नई सेटिंग लागू करने के लिए आपको लॉग आउट किया जा रहा है",
|
||||
"reconnect-to-default": "{{appName}} से फिर से कनेक्ट करें",
|
||||
"active-file-adapter": "सक्रिय फ़ाइल एडेप्टर",
|
||||
"file-adapter-unique-name": "फ़ाइल एडेप्टर का यूनिक नाम",
|
||||
"unique-name-for-file-adapter": "फ़ाइल एडेप्टर के लिए यूनिक नाम दर्ज करें",
|
||||
"storage-provider": "स्टोरेज प्रदाता",
|
||||
"enter-bucket-name": "बकेट का नाम दर्ज करें",
|
||||
"enter-space-name": "स्पेस का नाम दर्ज करें",
|
||||
"enter-region-of-bucket": "बकेट का क्षेत्र दर्ज करें",
|
||||
"enter-region-of-space": "स्पेस का क्षेत्र दर्ज करें",
|
||||
"enter-access-key": "एक्सेस की दर्ज करें",
|
||||
"enter-secret-access-key": "सीक्रेट एक्सेस की दर्ज करें",
|
||||
"otp-email": "हमने एक सत्यापन कोड भेजा है",
|
||||
"save-as-temp-warn": "नोट: इस दस्तावेज़ में पहले से भरे हुए विजेट शामिल हैं, जिन्हें स्वचालित रूप से हटा दिया जाएगा क्योंकि वे पहले से ही मूल दस्तावेज़ में सम्मिलित हैं।",
|
||||
"edit-document": "दस्तावेज़ संपादित करें",
|
||||
"modify": "संशोधित करें",
|
||||
"merge-certificate-to-pdf": "प्रमाणपत्र को PDF में मिलाएँ",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "यह सुनिश्चित करेगा कि पूर्णता प्रमाणपत्र अंतिम PDF दस्तावेज़ में शामिल हो। हालाँकि, कृपया ध्यान दें कि एक बार मिलाने के बाद, प्रमाणपत्र को मुख्य दस्तावेज़ से अलग नहीं किया जा सकता।",
|
||||
"p2": "यदि आप मिलाना नहीं चुनते हैं, तो पूर्णता प्रमाणपत्र हस्ताक्षरित दस्तावेज़ के साथ एक अलग PDF फ़ाइल के रूप में प्रदान किया जाएगा।"
|
||||
},
|
||||
"read-only-date-error": "रीड-ओनली दिनांक विजेट में एक डिफ़ॉल्ट दिनांक होना आवश्यक है।",
|
||||
"set-date": "दिनांक सेट करें",
|
||||
"set-today": "हस्ताक्षर की तिथि",
|
||||
"enter-name": "नाम दर्ज करें",
|
||||
"enter-email": "ईमेल दर्ज करें",
|
||||
"subscribe-to-opensign-msg": "{{appName}} की सदस्यता लें और असीमित निःशुल्क डिजिटल हस्ताक्षरों का आनंद लें।",
|
||||
"duplicate-subscribe-msg": "डुप्लिकेट करने से आप चयनित टेम्पलेट की एक सटीक प्रति बना सकते हैं, जिसे आप पुनः उपयोग या संशोधित कर सकते हैं बिना मूल को प्रभावित किए।",
|
||||
"save-as-template-msg": "इस दस्तावेज़ को एक पुन: प्रयोज्य टेम्पलेट के रूप में सहेजें जिसे आप भविष्य के दस्तावेज़ों के लिए फिर से उपयोग कर सकते हैं।",
|
||||
"public-credit-alert": "इस लिंक के माध्यम से हस्ताक्षर करने पर आपके निःशुल्क ईमेल क्रेडिट का उपभोग होगा। एक निःशुल्क उपयोगकर्ता के रूप में, आपको स्पैमर्स को हमारी प्रणाली का दुरुपयोग करने से रोकने के लिए प्रति माह 15 ईमेल क्रेडिट आवंटित किए जाते हैं। उच्च सीमा और निर्बाध पहुँच का आनंद लेने के लिए, <1>OpenSign™ पेड प्लान</1> की सदस्यता लें।",
|
||||
"know-more-about": "इसके बारे में और जानें",
|
||||
"free-unlimited-signatures": "मुफ़्त असीमित हस्ताक्षर",
|
||||
"email-quota-updated": "दैनिक ईमेल कोटा सफलतापूर्वक अपडेट किया गया।",
|
||||
"password-has-been-reset": "पासवर्ड रीसेट कर दिया गया है।",
|
||||
"Unauthorized to reset your own password.": "अपना पासवर्ड रीसेट करने के लिए अधिकृत नहीं है।",
|
||||
"User not found or not allowed.": "उपयोगकर्ता नहीं मिला या अनुमति नहीं है।",
|
||||
"User not found.": "उपयोगकर्ता नहीं मिला।",
|
||||
"Admin user tenant not found.": "एडमिन उपयोगकर्ता टेनेंट नहीं मिला।",
|
||||
"User is not authenticated.": "उपयोगकर्ता प्रमाणीकृत नहीं है।",
|
||||
"Please provide required parameters.": "कृपया आवश्यक पैरामीटर प्रदान करें।",
|
||||
"reset-password": "उपयोगकर्ता पासवर्ड रीसेट करें",
|
||||
"enter-strong-password": "एक मज़बूत पासवर्ड दर्ज करें या स्वचालित रूप से उत्पन्न करें।",
|
||||
"autogenerate": "स्वतः उत्पन्न करें",
|
||||
"extend": "बढ़ाएँ",
|
||||
"console-general-help": "व्यक्तिगत उपयोगकर्ताओं को अपनी स्वयं की ईमेल टेम्पलेट्स अनुकूलित करने की अनुमति देने के लिए, जाएँ",
|
||||
"console-mail-help": "कंपनी-व्यापी ईमेल टेम्पलेट्स अपडेट करने के लिए, जाएँ",
|
||||
"admin-setting-disabled": "यह सेटिंग अक्षम है। सहायता के लिए कृपया व्यवस्थापक से संपर्क करें।",
|
||||
"company-email-settings": "कंपनी ईमेल सेटिंग्स",
|
||||
"allow-email-template-individually": "उपयोगकर्ताओं को व्यक्तिगत रूप से ईमेल टेम्पलेट अनुकूलित करने की अनुमति दें ",
|
||||
"user-email-template-general-help": "इसे निष्क्रिय करने पर उपयोगकर्ता अपनी ईमेल टेम्पलेट्स को अनुकूलित नहीं कर पाएंगे।",
|
||||
"disable-individual-user-smtp": "व्यक्तिगत उपयोगकर्ता SMTP सेटिंग्स अक्षम करें",
|
||||
"enable-individual-user-smtp": "व्यक्तिगत उपयोगकर्ता SMTP सेटिंग्स सक्षम करें",
|
||||
"user-smtp-general-help": "जब यह निष्क्रिय हो, तो उपयोगकर्ता अपनी स्वयं की SMTP सेटिंग्स कॉन्फ़िगर नहीं कर पाएंगे। सभी ईमेल ग्लोबल SMTP कॉन्फ़िगरेशन का उपयोग करेंगे।",
|
||||
"global-smtp-settings": "ग्लोबल SMTP सेटिंग्स",
|
||||
"global-smtp-settings-help-p1": "ये सेटिंग्स आपके पूरे {{appName}} खाते में वैश्विक रूप से लागू होती हैं। यदि किसी उपयोगकर्ता ने अपनी स्वयं की ईमेल डिलीवरी सेटिंग्स कॉन्फ़िगर नहीं की हैं, तो सिस्टम स्वचालित रूप से यहाँ परिभाषित ग्लोबल SMTP सेटिंग्स का उपयोग करेगा।",
|
||||
"global-smtp-settings-help-p2": "सभी हस्ताक्षर अनुरोध ईमेल की बेहतर इनबॉक्स डिलीवरी सुनिश्चित करने के लिए, आप अपना पसंदीदा ईमेल प्रदाता जोड़ सकते हैं। निम्न विकल्पों में से एक चुनें:",
|
||||
"global-connect-to-gmail-help": "अपनी संस्था की इनबॉक्स से सीधे हस्ताक्षर अनुरोध ईमेल भेजने के लिए Gmail खाते का उपयोग करें। यह डिलीवरी दर, प्रतिष्ठा और विश्वसनीयता में सुधार करता है।",
|
||||
"global-connect-to-smtp-help": "अपने डोमेन के माध्यम से ईमेल भेजने के लिए अपना स्वयं का SMTP सर्वर उपयोग करें। यह आपकी ईमेल संरचना पर पूर्ण नियंत्रण प्रदान करता है, डिलीवरी को बेहतर बनाता है और ब्रांड की निरंतरता बनाए रखता है।",
|
||||
"global-smtp-settings-help-p3": "यदि कोई व्यक्तिगत उपयोगकर्ता-स्तरीय सेटिंग्स कॉन्फ़िगर नहीं की गई हैं, तो ये वैश्विक सेटिंग्स स्वचालित रूप से लागू होंगी।"
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"header-news": "Nuova funzionalità: Gli utenti del piano Teams possono ora integrare i propri bucket AWS S3 per l'archiviazione dei file",
|
||||
"header-news-btn": "Configura Ora",
|
||||
"sandbox-news": "Questo è un ambiente sandbox. Si prega di non utilizzarlo per scopi di produzione.",
|
||||
"sandbox-news": "Questo è un ambiente sandbox. Si prega di non utilizzarlo per scopi di produzione.",
|
||||
"create-account": "Crea Account",
|
||||
"login": "Accedi",
|
||||
"language": "Lingua",
|
||||
"dark-mode": "Modalità scura",
|
||||
"name": "Nome",
|
||||
"phone": "Telefono",
|
||||
"phone-optional": "facoltativo",
|
||||
"phone-optional": "Opzionale",
|
||||
"email": "Email",
|
||||
"company": "Azienda",
|
||||
"job-title": "Titolo professionale",
|
||||
@@ -26,6 +26,8 @@
|
||||
"Name": "Nome",
|
||||
"Date": "Data"
|
||||
},
|
||||
"folder": "Cartella",
|
||||
"pdf": "Pdf",
|
||||
"context-menu": {
|
||||
"Download": "Scarica",
|
||||
"Rename": "Rinomina",
|
||||
@@ -44,6 +46,10 @@
|
||||
"contact-now": "Contatta ora",
|
||||
"upgrade-to": "Aggiorna a",
|
||||
"plan": "Piano",
|
||||
"connect": "Connetti",
|
||||
"connect-to-g-drive": "Connetti a Google Drive",
|
||||
"reconnect-to-g-drive": "Riconnetti a Google Drive",
|
||||
"gdrive-info-connect": "Quando Google Drive è connesso, il documento completato verrà salvato nella cartella {{appName}} su Google Drive.",
|
||||
"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-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.",
|
||||
@@ -58,13 +64,14 @@
|
||||
"welcome": "Bentornato!",
|
||||
"Login-to-your-account": "Accedi al tuo account",
|
||||
"password": "Password",
|
||||
"forgot-password": "Password dimenticata?",
|
||||
"forgot-password": "Password dimenticata",
|
||||
"loading": "Caricamento...",
|
||||
"of": "di",
|
||||
"sign-SSO": "Accedi con SSO",
|
||||
"login-page": "Pagina di accesso",
|
||||
"or": "O",
|
||||
"signup-page": "Pagina di registrazione",
|
||||
"password-match-length": "La nuova password e la conferma devono corrispondere",
|
||||
"password-length": "La password deve contenere almeno 8 caratteri",
|
||||
"password-case": "La password deve contenere una lettera maiuscola, una minuscola e un numero",
|
||||
"password-special-char": "La password deve contenere un carattere speciale",
|
||||
@@ -114,7 +121,8 @@
|
||||
"Teams": "Team",
|
||||
"Team-update-successfully": "Team aggiornato con successo.",
|
||||
"Users": "Utenti"
|
||||
}
|
||||
},
|
||||
"Global SMTP": "SMTP Globale"
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necessita della tua firma",
|
||||
@@ -159,7 +167,8 @@
|
||||
"daily-mail-quota": "Quota e-mail giornaliera",
|
||||
"Save as template": "Salva come modello",
|
||||
"Fix & resend": "Correggi e reinvia",
|
||||
"Kiosk Mode": "Modalità Kiosk"
|
||||
"Kiosk Mode": "Modalità Kiosk",
|
||||
"Reset password": "Reimposta password"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nr.",
|
||||
@@ -181,7 +190,20 @@
|
||||
"created-date": "Data di creazione",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Log",
|
||||
"Expiry-date": "Data di scadenza"
|
||||
"Expiry-date": "Data di scadenza",
|
||||
"Company": "Azienda",
|
||||
"JobTitle": "Titolo professionale",
|
||||
"Time to complete (Days)": "Tempo per completare (giorni)",
|
||||
"Auto reminder": "Promemoria automatico",
|
||||
"Remind once in every (Days)": "Ricorda una volta ogni (giorni)",
|
||||
"Enable OTP verification": "Abilita verifica OTP",
|
||||
"Enable Tour": "Abilita tour",
|
||||
"Notify on signatures": "Notifica sulle firme",
|
||||
"Allow modifications": "Consenti modifiche",
|
||||
"Redirect url": "URL di reindirizzamento",
|
||||
"Created Date": "Data di creazione",
|
||||
"Updated Date": "Data di aggiornamento",
|
||||
"Expiry Date": "Data di scadenza"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Questi sono documenti che hai iniziato ma non hai ancora finalizzato per l'invio.",
|
||||
@@ -193,16 +215,15 @@
|
||||
"Contactbook": "Questa è una lista di contatti/firmatari aggiunti da te. Appariranno come suggerimenti quando provi ad aggiungere firmatari a un nuovo documento.",
|
||||
"Templates": "Questa è una lista di modelli disponibili per creare documenti. Puoi fare clic sul pulsante 'Usa' per creare un nuovo documento utilizzando un modello, modificare il documento e aggiungere firmatari nel passaggio successivo."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firma tu stesso",
|
||||
"Request Signatures": "Richiedi firme",
|
||||
"New Template": "Nuovo modello"
|
||||
},
|
||||
"Sign Yourself": "Firma tu stesso",
|
||||
"Request Signatures": "Richiedi firme",
|
||||
"New Template": "Nuovo modello",
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "file selezionato",
|
||||
"template-title": "Titolo del modello",
|
||||
"document-title": "Titolo del documento",
|
||||
"title": "Titolo",
|
||||
"description": "Descrizione",
|
||||
"time-to-complete": "Tempo per completare (giorni)",
|
||||
"send-in-order": "Invia in ordine",
|
||||
@@ -244,7 +265,7 @@
|
||||
"my-signature": "La mia firma",
|
||||
"signature": "Firma",
|
||||
"upload-image": "Carica immagine",
|
||||
"clear": "Chiara",
|
||||
"clear": "Cancella",
|
||||
"upload-signature/Image": "Carica firma/immagine",
|
||||
"initials": "Iniziali",
|
||||
"API": "API",
|
||||
@@ -280,7 +301,7 @@
|
||||
"deactivate": "disattiva",
|
||||
"this-user": "questo utente",
|
||||
"delete-user": "Elimina utente",
|
||||
"delete": "elimina",
|
||||
"delete": "Elimina",
|
||||
"add-user": "Aggiungi utente",
|
||||
"password-generateed": "La password verrà generata solo una volta; assicurati di copiarla.",
|
||||
"Team status": "Stato del Team",
|
||||
@@ -367,7 +388,8 @@
|
||||
"dropdown": "menu a tendina",
|
||||
"radio button": "pulsante di opzione",
|
||||
"image": "immagine",
|
||||
"email": "email"
|
||||
"email": "email",
|
||||
"number": "numero"
|
||||
},
|
||||
"fields": "Campi",
|
||||
"recipients": "Destinatari",
|
||||
@@ -379,6 +401,7 @@
|
||||
"certificate": "Certificato",
|
||||
"decline": "Rifiuta",
|
||||
"finish": "Completa",
|
||||
"done": "Fatto",
|
||||
"mail": "Email",
|
||||
"sign-now": "Firma ora",
|
||||
"successfully-signed": "Firmato con successo!",
|
||||
@@ -388,6 +411,10 @@
|
||||
"Email-verified-alert-1": "Email verificata.",
|
||||
"Email-verified-alert-2": "Email già verificata.",
|
||||
"upload-stamp-image": "Carica immagine timbro",
|
||||
"draw-signature": "Disegna la firma",
|
||||
"draw-initials": "Disegna le iniziali",
|
||||
"enter-text": "Inserisci testo",
|
||||
"enter-widgettype": "Inserisci {{widgetType}}",
|
||||
"draw": "Disegno",
|
||||
"type": "Digitata",
|
||||
"color-type": {
|
||||
@@ -400,7 +427,7 @@
|
||||
"upload": "Caricamento",
|
||||
"initial-teb": "Iniziali",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "La tua firma",
|
||||
"initial-alert": "La mia iniziale non trovata",
|
||||
"copy-title": "Copia widget in",
|
||||
"contact-delete-alert": "Sei sicuro di voler eliminare questo contatto?",
|
||||
"reset-password-alert-1": "Il link per reimpostare la password è stato inviato al tuo indirizzo email",
|
||||
@@ -419,6 +446,19 @@
|
||||
"maximum-check": "Controllo massimo",
|
||||
"cell-count": "conteggio delle cellule",
|
||||
"default-value": "Valore predefinita",
|
||||
"formula": "Formula",
|
||||
"formula-placeholder": "Crea una formula utilizzando i widget numerici",
|
||||
"formula-no-widgets": "Non ci sono ancora altri widget numerici assegnati a questo firmatario.",
|
||||
"supported-operators": "Operatori supportati:",
|
||||
"formula-helper": "Fai clic su una chip di widget numerico per inserirla (es. {{number-1}} + {{number-2}}).",
|
||||
"formula-error-letter": "Racchiudi i nomi dei widget tra doppie parentesi graffe (es. {{number-1}}).",
|
||||
"formula-error-characters": "Al di fuori delle parentesi sono consentiti solo numeri e +, -, *, /, (, ).",
|
||||
"formula-error-braces": "Ogni {{ di apertura deve avere una }} corrispondente.",
|
||||
"formula-error-operators": "Quando usi operatori o parentesi, aggiungi almeno due valori.",
|
||||
"formula-error-leading-operator": "Le formule non possono iniziare con operatori.",
|
||||
"decimal-places": "Cifre decimali",
|
||||
"number-of-decimal-places": "Numero di cifre decimali",
|
||||
"decimal-places-helper": "Imposta su 0 per numeri interi",
|
||||
"select": "Seleziona",
|
||||
"read-only": "È di sola lettura",
|
||||
"hide-labels": "Nascondi etichette",
|
||||
@@ -465,13 +505,13 @@
|
||||
"add-recipients": "Aggiungi destinatari",
|
||||
"loading-mssg": "Questo potrebbe richiedere del tempo",
|
||||
"send-mail": "Invia Mail",
|
||||
"signature-field-widget":"È necessario aggiungere almeno un campo firma per ogni utente. Non hai aggiunto campi firma per {{signersName}}",
|
||||
"signature-field-widget": "Ogni utente deve avere almeno un widget di firma. Non hai aggiunto un widget di firma per {{signersName}}.",
|
||||
"placeholder-alert-1": "Assicurati che sia stato aggiunto almeno un widget per la firma per tutti i destinatari.",
|
||||
"placeholder-alert-2": "Conferma di aver compilato il campo di testo.",
|
||||
"placeholder-alert-3": "Sei sicuro di voler inviare questo documento per le firme?",
|
||||
"placeholder-alert-4": "Hai inviato con successo le mail a tutti i destinatari!",
|
||||
"placeholder-mail-alert": "Hai inviato con successo un'e-mail a {{name}}. I firmatari successivi riceveranno un'e-mail una volta che {{name}} avrà firmato il documento.",
|
||||
"placeholder-mail-alert-you": "I firmatari successivi riceveranno un'email non appena firmi il documento.",
|
||||
"placeholder-mail-alert-you": "I firmatari successivi riceveranno un'email dopo che avrai firmato il documento.",
|
||||
"placeholder-alert-5": "Vuoi firmare i documenti ora?",
|
||||
"placeholder-alert-6": "Configura l'adattatore email per inviare mail!",
|
||||
"placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!",
|
||||
@@ -600,7 +640,8 @@
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "Accesso effettuato con successo! Diamo un'occhiata.",
|
||||
"home-layout-2": "Per caricare documenti da firmare personalmente o richiedere le firme di altri, seleziona semplicemente i pulsanti corrispondenti.",
|
||||
"home-layout-3": "Sei pronto per iniziare a usare {{appName}}! Se hai bisogno di supporto, contattaci pure.",
|
||||
"home-layout-3": "Sei pronto per iniziare a usare {{appName}}!",
|
||||
"home-layout-4": "Dacci una stella su",
|
||||
"generate-token": "Esegui l'upgrade ora per generare un token API di produzione.",
|
||||
"opensign-drive-1": "Fai clic sui collegamenti breadcrumb per navigare facilmente attraverso la gerarchia delle cartelle e visualizzare i documenti in ciascuna cartella.",
|
||||
"opensign-drive-2": "Fai clic sul pulsante aggiungi per creare una nuova cartella o documento.",
|
||||
@@ -610,34 +651,32 @@
|
||||
"opensign-drive-6": "Fai clic con il tasto destro su un documento per vedere opzioni come Scarica, Rinomina, Sposta e Elimina. Fai clic sul documento per aprirlo.",
|
||||
"opensign-drive-7": "Fai clic con il tasto destro su qualsiasi cartella per vedere le opzioni. Scegli 'Rinomina' per cambiare il nome della cartella o fai clic sulla cartella per navigare nei suoi contenuti.",
|
||||
"pdf-request-file-1": "Elenco dei firmatari che devono ancora firmare il documento.",
|
||||
"pdf-request-file-2": "Fai clic su uno dei segnaposto che appaiono sul documento per firmare. Vedrai poi le opzioni per disegnare la tua firma, digitarla o caricare un'immagine.",
|
||||
"pdf-request-file-3": "Clicca sui pulsanti Rifiuta o Fine per navigare nel tuo documento. Usa il menu con i puntini di sospensione per opzioni aggiuntive, incluso il pulsante Scarica.",
|
||||
"pdf-request-file-2": "Fai clic su uno qualsiasi dei campi presenti nel documento per iniziare. Potrai quindi compilare le informazioni richieste.",
|
||||
"pdf-request-file-3": "Dopo aver compilato tutti i campi richiesti, fai clic su 'Fine'. Potrai quindi scaricare il documento firmato. A seconda delle impostazioni del mittente, potresti ricevere una copia del documento completato insieme al certificato di completamento una volta che tutti i destinatari avranno firmato.",
|
||||
"pdf-request-file-4": "Elenco dei firmatari che hanno già firmato il documento.",
|
||||
"pdf-request-file-5": "Puoi fare clic su 'Firma Tutto Automaticamente' per firmare automaticamente in tutte le posizioni previste per te. Assicurati di esaminare attentamente il documento prima di fare clic su questo pulsante.",
|
||||
"pdf-request-file-6": "Si prega di completare i campi alla pagina {{pagenumbers}}, tutti evidenziati con lo stesso colore per una facile identificazione.",
|
||||
"placeholder-sign-1": "Seleziona un destinatario da questo elenco per aggiungere un segnaposto dove deve firmare. Il segnaposto apparirà dello stesso colore del nome del destinatario una volta posizionato sul documento.",
|
||||
"placeholder-sign-1": "Seleziona un destinatario da questo elenco per aggiungere un widget. Il widget apparirà con lo stesso colore del nome del destinatario una volta posizionato sul documento.",
|
||||
"placeholder-sign-2": "Facendo clic sul pulsante 'Aggiungi destinatari' potrai aggiungere più firmatari.",
|
||||
"placeholder-sign-3": "Fai clic su questo pulsante per aggiungere altri destinatari/firmatari al documento.",
|
||||
"placeholder-sign-4": "Trascina o fai clic su un campo per aggiungerlo al documento.",
|
||||
"placeholder-sign-5": "L'area del contenuto PDF visualizza già i segnaposti esistenti del modello. Per tua comodità, questi segnaposti corrisponderanno al colore del nome del destinatario, rendendoli facilmente identificabili.",
|
||||
"placeholder-sign-4": "Trascina o fai clic su un widget per aggiungerlo al documento.",
|
||||
"placeholder-sign-5": "L'area di contenuto PDF mostra già i widget esistenti del modello. Per comodità, questi widget corrisponderanno al colore del nome del destinatario, rendendoli facilmente identificabili.",
|
||||
"placeholder-sign-6": "Facendo clic su 'Invia' il documento verrà salvato. Nel passaggio successivo potrai personalizzare le email da inviare ai destinatari o copiare i link di firma e condividerli direttamente con i destinatari.",
|
||||
"report-1": "Fai clic sul pulsante 'Aggiungi' per creare un nuovo modello. I modelli sono documenti riutilizzabili progettati per generare rapidamente nuovi documenti con la stessa struttura e firmatari diversi. Ad esempio, un modello HR per l'onboarding potrebbe avere ruoli predefinita come 'Responsabile HR' e 'Nuovo Dipendente'. Ogni volta che usi il modello, puoi assegnare il ruolo 'Nuovo Dipendente' a membri dello staff in arrivo, mentre il ruolo 'Responsabile HR' rimane costante, facilitando un processo di onboarding fluido per ogni nuovo assunto.",
|
||||
"redirect": "Fai clic sul pulsante 'Usa' per creare un nuovo documento da un modello esistente.",
|
||||
"bulksend": "Per inviare rapidamente più documenti utilizzando un modello esistente creando semplicemente gli indirizzi e-mail dei destinatari, fai clic sul pulsante 'Invio Multiplo'.",
|
||||
"option": "Questo menu rivela altre opzioni come Modifica ed Elimina. Usa il pulsante 'Modifica' per aggiungere ruoli di firmatari, modificare i campi e aggiornare il modello. Le modifiche si applicheranno a tutti i futuri documenti creati da questo modello ma non influiranno sui documenti esistenti. Usa il pulsante Elimina per eliminare il modello.",
|
||||
"signyour-self-1": "Seleziona e trascina i widget preferiti sul PDF per personalizzare il documento prima di firmarlo. Scegli i punti perfetti per ogni modifica per adattare il documento alle tue esigenze.",
|
||||
"bulksend": "Per inviare rapidamente più documenti utilizzando un modello esistente, basta inserire gli indirizzi e-mail dei destinatari e fare clic sul pulsante 'Invio massivo'. Puoi inviare fino a 50 destinatari.",
|
||||
"option": "Questo menu mostra ulteriori opzioni come Modifica, Elimina, Rinomina, Duplica, Condividi, ecc. <1>Fai clic qui</1> per saperne di più su tutte le opzioni disponibili. <3>Nota: Le modifiche a un modello esistente si applicheranno a tutti i futuri documenti creati da quel modello, ma non influenzeranno i documenti già inviati.</3>",
|
||||
"signyour-self-1": "Seleziona o trascina i widget preferiti sul PDF per personalizzare il tuo documento prima della firma. Scegli i punti perfetti per ciascun widget per adattare il documento alle tue esigenze.",
|
||||
"signyour-self-2": "Trascina e rilascia ovunque in quest'area. Puoi ridimensionarlo e spostarlo in seguito.",
|
||||
"template-placeholder-1": "Facendo clic sul pulsante 'Aggiungi ruolo' potrai aggiungere vari ruoli di firmatari. Puoi assegnare utenti a ciascun ruolo nei passaggi successivi.",
|
||||
"template-placeholder-2": "Una volta aggiunti i ruoli, seleziona un ruolo dall'elenco per aggiungere un segnaposto dove deve firmare. Il segnaposto apparirà dello stesso colore del nome del ruolo una volta posizionato sul documento.",
|
||||
"template-placeholder-3": "Trascina o fai clic su un campo per aggiungerlo al documento.",
|
||||
"template-placeholder-4": "Trascina il segnaposto per un ruolo ovunque sul documento. Ricorda, apparirà dello stesso colore del nome del destinatario per un facile riferimento.",
|
||||
"template-placeholder-5": "Facendo clic su 'Avanti' il modello corrente verrà memorizzato. Dopo averlo salvato, ti verrà chiesto di creare un nuovo documento da questo modello, se lo desideri.",
|
||||
"template-placeholder-2": "Dopo aver aggiunto i ruoli, scegli uno dall'elenco per posizionare un widget per quel destinatario. Puoi fare clic sul widget o trascinarlo nel documento. Una volta posizionato, il widget verrà visualizzato nello stesso colore del ruolo selezionato.",
|
||||
"template-placeholder-3": "Facendo clic su 'Avanti' il modello corrente verrà memorizzato. Dopo averlo salvato, ti verrà chiesto di creare un nuovo documento da questo modello, se lo desideri.",
|
||||
"webhook-1": "Esegui l'upgrade ora per configurare il webhook.",
|
||||
"Need your Signature": "Facendo clic su questa scheda, accederai all'elenco dei documenti in attesa della tua revisione.",
|
||||
"Out for signatures": "Facendo clic su questa scheda, accederai all'elenco dei documenti in attesa di firma.",
|
||||
"Recent signature requests": "Questo è un elenco di documenti che aspettano la tua firma.",
|
||||
"Recently sent for signatures": "Questo è un elenco di documenti che hai inviato ad altre parti per la firma.",
|
||||
"Drafts": "Questi sono documenti che hai iniziato ma non hai finalizzato per l'invio.",
|
||||
"Drafts": "Questi sono documenti che hai iniziato ma non hai ancora finalizzato per l'invio.",
|
||||
"public-template": "Questo video dimostra come configurare il tuo profilo pubblico personalizzato, come 'https://opensign.me/tuo-username'. Imparerai anche come personalizzare il tuo slogan e rendere i tuoi modelli disponibili per la firma pubblica.",
|
||||
"allowModify-widgets": "È possibile trascinare e rilasciare uno qualsiasi di questi campi nel documento, oltre ai campi già designati dal creatore del documento."
|
||||
},
|
||||
@@ -728,11 +767,11 @@
|
||||
"public-tour-message": "Il modello deve essere pubblico prima di poter generare un link condivisibile.",
|
||||
"add-user-template": "Devi aggiungere un ruolo prima di poter aggiungere i campi per esso.",
|
||||
"pdf-uncompatible": "Questo PDF non è compatibile, contatta {{appName}}",
|
||||
"text-field-tour": "I campi di tipo 'Testo' devono essere compilati in anticipo prima che il documento venga inviato. Se hai bisogno che i firmatari forniscano un input, usa invece il campo 'Testo di input'.",
|
||||
"text-field-tour": "I campi 'Prefill' devono essere compilati in anticipo prima dell'invio del documento. Se hai bisogno che i firmatari forniscano input, usa invece i campi dei firmatari.",
|
||||
"attach-signer-tour": "Devi allegare un firmatario a ogni ruolo. Puoi farlo cliccando su questa icona. Una volta selezionato un firmatario, sarà associato a tutti i campi associati a quel ruolo che appariranno dello stesso colore.",
|
||||
"allowed-signature-types": "Tipi di firma consentiti",
|
||||
"at-least-one-signature-type": "Almeno un tipo di firma deve essere abilitato.",
|
||||
"expect-default-one-more-signature-type": "Abilita un tipo di firma aggiuntivo oltre al tipo predefinita.",
|
||||
"expect-default-one-signature-type": "Abilita un tipo di firma aggiuntivo oltre al tipo predefinita.",
|
||||
"add-signer-note": "Nota - Questo contatto verrà aggiunto alla tua lista di contatti",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Questa preferenza di firma controlla le opzioni di firma disponibili per i tuoi firmatari. Solo i tipi di firma che selezioni appariranno nel widget di firma quando un documento viene firmato. Ad esempio, se scegli solo l'opzione 'Disegna' nelle preferenze, il tuo firmatario vedrà solo l'opzione 'Disegna' nel widget di firma, mentre gli altri tre tipi non saranno disponibili.",
|
||||
@@ -776,7 +815,7 @@
|
||||
"agree-p1": "Confermo di aver letto e compreso il",
|
||||
"agree-p2": "Divulgazione sulle registrazioni e firme elettroniche",
|
||||
"agree-p3": "e acconsento all'uso di registrazioni e firme elettroniche.",
|
||||
"agrre-button": "Accetta e Continua",
|
||||
"agrre-button": "Confermo e accetto di continuare",
|
||||
"term-cond-title": "Termini e Condizioni",
|
||||
"term-cond-h": "DIVULGAZIONE SULLE REGISTRAZIONI E FIRME ELETTRONICHE",
|
||||
"term-cond-p1": "Questa Divulgazione sulle Registrazioni e Firme Elettroniche ('Divulgazione') è un accordo tra il Creatore del Documento ('Mittente') e il Firmatario ('Tu'), facilitato tramite la piattaforma {{appName}} (Piattaforma). Firmando documenti tramite {{appName}}, accetti i termini descritti in questa Divulgazione. Ti preghiamo di leggerla attentamente prima di procedere.",
|
||||
@@ -879,6 +918,10 @@
|
||||
"thanks-for-feedback": "Grazie per il tuo feedback 🙏",
|
||||
"share-your-feedback": "Condividi il tuo feedback",
|
||||
"share-your-review": "Condividi la tua recensione",
|
||||
"please-select-rating": "Si prega di selezionare una valutazione",
|
||||
"feedback-optional": "Feedback (opzionale)",
|
||||
"feedback-saved": "Il feedback è stato salvato.",
|
||||
"feedback-save-error": "Impossibile salvare il feedback, riprova.",
|
||||
"date-format": "Formato data",
|
||||
"document-deleted": "Il documento è stato eliminato o non hai accesso. Si prega di contattare il mittente.",
|
||||
"save-as-template-?": "Sei sicuro di voler salvare questo documento come modello?",
|
||||
@@ -898,7 +941,7 @@
|
||||
"note-length-alert": "La nota può contenere al massimo 200 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?",
|
||||
"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",
|
||||
@@ -988,7 +1031,7 @@
|
||||
"finish-mssg": "Sei sicuro di voler completare il documento?",
|
||||
"review": "Rivedere",
|
||||
"next-field": "Campo successivo",
|
||||
"required-mssg":"{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti",
|
||||
"required-mssg": "{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti",
|
||||
"verify-document": "Verifica documento",
|
||||
"verify-document-signature": "Verifica firma documento",
|
||||
"select-pdf-document": "Seleziona documento PDF",
|
||||
@@ -1032,14 +1075,204 @@
|
||||
"not-calculated": "Non calcolato",
|
||||
"not-found-in-signature": "Non trovato nella firma",
|
||||
"readonly-error": "Il widget {{widgetName}} di sola lettura deve avere un valore predefinito oppure può essere reso facoltativo.",
|
||||
"choose-one":"Scegline uno",
|
||||
"choose-one": "Scegline uno",
|
||||
"search-templates": "Cerca modelli…",
|
||||
"search-documents": "Cerca documenti…",
|
||||
"search-contacts": "Cerca contatti…",
|
||||
"add-role-alert": "Si prega di aggiungere almeno un ruolo",
|
||||
"edit-draft": "Modifica bozza",
|
||||
"invalid-email-found": "Email non valida trovata: {{email}}",
|
||||
"duplicate-email-found": "Email duplicata trovata: {{email}}",
|
||||
"vertical": "Verticale",
|
||||
"horizontal": "Orizzontale",
|
||||
"billing": "Fatturazione",
|
||||
"console": "Console"
|
||||
}
|
||||
"console": "Console",
|
||||
"prefill-widget": "Widget Precompilati",
|
||||
"action-prohibited": "Questa azione non è consentita per il tuo dominio email. Contatta il tuo amministratore per ricevere assistenza.",
|
||||
"must-have-at-least-one-vacant-role": "Devi lasciare almeno un ruolo non assegnato prima di impostare un template su 'public'.",
|
||||
"remove-duplicate": "Si prega di rimuovere l'opzione duplicata",
|
||||
"prefill-bulk-error": "L'invio in blocco non è consentito quando sono stati aggiunti i widget di precompilazione. Si prega di rimuovere i widget di precompilazione per procedere.",
|
||||
"session-expired-title": "Sessione scaduta",
|
||||
"access-denied": "Accesso negato",
|
||||
"upgrade": "Aggiorna",
|
||||
"do-not-access-app": "Non hai accesso a questa applicazione.",
|
||||
"dont-have-access": "Non hai accesso.",
|
||||
"valid-email-alert": "Inserisci un indirizzo email valido.",
|
||||
"otp-not-validate": "OTP non valido.",
|
||||
"domain-not-allowed": "Questo dominio non è consentito",
|
||||
"atleast-one-recipient-alert": "Aggiungi almeno un destinatario!",
|
||||
"incorrect-password-or-decryption-failed": "Password errata o decrittazione non riuscita.",
|
||||
"incorrect-password-for-file": "Password errata per il file: {{file}}",
|
||||
"error-uploading-pdf": "Errore durante il caricamento del PDF.",
|
||||
"provide-password": "Fornisci la password.",
|
||||
"only-pdf-allowed": "Sono consentiti solo file PDF.",
|
||||
"invalid-username-password-region": "Nome utente/password o regione non valida.",
|
||||
"pfx-extension-alert": "Carica un file con estensione .pfx.",
|
||||
"email-already-exist": "L'email esiste già",
|
||||
"branding": "Marchio",
|
||||
"branding-help": "Il branding consente il white labelling della tua app",
|
||||
"custom-sub-domain": "Sottodominio personalizzato",
|
||||
"app-name": "Nome dell'app",
|
||||
"provide-domain-name": "Fornisci il tuo nome di dominio",
|
||||
"provide-app-name": "Fornisci il nome della tua app",
|
||||
"logo": "Logo",
|
||||
"upload-app-logo": "Carica il logo della tua app",
|
||||
"prefill-unfilled-widget": "I seguenti campi obbligatori non possono essere lasciati vuoti: {{emptyWidget}}. Per favore compilali per procedere.",
|
||||
"Dashboard": "Dashboard",
|
||||
"Analytics": "Analitica",
|
||||
"Templates": "Modelli",
|
||||
"Need your sign": "Necessita della tua firma",
|
||||
"In Progress": "In corso",
|
||||
"Completed": "Completati",
|
||||
"Drafts": "Bozze",
|
||||
"Declined": "Rifiutati",
|
||||
"Expired": "Scaduti",
|
||||
"Contactbook": "Rubrica",
|
||||
"My Signature": "La mia firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferenze",
|
||||
"Teams": "Team",
|
||||
"Users": "Utenti",
|
||||
"Drive": "Drive",
|
||||
"Branding": "Marchio",
|
||||
"Mail": "Posta",
|
||||
"Storage": "Archiviazione",
|
||||
"Signing certificate": "Certificato di firma",
|
||||
"General": "Generale",
|
||||
"Organizations": "Organizzazioni",
|
||||
"OrgAdmins": "OrgAdmins",
|
||||
"Debug Pdf": "Debug PDF",
|
||||
"New Document": "Nuovo documento",
|
||||
"subscription": "Abbonamento",
|
||||
"Draft document": "Bozza di documento",
|
||||
"Draft template": "Bozza di modello",
|
||||
"Public sign": "Firma pubblica",
|
||||
"Signup": "Registrazione",
|
||||
"delete-contact": "Elimina contatto",
|
||||
"total-records-found": "Totale record trovati: {{count}}",
|
||||
"Invalid-records-found": "Record non validi trovati: {{records}}",
|
||||
"previous": "Precedente",
|
||||
"page-n-of-n": "Pagina {{currentPage}} di {{totalPages}}",
|
||||
"import": "Importa",
|
||||
"search": "Cerca",
|
||||
"viewed-on": "Visualizzato il: {{ViewedOn}}",
|
||||
"signed-on": "Firmato il: {{SignedOn}}",
|
||||
"hide": "Nascondi",
|
||||
"show-more": "Mostra di più",
|
||||
"browse-or-drag-to-replace-existing-file": "Sfoglia o trascina un nuovo file per sostituire quello esistente",
|
||||
"optional-details": "Dettagli facoltativi",
|
||||
"hide-optional-details": "nascondi dettagli facoltativi",
|
||||
"mail-adapter-subscription-alert": "Esegui l'upgrade al piano Professional o Team per configurare un SMTP personalizzato.",
|
||||
"connect-to-mail": "Connetti a Gmail",
|
||||
"custom-smtp": "SMTP personalizzato",
|
||||
"default-smtp": "SMTP predefinito di {{appName}}",
|
||||
"host": "Host",
|
||||
"port": "Porta",
|
||||
"sender-email": "Email del mittente",
|
||||
"username": "Nome utente",
|
||||
"use-default-mail-adapter": "Sei sicuro di voler usare i server di posta predefiniti di {{appName}} per inviare le richieste di firma? Ti consigliamo di usare i tuoi server Gmail o SMTP per una migliore consegna in posta in arrivo.",
|
||||
"verification-code-sent-registered-email": "Un codice di verifica è stato inviato alla tua email registrata <1>{{useremail}}</1>. Inserisci il codice qui sotto per confermare le impostazioni.",
|
||||
"smpt-credentials": "Credenziali SMTP",
|
||||
"delete-account": "Elimina account",
|
||||
"delete-account-que": "Sei sicuro di voler eliminare il tuo account?",
|
||||
"delete-account-que-user": "Stai per eliminare in modo permanente questo utente e tutti i dati associati. Questa azione non può essere annullata.",
|
||||
"user-deleted-successfully": "Utente e tutti i dati associati eliminati con successo.",
|
||||
"account-deletion-request-sent-via-mail": "Le abbiamo inviato via e-mail un link di conferma. Approvi la richiesta per completare l'eliminazione del suo account.",
|
||||
"type-exact-email-delete": "Digita l'e-mail esatta per abilitare l'eliminazione",
|
||||
"email-does-not-match": "L'e-mail non corrisponde.",
|
||||
"please-type-to-confirm": "Per favore, digita <1>{{userEmail}}</1> per confermare:",
|
||||
"email-settings": "Impostazioni e-mail",
|
||||
"email-settings-help": "Per garantire una migliore consegna nella casella di posta in arrivo delle e-mail di richiesta firma, puoi collegare il tuo provider di posta elettronica. Scegli una delle seguenti opzioni:",
|
||||
"connect-to-gmail": "Connetti a Gmail",
|
||||
"connect-to-gmail-help": "Usa il tuo account Gmail per inviare e-mail di richiesta firma direttamente dalla tua casella di posta, migliorando il tasso di consegna e l'affidabilità.",
|
||||
"connect-to-smtp": "SMTP personalizzato",
|
||||
"connect-to-smtp-help": "Usa il tuo server SMTP personalizzato per inviare e-mail tramite il tuo dominio. Questa opzione ti offre il pieno controllo sulla tua infrastruttura e-mail, migliorando la consegna e la coerenza del marchio.",
|
||||
"connect-to-default": "Se preferisci, puoi anche utilizzare i server di posta predefiniti di {{appName}}, anche se consigliamo di usare i tuoi per ottenere risultati ottimali.",
|
||||
"email-settings-redirect-message": "Questa impostazione è stata spostata dalla console a Impostazioni del menu principale → Preferenze. Questa pagina verrà rimossa nelle prossime versioni.",
|
||||
"go-to-preferences-menu": "Vai al menu Preferenze",
|
||||
"document-download-filename-format": "Formato del nome file per il download del documento",
|
||||
"preview": "Anteprima: ",
|
||||
"download-filename-format-help": "Scegli come vengono nominati i PDF scaricati. La tua selezione viene salvata nel tuo profilo e utilizzata in tutta l'app.",
|
||||
"delete-action-prohibited": "Questa azione non è consentita. Si prega di contattare l'amministratore per richiedere l'eliminazione dell'account.",
|
||||
"not-verified": "Non verificato",
|
||||
"verified": "Verificato",
|
||||
"expires": "Scade",
|
||||
"fix-resend-error": "Non è possibile correggere e reinviare questo documento perché contiene widget precompilati.",
|
||||
"duplicate-template-error": "Non è possibile duplicare questo modello perché contiene widget precompilati.",
|
||||
"save-as-template-error": "Questo documento non può essere salvato come modello perché contiene widget precompilati.",
|
||||
"redirecting-you-in": "Reindirizzamento tra {{redirectTimeLeft}} sec...",
|
||||
"pdf-tools-tour": "Fare clic su questi pulsanti per aggiungere, eliminare, riorganizzare, ruotare e ingrandire/ridurre le pagine.",
|
||||
"widgets": "Widget",
|
||||
"prefill-tour": "Usa questa opzione per inserire le informazioni in anticipo prima di inviare il documento ai destinatari.",
|
||||
"empty-prefill-error": "I seguenti campi obbligatori non possono essere lasciati vuoti:",
|
||||
"please-fill-out": "Si prega di compilarli per continuare.",
|
||||
"custom-signing-certificate": "Certificato di firma personalizzato",
|
||||
"signing-certificate-help": "Puoi caricare il tuo certificato di firma dei documenti, che verrà utilizzato per firmare tutti i tuoi documenti e i certificati di completamento. Il file del certificato deve essere un certificato P12 in formato PFX.",
|
||||
"certificate-file-p12-in-PFX-format": "File di certificato (certificato p12 in formato PFX)",
|
||||
"password-of-pfx-file": "Inserisci la password del file pfx",
|
||||
"update": "Aggiorna",
|
||||
"use-default-certificate": "Usa il certificato predefinito di {{appName}}",
|
||||
"upgrade-to-team-plan": "Aggiorna al piano Team",
|
||||
"setup-file-storage": "Configura archiviazione file",
|
||||
"save-and-activate": "Salva e attiva",
|
||||
"logging-out-to-apply-settings": "Stai effettuando il logout per applicare le nuove impostazioni",
|
||||
"reconnect-to-default": "Riconnetti a {{appName}}",
|
||||
"active-file-adapter": "File Adapter attivo",
|
||||
"file-adapter-unique-name": "Nome univoco del File Adapter",
|
||||
"unique-name-for-file-adapter": "Inserisci un nome univoco per il File Adapter",
|
||||
"storage-provider": "Provider di archiviazione",
|
||||
"enter-bucket-name": "Inserisci il nome del bucket",
|
||||
"enter-space-name": "Inserisci il nome dello space",
|
||||
"enter-region-of-bucket": "Inserisci la regione del bucket",
|
||||
"enter-region-of-space": "Inserisci la regione dello space",
|
||||
"enter-access-key": "Inserisci l'access key",
|
||||
"enter-secret-access-key": "Inserisci la secret access key",
|
||||
"otp-email": "Abbiamo inviato un codice di verifica",
|
||||
"save-as-temp-warn": "Nota: Questo documento include widget precompilati, che verranno automaticamente rimossi poiché sono già incorporati nel documento di base.",
|
||||
"edit-document": "Modifica documento",
|
||||
"modify": "Modifica",
|
||||
"merge-certificate-to-pdf": "Unisci certificato al PDF",
|
||||
"merge-cc-to-pdf-help": {
|
||||
"p1": "Questo garantirà che il certificato di completamento sia incluso nel documento PDF finale. Tuttavia, si noti che una volta unito, il certificato non può essere separato dal documento principale.",
|
||||
"p2": "Se scegli di non unire, il certificato di completamento sarà fornito come file PDF separato insieme al documento firmato."
|
||||
},
|
||||
"read-only-date-error": "Il widget data di sola lettura deve avere una data predefinita.",
|
||||
"set-date": "Imposta data",
|
||||
"set-today": "Data di firma",
|
||||
"enter-name": "Inserisci nome",
|
||||
"enter-email": "Inserisci e-mail",
|
||||
"subscribe-to-opensign-msg": "Abbonati a {{appName}} e goditi firme digitali gratuite illimitate.",
|
||||
"duplicate-subscribe-msg": "Duplicare consente di creare una copia esatta del modello selezionato, permettendoti di riutilizzarlo o modificarlo senza influire sull'originale.",
|
||||
"save-as-template-msg": "Salva questo documento come modello riutilizzabile che potrai usare di nuovo per documenti futuri.",
|
||||
"public-credit-alert": "Firmare tramite questo link consumerà i tuoi crediti e-mail gratuiti. Come utente gratuito, ti vengono assegnati 15 crediti e-mail al mese per impedire agli spammer di abusare dei nostri sistemi. Per usufruire di limiti più elevati e accesso ininterrotto, abbonati ai piani a pagamento di <1>OpenSign™.</1>",
|
||||
"know-more-about": "Scopri di più su",
|
||||
"free-unlimited-signatures": "Firme illimitate gratuite",
|
||||
"email-quota-updated": "Quota e-mail giornaliera aggiornata con successo.",
|
||||
"password-has-been-reset": "La password è stata reimpostata.",
|
||||
"Unauthorized to reset your own password.": "Non sei autorizzato a reimpostare la tua password.",
|
||||
"User not found or not allowed.": "Utente non trovato o non autorizzato.",
|
||||
"User not found.": "Utente non trovato.",
|
||||
"Admin user tenant not found.": "Tenant utente amministratore non trovato.",
|
||||
"User is not authenticated.": "Utente non autenticato.",
|
||||
"Please provide required parameters.": "Fornisci i parametri richiesti.",
|
||||
"reset-password": "Reimposta password utente",
|
||||
"enter-strong-password": "Inserisci una password sicura o generane una automaticamente.",
|
||||
"autogenerate": "Genera automaticamente",
|
||||
"extend": "Estendi",
|
||||
"console-general-help": "Per consentire ai singoli utenti di personalizzare i propri modelli di e-mail, vai a",
|
||||
"console-mail-help": "Per aggiornare i modelli di e-mail aziendali, vai a",
|
||||
"admin-setting-disabled": "Questa impostazione è disabilitata. Contatta l'amministratore per assistenza.",
|
||||
"company-email-settings": "Impostazioni e-mail aziendali",
|
||||
"allow-email-template-individually": "Consenti la personalizzazione individuale dei modelli di e-mail per gli utenti ",
|
||||
"user-email-template-general-help": "Disabilitando questa opzione, gli utenti non potranno più personalizzare i loro modelli di e-mail.",
|
||||
"disable-individual-user-smtp": "disabilita le impostazioni smtp individuali dell'utente",
|
||||
"enable-individual-user-smtp": "abilita le impostazioni smtp individuali dell'utente",
|
||||
"user-smtp-general-help": "Quando disattivato, gli utenti non potranno configurare il proprio SMTP. Tutte le e-mail utilizzeranno la configurazione SMTP globale.",
|
||||
"global-smtp-settings": "Impostazioni SMTP Globali",
|
||||
"global-smtp-settings-help-p1": "Queste impostazioni si applicano globalmente all'intero account {{appName}}. Se un utente non ha configurato le proprie impostazioni di consegna e-mail, il sistema utilizzerà automaticamente le impostazioni SMTP globali definite qui.",
|
||||
"global-smtp-settings-help-p2": "Per garantire una migliore consegna delle e-mail di richiesta firma, puoi collegare il tuo provider di posta preferito. Scegli una delle seguenti opzioni:",
|
||||
"global-connect-to-gmail-help": "Utilizza un account Gmail per inviare e-mail di richiesta firma direttamente dalla casella di posta della tua organizzazione. Migliora la consegna, la reputazione e l'affidabilità.",
|
||||
"global-connect-to-smtp-help": "Utilizza il tuo server SMTP per inviare e-mail tramite il tuo dominio. Questo offre pieno controllo sulla tua infrastruttura e-mail, migliora la consegna e mantiene la coerenza del marchio.",
|
||||
"global-smtp-settings-help-p3": "Se non sono configurate impostazioni a livello utente, verranno applicate automaticamente queste impostazioni globali."
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,4 +1,4 @@
|
||||
Name,Email,Phone
|
||||
John Doe,john@example.com,1234567890
|
||||
Jane Smith,jane@example.com,9876543210
|
||||
Foo Bar,foo@example.com,5555555555
|
||||
Name,Email,Phone,Company,JobTitle
|
||||
John Doe,john@example.com,1234567890,abc firm,dev
|
||||
Jane Smith,jane@example.com,9876543210,xyz firm,manager
|
||||
Foo Bar,foo@example.com,5555555555,xyz firm,director
|
||||
|
||||
|
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 86 KiB |
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect, lazy } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { lazyWithRetry, hideUpgradeProgress } from "./utils";
|
||||
import { Routes, Route, BrowserRouter } from "react-router";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import Form from "./pages/Form";
|
||||
@@ -13,24 +14,27 @@ import SignYourSelf from "./pages/SignyourselfPdf";
|
||||
import DraftDocument from "./components/pdf/DraftDocument";
|
||||
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
||||
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
||||
import LazyPage from "./primitives/LazyPage";
|
||||
import Lazy from "./primitives/LazyPage";
|
||||
import Loader from "./primitives/Loader";
|
||||
import UserList from "./pages/UserList";
|
||||
import { serverUrl_fn } from "./constant/appinfo";
|
||||
import DocSuccessPage from "./pages/DocSuccessPage";
|
||||
import ValidateSession from "./primitives/ValidateSession";
|
||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
||||
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
||||
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
||||
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||
const Preferences = lazy(() => import("./pages/Preferences"));
|
||||
const Login = lazy(() => import("./pages/Login"));
|
||||
const VerifyDocument = lazy(() => import("./pages/VerifyDocument"));
|
||||
import DragProvider from "./components/DragProivder";
|
||||
import Title from "./components/Title";
|
||||
const DebugPdf = lazyWithRetry(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazyWithRetry(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazyWithRetry(() => import("./pages/GuestLogin"));
|
||||
const ChangePassword = lazyWithRetry(() => import("./pages/ChangePassword"));
|
||||
const UserProfile = lazyWithRetry(() => import("./pages/UserProfile"));
|
||||
const Opensigndrive = lazyWithRetry(() => import("./pages/Opensigndrive"));
|
||||
const ManageSign = lazyWithRetry(() => import("./pages/Managesign"));
|
||||
const AddAdmin = lazyWithRetry(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazyWithRetry(
|
||||
() => import("./pages/UpdateExistUserAdmin")
|
||||
);
|
||||
const Preferences = lazyWithRetry(() => import("./pages/Preferences"));
|
||||
const Login = lazyWithRetry(() => import("./pages/Login"));
|
||||
const VerifyDocument = lazyWithRetry(() => import("./pages/VerifyDocument"));
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||
const AppLoader = () => {
|
||||
return (
|
||||
@@ -42,137 +46,94 @@ const AppLoader = () => {
|
||||
function App() {
|
||||
const [isloading, setIsLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
handleCredentials();
|
||||
// initialize creds
|
||||
const id = process.env.REACT_APP_APPID ?? "opensign";
|
||||
localStorage.setItem("parseAppId", id);
|
||||
localStorage.setItem("baseUrl", `${serverUrl_fn()}/`);
|
||||
hideUpgradeProgress();
|
||||
localStorage.removeItem("showUpgradeProgress");
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
const handleCredentials = () => {
|
||||
const appId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const baseurl = serverUrl_fn();
|
||||
try {
|
||||
localStorage.setItem("baseUrl", `${baseurl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-base-200">
|
||||
{isloading ? (
|
||||
<AppLoader />
|
||||
) : (
|
||||
<BrowserRouter>
|
||||
<Title />
|
||||
<Routes>
|
||||
<Route element={<ValidateRoute />}>
|
||||
<Route exact path="/" element={<LazyPage Page={Login} />} />
|
||||
<Route
|
||||
path="/addadmin"
|
||||
element={<LazyPage Page={AddAdmin} />}
|
||||
/>
|
||||
<Route exact path="/" element={<Lazy Page={Login} />} />
|
||||
<Route path="/addadmin" element={<Lazy Page={AddAdmin} />} />
|
||||
<Route
|
||||
path="/upgrade-2.1"
|
||||
element={<LazyPage Page={UpdateExistUserAdmin} />}
|
||||
element={<Lazy Page={UpdateExistUserAdmin} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route element={<Validate />}>
|
||||
<Route
|
||||
path="/load/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/placeholdersign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
element={<DragProvider Page={PdfRequestFiles} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/login/:base64url"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
element={<Lazy Page={GuestLogin} />}
|
||||
/>
|
||||
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||
<Route path="/debugpdf" element={<Lazy Page={DebugPdf} />} />
|
||||
<Route
|
||||
path="/forgetpassword"
|
||||
element={<LazyPage Page={ForgetPassword} />}
|
||||
element={<Lazy Page={ForgetPassword} />}
|
||||
/>
|
||||
<Route
|
||||
element={
|
||||
<ValidateSession>
|
||||
<HomeLayout />
|
||||
</ValidateSession>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
path="/changepassword"
|
||||
element={<LazyPage Page={ChangePassword} />}
|
||||
/>
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route
|
||||
path="/changepassword"
|
||||
element={<Lazy Page={ChangePassword} />}
|
||||
/>
|
||||
<Route path="/form/:id" element={<Form />} />
|
||||
<Route path="/report/:id" element={<Report />} />
|
||||
<Route path="/dashboard/:id" element={<Dashboard />} />
|
||||
<Route
|
||||
path="/profile"
|
||||
element={<LazyPage Page={UserProfile} />}
|
||||
/>
|
||||
<Route
|
||||
path="/drive"
|
||||
element={<LazyPage Page={Opensigndrive} />}
|
||||
/>
|
||||
<Route
|
||||
path="/managesign"
|
||||
element={<LazyPage Page={ManageSign} />}
|
||||
/>
|
||||
<Route path="/profile" element={<Lazy Page={UserProfile} />} />
|
||||
<Route path="/drive" element={<Lazy Page={Opensigndrive} />} />
|
||||
<Route path="/managesign" element={<Lazy Page={ManageSign} />} />
|
||||
<Route
|
||||
path="/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
element={<DragProvider Page={TemplatePlaceholder} />}
|
||||
/>
|
||||
{/* signyouself route with no rowlevel data using docId from url */}
|
||||
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
||||
<Route
|
||||
path="/signaturePdf/:docId"
|
||||
element={<DragProvider Page={SignYourSelf} />}
|
||||
/>
|
||||
{/* draft document route to handle and navigate route page according to document status */}
|
||||
<Route path="/draftDocument" element={<DraftDocument />} />
|
||||
<Route
|
||||
path="/draftDocument"
|
||||
element={<DragProvider Page={DraftDocument} />}
|
||||
/>
|
||||
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
||||
<Route
|
||||
path="/placeHolderSign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
{/* for user signature (need your sign route) with row level data */}
|
||||
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
|
||||
{/* for user signature (need your sign route) with no row level data */}
|
||||
<Route
|
||||
path="/pdfRequestFiles/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
element={<DragProvider Page={PlaceHolderSign} />}
|
||||
/>
|
||||
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
element={<DragProvider Page={PdfRequestFiles} />}
|
||||
/>
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
element={<DragProvider Page={PdfRequestFiles} />}
|
||||
/>
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route
|
||||
path="/verify-document"
|
||||
element={<LazyPage Page={VerifyDocument} />}
|
||||
element={<Lazy Page={VerifyDocument} />}
|
||||
/>
|
||||
<Route
|
||||
path="/preferences"
|
||||
element={<LazyPage Page={Preferences} />}
|
||||
element={<Lazy Page={Preferences} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/success" element={<DocSuccessPage />} />
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,300 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddSigner = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [addYourself, setAddYourself] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
}, []);
|
||||
// Load user details from localStorage when the component mounts
|
||||
useEffect(() => {
|
||||
const savedUserDetails = JSON.parse(
|
||||
localStorage.getItem("UserInformation")
|
||||
);
|
||||
if (savedUserDetails && addYourself) {
|
||||
setName(savedUserDetails.name);
|
||||
setPhone(savedUserDetails?.phone || "");
|
||||
setEmail(savedUserDetails.email?.toLowerCase()?.replace(/\s/g, ""));
|
||||
}
|
||||
}, [addYourself]);
|
||||
|
||||
const checkUserExist = async () => {
|
||||
const user = Parse.User.current();
|
||||
try {
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", user.getEmail());
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
setIsUserExist(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
}
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey],
|
||||
email: parseData?.Email
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.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);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey],
|
||||
email: parseData?.Email
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleAddYourselfChange = () => {
|
||||
if (addYourself) {
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
} else {
|
||||
setAddYourself(true);
|
||||
}
|
||||
};
|
||||
const handleReset = () => {
|
||||
setAddYourself(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full p-[20px]">
|
||||
{isLoader && (
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto p-2 text-base-content">
|
||||
<p className="mb-3 text-xs text-gray-500">{t("add-signer-note")}</p>
|
||||
{isUserExist && (
|
||||
<div className="mb-3 flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="addYourself"
|
||||
checked={addYourself}
|
||||
onChange={handleAddYourselfChange}
|
||||
className="op-checkbox op-checkbox-sm"
|
||||
/>
|
||||
<label htmlFor="addYourself" className="ml-2 mb-0">
|
||||
{t("add-yourself")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="name" className="block text-xs font-semibold">
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
disabled={addYourself}
|
||||
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 htmlFor="email" className="block text-xs font-semibold">
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) =>
|
||||
setEmail(e.target.value?.toLowerCase()?.replace(/\s/g, ""))
|
||||
}
|
||||
required
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
disabled={addYourself}
|
||||
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 font-semibold">
|
||||
{t("phone")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
disabled={addYourself}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 flex gap-x-2 justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("reset")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddSigner;
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
import {
|
||||
copytoData,
|
||||
@@ -72,7 +71,7 @@ const AddUser = (props) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
alert(t("valid-email-alert"));
|
||||
} else {
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsFormLoader(true);
|
||||
@@ -100,7 +99,6 @@ const AddUser = (props) => {
|
||||
};
|
||||
const res = await Parse.Cloud.run("adduser", params);
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
console.log("parseData ", parseData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
@@ -155,7 +153,6 @@ const AddUser = (props) => {
|
||||
};
|
||||
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 />
|
||||
@@ -166,7 +163,7 @@ const AddUser = (props) => {
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
@@ -182,12 +179,13 @@ const AddUser = (props) => {
|
||||
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"
|
||||
placeholder={t("enter-name")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
@@ -203,10 +201,11 @@ const AddUser = (props) => {
|
||||
}
|
||||
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"
|
||||
placeholder={t("enter-email")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
<label className="block text-xs 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]">
|
||||
@@ -223,7 +222,7 @@ const AddUser = (props) => {
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
@@ -240,7 +239,7 @@ const AddUser = (props) => {
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("Role")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
|
||||
@@ -17,6 +17,7 @@ const BulkSendUi = (props) => {
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [signers, setSigners] = useState([]);
|
||||
const [emails, setEmails] = useState([]);
|
||||
const [isPrefillExist, setIsPrefillExist] = useState(false);
|
||||
useEffect(() => {
|
||||
signatureExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -24,9 +25,14 @@ const BulkSendUi = (props) => {
|
||||
|
||||
//function to check at least one signature field exist
|
||||
const signatureExist = async () => {
|
||||
const isPrefill = props?.Placeholders.some((x) => x?.Role === "prefill");
|
||||
if (isPrefill) {
|
||||
setIsPrefillExist(isPrefill);
|
||||
}
|
||||
setIsDisableBulkSend(false);
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
const removePrefill = getPlaceholder.filter((x) => x?.Role !== "prefill");
|
||||
const checkIsSignatureExistt = removePrefill?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
)
|
||||
@@ -216,7 +222,11 @@ const BulkSendUi = (props) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{!isDisableBulkSend ? (
|
||||
{isPrefillExist ? (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("prefill-bulk-error")}
|
||||
</div>
|
||||
) : !isDisableBulkSend ? (
|
||||
<>
|
||||
{props.Placeholders?.length > 0 ? (
|
||||
isSignatureExist ? (
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const ColumnSelector = ({
|
||||
isOpen,
|
||||
allColumns = [],
|
||||
visibleColumns = [],
|
||||
columnLabels = {},
|
||||
defaultColumns = [],
|
||||
onApply,
|
||||
onClose
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [selected, setSelected] = useState(visibleColumns);
|
||||
const [names, setNames] = useState(columnLabels);
|
||||
|
||||
useEffect(() => {
|
||||
setSelected(visibleColumns);
|
||||
setNames(columnLabels);
|
||||
}, [visibleColumns, columnLabels]);
|
||||
|
||||
const handleChange = (col) => {
|
||||
setSelected((prev) =>
|
||||
prev.includes(col) ? prev.filter((c) => c !== col) : [...prev, col]
|
||||
);
|
||||
};
|
||||
|
||||
const handleApply = () => {
|
||||
onApply && onApply(selected, names);
|
||||
onClose && onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalUi isOpen={isOpen} title={t("select-columns")} handleClose={onClose}>
|
||||
<div className="p-[20px] flex flex-col gap-2">
|
||||
{allColumns.map((col, i) => (
|
||||
<div key={col} className="flex justify-between items-center gap-2">
|
||||
<span className="flex justify-center items-center h-full">
|
||||
<input
|
||||
id={col + "_" + i}
|
||||
type="checkbox"
|
||||
checked={selected.includes(col)}
|
||||
onChange={() => handleChange(col)}
|
||||
className="mb-0 cursor-pointer"
|
||||
/>
|
||||
<span className="whitespace-nowrap ml-1">
|
||||
{t(`report-heading.${col}`, { defaultValue: col })}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex justify-start mt-2">
|
||||
<button onClick={handleApply} className="op-btn op-btn-primary">
|
||||
{t("apply")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColumnSelector;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import {
|
||||
DndProvider,
|
||||
MouseTransition
|
||||
} from "react-dnd-multi-backend";
|
||||
import LazyPage from "../primitives/LazyPage";
|
||||
import { GuidelinesProvider } from "../context/GuidelinesContext";
|
||||
|
||||
const HTML5toTouch = {
|
||||
backends: [
|
||||
{
|
||||
id: "html5",
|
||||
backend: HTML5Backend,
|
||||
transition: MouseTransition
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default function DragProvider({ Page, lazy = false }) {
|
||||
return (
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<GuidelinesProvider>
|
||||
{lazy ? <LazyPage Page={Page} /> : <Page />}
|
||||
</GuidelinesProvider>
|
||||
</DndProvider>
|
||||
);
|
||||
}
|
||||
@@ -12,27 +12,44 @@ import {
|
||||
} from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSidebar } from "../redux/reducers/sidebarReducer";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
|
||||
const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
const Header = ({ isConsole, setIsLoggingOut }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
const dispatch = useDispatch();
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [applogo, setAppLogo] = useState("");
|
||||
const [isDarkTheme, setIsDarkTheme] = useState();
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
setIsMenu(false);
|
||||
closeSidebar();
|
||||
};
|
||||
const closeSidebar = () => {
|
||||
if (width && width <= 768) {
|
||||
dispatch(toggleSidebar(false));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initializeHead();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
closeSidebar();
|
||||
}, [width]);
|
||||
|
||||
const showSidebar = () => {
|
||||
dispatch(toggleSidebar());
|
||||
};
|
||||
|
||||
|
||||
async function initializeHead() {
|
||||
const applogo = await getAppLogo();
|
||||
@@ -44,12 +61,15 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const closeDropdown = async () => {
|
||||
const handleLogout = async () => {
|
||||
setIsOpen(false);
|
||||
setIsLoggingOut(true);
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
} finally {
|
||||
dispatch(sessionStatus(true));
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
@@ -57,6 +77,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
let favicon = localStorage.getItem("favicon");
|
||||
|
||||
localStorage.clear();
|
||||
saveLanguageInLocal(i18n);
|
||||
@@ -66,7 +87,8 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
localStorage.setItem("favicon", favicon);
|
||||
setIsLoggingOut(false);
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
@@ -88,22 +110,15 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const updateLogoForTheme = () => {
|
||||
const isDarkMode =
|
||||
const updateThemeStatus = () => {
|
||||
const isDarkTheme =
|
||||
document.documentElement.getAttribute("data-theme") === "opensigndark";
|
||||
const logo = isDarkMode
|
||||
? "/static/js/assets/images/logo-dark.png" // Path to the dark mode logo
|
||||
: appInfo.applogo; // Use current logo for light mode
|
||||
if (applogo !== logo) {
|
||||
setAppLogo(logo);
|
||||
}
|
||||
setIsDarkTheme(isDarkTheme);
|
||||
};
|
||||
|
||||
// Set the logo immediately based on the current theme
|
||||
updateLogoForTheme();
|
||||
updateThemeStatus();
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
updateLogoForTheme();
|
||||
updateThemeStatus();
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
@@ -112,10 +127,10 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [applogo]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<div className="op-navbar bg-base-100 shadow touch-none">
|
||||
<div className="flex-none">
|
||||
<button
|
||||
@@ -126,11 +141,18 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 ml-2">
|
||||
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
||||
<div
|
||||
onClick={() => navigate("/dashboard/35KBoSgoAK")}
|
||||
className="h-[25px] md:h-[40px] w-auto overflow-hidden cursor-pointer"
|
||||
>
|
||||
{applogo && (
|
||||
<img
|
||||
className="object-contain h-full w-auto"
|
||||
src={applogo}
|
||||
src={
|
||||
isDarkTheme
|
||||
? "/static/js/assets/images/logo-dark.png"
|
||||
: applogo
|
||||
}
|
||||
alt="logo"
|
||||
/>
|
||||
)}
|
||||
@@ -235,7 +257,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
<li onClick={closeDropdown}>
|
||||
<li onClick={handleLogout}>
|
||||
<span>
|
||||
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
|
||||
{t("log-out")}
|
||||
@@ -245,7 +267,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function RotateAlert(props) {
|
||||
props.setShowRotateAlert({ status: false, degree: 0 })
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost shadow-md"
|
||||
className="op-btn op-btn-ghost text-base-content shadow-md"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
|
||||
@@ -1,20 +1,82 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useLocation, matchPath } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMemo } from "react";
|
||||
import { useManifestUrl } from "../hook/useManifestUrl";
|
||||
|
||||
function Title({ title, drive }) {
|
||||
const TITLE_MAP = {
|
||||
"/": "login",
|
||||
// Homelayout
|
||||
"/dashboard/35KBoSgoAK": "Dashboard",
|
||||
"/form/sHAnZphf69": "Sign Yourself",
|
||||
"/form/8mZzFxbG1z": "Request Signatures",
|
||||
"/form/template": "New Template",
|
||||
"/report/6TeaPr321t": "Templates",
|
||||
"/report/4Hhwbp482K": "Need your sign",
|
||||
"/report/1MwEuxLEkF": "In Progress",
|
||||
"/report/kQUoW4hUXz": "Completed",
|
||||
"/report/ByHuevtCFY": "Drafts",
|
||||
"/report/UPr2Fm5WY3": "Declined",
|
||||
"/report/zNqBHXHsYH": "Expired",
|
||||
"/report/contacts": "Contactbook",
|
||||
"/drive": "Drive",
|
||||
"/managesign": "My Signature",
|
||||
"/preferences": "Preferences",
|
||||
"/users": "Users",
|
||||
"/profile": "profile",
|
||||
"/changepassword": "change-password",
|
||||
"/verify-document": "verify-document",
|
||||
|
||||
"/signaturePdf/:docId": "Sign Yourself",
|
||||
"/placeHolderSign/:docId": "Request Signatures",
|
||||
"/template/:templateId": "New Template",
|
||||
"/recipientSignPdf/:docId": "Request Signatures",
|
||||
"/recipientSignPdf/:docId/:contactBookId": "Request Signatures",
|
||||
"/load/recipientSignPdf/:docId/:contactBookId": "Request Signatures",
|
||||
|
||||
// alone
|
||||
"/debugpdf": "Debug Pdf",
|
||||
"/forgetpassword": "forgot-password",
|
||||
"/success": "success",
|
||||
"/addadmin": "add-admin",
|
||||
"/upgrade-2.1": "add-admin",
|
||||
"/draftDocument": "New Document",
|
||||
"/login/:base64url": "Request Signatures",
|
||||
|
||||
};
|
||||
|
||||
function resolveTitle(pathname, override) {
|
||||
if (override) return override;
|
||||
for (let [pattern, label] of Object.entries(TITLE_MAP)) {
|
||||
if (matchPath({ path: pattern, end: true }, pathname)) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export default function Title() {
|
||||
const { pathname, state } = useLocation();
|
||||
const { t } = useTranslation();
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const logo = useMemo(() => localStorage.getItem("favicon"), []);
|
||||
const prefix = useMemo(
|
||||
() => resolveTitle(pathname, state?.title),
|
||||
[pathname, state?.title]
|
||||
);
|
||||
const title = useMemo(
|
||||
() => (prefix ? `${t(prefix)} - ${appName}` : appName),
|
||||
[t, prefix, appName]
|
||||
);
|
||||
const manifestUrl = useManifestUrl(appName, logo);
|
||||
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{drive ? title : `${title} - ${appName}`}</title>
|
||||
<meta name="description" content={`${title} - ${appName}`} />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href={localStorage.getItem("fev_Icon")}
|
||||
sizes="40x40"
|
||||
/>
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={title} />
|
||||
{logo && <link rel="icon" type="image/png" href={logo} />}
|
||||
<link rel="manifest" href={manifestUrl} />
|
||||
</Helmet>
|
||||
);
|
||||
}
|
||||
|
||||
export default Title;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import Parse from "parse";
|
||||
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||
@@ -168,25 +168,21 @@ const DashboardCard = (props) => {
|
||||
}
|
||||
}
|
||||
setresponse(arr.length);
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
await axios.get(url, { headers: headers }).then((res) => {
|
||||
if (res.data.results.length > 0) {
|
||||
setLoading(false);
|
||||
if (props.Data.key !== "count") {
|
||||
setresponse(res.data.results[0][props.Data.key]);
|
||||
} else {
|
||||
setresponse(res.data[props.Data.key]);
|
||||
}
|
||||
if (res?.data?.[props.Data.key]) {
|
||||
setresponse(parseInt(res.data[props.Data.key]));
|
||||
} else if (res?.data?.results?.length > 0) {
|
||||
setresponse(res.data.results.length);
|
||||
} else {
|
||||
setresponse(0);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import Parse from "parse";
|
||||
import ReportTable from "../../primitives/GetReportDisplay";
|
||||
import DocumentsReport from "../../reports/document/DocumentsReport";
|
||||
import reportJson from "../../json/ReportJson";
|
||||
import axios from "axios";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function DashboardReport(props) {
|
||||
const { t } = useTranslation();
|
||||
// console.log("props ", props)
|
||||
const [List, setList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [reportName, setReportName] = useState("");
|
||||
@@ -198,7 +197,7 @@ function DashboardReport(props) {
|
||||
) : (
|
||||
<>
|
||||
{reportName ? (
|
||||
<ReportTable
|
||||
<DocumentsReport
|
||||
ReportName={reportName}
|
||||
List={List}
|
||||
setList={setList}
|
||||
@@ -215,9 +214,9 @@ function DashboardReport(props) {
|
||||
isSearchResult={isSearchResult}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
|
||||
<div className="text-center">
|
||||
<p className="text-xl text-base-content">{t("report-not-found")}</p>
|
||||
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded-box">
|
||||
<div className="text-center text-xl text-base-content">
|
||||
{t("report-not-found")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { Suspense, lazy } from "react";
|
||||
import { useState, useEffect, Suspense } from "react";
|
||||
import { lazyWithRetry } from "../../utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const DashboardButton = lazy(() => import("./DashboardButton"));
|
||||
const DashboardCard = lazy(() => import("./DashboardCard"));
|
||||
const DashboardReport = lazy(() => import("./DashboardReport"));
|
||||
const DashboardButton = lazyWithRetry(() => import("./DashboardButton"));
|
||||
const DashboardCard = lazyWithRetry(() => import("./DashboardCard"));
|
||||
const DashboardReport = lazyWithRetry(() => import("./DashboardReport"));
|
||||
const buttonList = [
|
||||
{
|
||||
label: "Sign yourself",
|
||||
@@ -19,6 +20,7 @@ const buttonList = [
|
||||
];
|
||||
const GetDashboard = (props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const Button = ({ label, redirectId, redirectType, icon }) => (
|
||||
<DashboardButton
|
||||
Icon={icon}
|
||||
@@ -58,7 +60,9 @@ const GetDashboard = (props) => {
|
||||
<div data-tut={col.widget.data.tourSection}>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
<DashboardReport
|
||||
Record={col.widget}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
@@ -92,7 +96,9 @@ const GetDashboard = (props) => {
|
||||
return (
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
<DashboardReport
|
||||
Record={col.widget}
|
||||
/>
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -71,11 +71,9 @@ function DriveBody(props) {
|
||||
});
|
||||
props.setPdfData(updatedData);
|
||||
props.sortingData(null, null, updatedData);
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${docId}`,
|
||||
try {
|
||||
await axios.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${docId}`,
|
||||
updateName,
|
||||
{
|
||||
headers: {
|
||||
@@ -84,18 +82,14 @@ function DriveBody(props) {
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
// const res = result.data;
|
||||
// console.log("res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -245,7 +239,6 @@ function DriveBody(props) {
|
||||
)
|
||||
|
||||
.then((Listdata) => {
|
||||
// console.log("Listdata ", Listdata);
|
||||
const res = Listdata.data;
|
||||
if (res) {
|
||||
const updatedData = props.pdfData.filter(
|
||||
@@ -366,14 +359,14 @@ function DriveBody(props) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[26px] h-[26px] fill-current op-text-secondary"
|
||||
className="w-[26px] h-[26px] fill-current"
|
||||
>
|
||||
<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>
|
||||
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||
</td>
|
||||
<td>_</td>
|
||||
<td>Folder</td>
|
||||
<td>{t("folder")}</td>
|
||||
<td>_</td>
|
||||
<td>_</td>
|
||||
</tr>
|
||||
@@ -390,7 +383,7 @@ function DriveBody(props) {
|
||||
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||
</td>
|
||||
<td>{createddate}</td>
|
||||
<td>Pdf</td>
|
||||
<td>{t("pdf")}</td>
|
||||
<td>{t(`drive-document-status.${status}`)}</td>
|
||||
<td>
|
||||
<i
|
||||
@@ -421,7 +414,7 @@ function DriveBody(props) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[100px] h-[100px] fill-current op-text-secondary"
|
||||
className="w-[100px] h-[100px] fill-current"
|
||||
>
|
||||
<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>
|
||||
@@ -458,14 +451,14 @@ function DriveBody(props) {
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-light fa-font mr-[8px]"></i>
|
||||
<span>Rename</span>
|
||||
<span>{t(`context-menu.Rename`)}</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Delete", data, data.Type)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-light fa-trash mr-[8px]"></i>
|
||||
<span>Delete</span>
|
||||
<span>{t(`context-menu.Delete`)}</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
@@ -550,7 +543,6 @@ function DriveBody(props) {
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ContextMenu.Portal>
|
||||
<ContextMenu.Content
|
||||
className="ContextMenuContent"
|
||||
@@ -603,7 +595,6 @@ function DriveBody(props) {
|
||||
{signersName()}
|
||||
</>
|
||||
)}
|
||||
|
||||
<HoverCard.Arrow className="HoverCardArrow" />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Portal>
|
||||
@@ -627,21 +618,21 @@ function DriveBody(props) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props?.pdfData?.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{handleFolderData(data, ind, "table")}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{props?.pdfData?.map((data, ind) => (
|
||||
<React.Fragment key={ind}>
|
||||
{handleFolderData(data, ind, "table")}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
|
||||
{props.pdfData.map((data, ind) => {
|
||||
return <React.Fragment key={ind}>{handleFolderData(data, ind, "list")}</React.Fragment>;
|
||||
})}
|
||||
{props?.pdfData?.map((data, ind) => (
|
||||
<React.Fragment key={ind}>
|
||||
{handleFolderData(data, ind, "list")}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const AddRoleModal = (props) => {
|
||||
<button
|
||||
onClick={props.handleCloseRoleModal}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
className="op-btn op-btn-ghost text-base-content ml-2"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
|
||||
@@ -9,7 +9,6 @@ function AgreementContent(props) {
|
||||
const h2Style = "text-base-content font-medium text-lg";
|
||||
const ulStyle = "list-disc px-4 py-3";
|
||||
const handleOnclick = () => {
|
||||
props.setIsAgreeTour(false);
|
||||
props.setIsAgree(true);
|
||||
props.setIsShowAgreeTerms(false);
|
||||
props.showFirstWidget();
|
||||
@@ -119,7 +118,7 @@ function AgreementContent(props) {
|
||||
{t("agrre-button")}
|
||||
</button>
|
||||
<button
|
||||
className="op-btn op-btn-ghost"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
onClick={() => props.setIsShowAgreeTerms(false)}
|
||||
>
|
||||
{t("close")}
|
||||
|
||||
@@ -4,7 +4,6 @@ import AgreementContent from "./AgreementContent";
|
||||
|
||||
function AgreementSign(props) {
|
||||
const { t } = useTranslation();
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [isShowAgreeTerms, setIsShowAgreeTerms] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -12,39 +11,12 @@ function AgreementSign(props) {
|
||||
<div className="op-modal op-modal-open absolute z-[448]">
|
||||
<div className="w-[95%] md:w-[60%] lg:w-[40%] op-modal-box overflow-y-auto hide-scrollbar text-sm p-4">
|
||||
<div className="flex flex-row items-center">
|
||||
<label className="inline-flex justify-center items-center cursor-pointer mb-0">
|
||||
{/* 1) This div becomes the “fake” checkbox */}
|
||||
<div
|
||||
data-tut="IsAgree"
|
||||
className={`w-6 h-6 border-2 mr-3 rounded-full flex text-center items-center justify-center ${isChecked ? "op-border-primary" : "border-red-500"}`}
|
||||
>
|
||||
{isChecked ? (
|
||||
<span className="op-text-primary text-sm font-bold">✓</span>
|
||||
) : (
|
||||
<span className="text-red-500 text-sm font-bold">X</span>
|
||||
)}
|
||||
</div>
|
||||
{/* 2) Visually hide the native checkbox but keep it in the DOM */}
|
||||
<input
|
||||
className="sr-only"
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
onChange={(e) => {
|
||||
setIsChecked(e.target.checked);
|
||||
if (e.target.checked) {
|
||||
props.setIsAgreeTour(false);
|
||||
}
|
||||
props.showFirstWidget();
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<div className="text-[11px] md:text-base text-base-content">
|
||||
<span>{t("agree-p1")}</span>
|
||||
<span
|
||||
className="font-bold text-blue-600 cursor-pointer"
|
||||
onClick={() => {
|
||||
setIsShowAgreeTerms(true);
|
||||
props.setIsAgreeTour(false);
|
||||
}}
|
||||
>
|
||||
{t("agree-p2")}
|
||||
@@ -52,15 +24,11 @@ function AgreementSign(props) {
|
||||
<span> {t("agree-p3")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex ml-[35px] mt-3">
|
||||
<div className="flex mt-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isChecked) {
|
||||
props.setIsAgreeTour(false);
|
||||
props.setIsAgree(true);
|
||||
} else {
|
||||
props.setIsAgreeTour(true);
|
||||
}
|
||||
props.setIsAgree(true);
|
||||
props.showFirstWidget();
|
||||
}}
|
||||
className="op-btn op-btn-primary op-btn-sm w-full md:w-auto"
|
||||
>
|
||||
@@ -74,7 +42,6 @@ function AgreementSign(props) {
|
||||
</div>
|
||||
{isShowAgreeTerms && (
|
||||
<AgreementContent
|
||||
setIsAgreeTour={props.setIsAgreeTour}
|
||||
setIsAgree={props.setIsAgree}
|
||||
setIsShowAgreeTerms={setIsShowAgreeTerms}
|
||||
showFirstWidget={props.showFirstWidget}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import { useRef, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { EmailBody } from "./EmailBody";
|
||||
import {
|
||||
contractDocument,
|
||||
defaultMailBody,
|
||||
defaultMailSubject,
|
||||
sendEmailToSigners
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
function CustomizeMail(props) {
|
||||
const { t } = useTranslation();
|
||||
const editorRef = useRef();
|
||||
const navigate = useNavigate();
|
||||
const copyUrlRef = useRef(null);
|
||||
const [isCustomize, setIsCustomize] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
|
||||
const handleCloseSendmailModal = () => {
|
||||
if (props?.handleClose) {
|
||||
props?.handleClose();
|
||||
return;
|
||||
}
|
||||
props?.setIsMailModal(false);
|
||||
navigate("/report/1MwEuxLEkF");
|
||||
};
|
||||
const handleOnchangeRequest = () => {
|
||||
if (editorRef.current) {
|
||||
const html = editorRef.current.editor.root.innerHTML;
|
||||
props?.setCustomizeMail((prev) => ({
|
||||
...prev,
|
||||
body: html
|
||||
}));
|
||||
}
|
||||
};
|
||||
const handleEmailSendToSigners = async () => {
|
||||
setIsLoader(true);
|
||||
const documentData = await contractDocument(props?.documentId);
|
||||
if (documentData && documentData?.length > 0) {
|
||||
props?.setDocumentDetails(documentData[0]);
|
||||
if (
|
||||
documentData?.[0]?.SendinOrder &&
|
||||
documentData?.[0]?.SendinOrder === true
|
||||
) {
|
||||
const ownerEmail = documentData[0].ExtUserPtr.Email;
|
||||
const ownerDetails = documentData[0].Signers.find(
|
||||
(x) => x.Email === ownerEmail
|
||||
);
|
||||
props?.setCurrUserId(ownerDetails?.objectId);
|
||||
}
|
||||
//function is used to send email to signers for sign the document
|
||||
const mailRes = await sendEmailToSigners(
|
||||
documentData,
|
||||
props?.signerList,
|
||||
props?.customizeMail,
|
||||
props?.defaultMail,
|
||||
isCustomize,
|
||||
);
|
||||
props?.setIsMailModal(false);
|
||||
props?.setIsSend(true);
|
||||
setIsLoader(false);
|
||||
if (mailRes?.status === "success") {
|
||||
props?.setMailStatus("success");
|
||||
} else if (mailRes?.status === "quota-reached") {
|
||||
props?.setMailStatus("quotareached");
|
||||
} else if (mailRes?.status === "daily-quota-reached") {
|
||||
props?.setMailStatus("dailyquotareached");
|
||||
} else {
|
||||
props?.setMailStatus("failed");
|
||||
}
|
||||
// setMailStatus(mail_status);
|
||||
} else {
|
||||
alert("something-went-wrong-mssg");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isLoader ? (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 rounded-box z-30">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<ModalUi
|
||||
isOpen={props?.isMailModal}
|
||||
title={t("send-mail")}
|
||||
handleClose={() => handleCloseSendmailModal()}
|
||||
>
|
||||
<div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content">
|
||||
{!isCustomize && <span>{t("placeholder-alert-3")}</span>}
|
||||
{
|
||||
isCustomize && (
|
||||
<>
|
||||
<EmailBody
|
||||
editorRef={editorRef}
|
||||
requestBody={props?.customizeMail.body}
|
||||
requestSubject={props?.customizeMail.subject}
|
||||
handleOnchangeRequest={handleOnchangeRequest}
|
||||
setCustomizeMail={props?.setCustomizeMail}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||
onClick={() => {
|
||||
props?.setCustomizeMail({
|
||||
subject: defaultMailSubject,
|
||||
body: defaultMailBody
|
||||
});
|
||||
}}
|
||||
>
|
||||
<span>{t("reset-to-default")}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
onClick={() => handleEmailSendToSigners()}
|
||||
className="op-btn op-btn-primary font-[500] text-sm shadow"
|
||||
>
|
||||
{t("send")}
|
||||
</button>
|
||||
{isCustomize && (
|
||||
<button
|
||||
onClick={() => setIsCustomize(false)}
|
||||
className="op-btn op-btn-ghost font-[500] text-sm"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
!isCustomize && (
|
||||
<span
|
||||
className="op-link op-link-accent text-sm"
|
||||
onClick={() => setIsCustomize(!isCustomize)}
|
||||
>
|
||||
{t("cutomize-email")}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center items-center mt-3">
|
||||
<span className="h-[1px] w-[20%] bg-[#ccc]"></span>
|
||||
<span className="ml-[5px] mr-[5px]">{t("or")}</span>
|
||||
<span className="h-[1px] w-[20%] bg-[#ccc]"></span>
|
||||
</div>
|
||||
<div className="my-3">{props?.handleShareList()}</div>
|
||||
<p id="copyUrl" ref={copyUrlRef} className="hidden"></p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomizeMail;
|
||||
@@ -3,43 +3,39 @@ import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
function DefaultSignature(props) {
|
||||
const { t } = useTranslation();
|
||||
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
|
||||
const myInitial = useSelector((state) => state.widget.myInitial)
|
||||
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
|
||||
const myInitial = useSelector((state) => state.widget.myInitial);
|
||||
const tabName = ["my-signature", "my-initials"];
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const confirmToaddDefaultSign = (type) => {
|
||||
if (!props.isAgree) {
|
||||
props.setIsAgreeTour(true);
|
||||
} else {
|
||||
if (props?.xyPosition.length > 0) {
|
||||
//check signature or initial widgets exist or not for auto signing
|
||||
const getCurrentSignerXY = props?.xyPosition.filter(
|
||||
(data) => data.Id === props.uniqueId
|
||||
);
|
||||
const checkIsSignInitialExist = getCurrentSignerXY?.every(
|
||||
(placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((placeholder) =>
|
||||
placeholder?.pos?.some((posItem) => posItem?.type === type)
|
||||
)
|
||||
);
|
||||
if (checkIsSignInitialExist) {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("default-sign-alert", { widgetsType: type }),
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("defaultSign-alert", { widgetsType: type })
|
||||
});
|
||||
}
|
||||
if (props?.xyPosition.length > 0) {
|
||||
//check signature or initial widgets exist or not for auto signing
|
||||
const getCurrentSignerXY = props?.xyPosition.filter(
|
||||
(data) => data.Id === props.uniqueId
|
||||
);
|
||||
const checkIsSignInitialExist = getCurrentSignerXY?.every(
|
||||
(placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((placeholder) =>
|
||||
placeholder?.pos?.some((posItem) => posItem?.type === type)
|
||||
)
|
||||
);
|
||||
if (checkIsSignInitialExist) {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("default-sign-alert", { widgetsType: type }),
|
||||
type: type
|
||||
});
|
||||
} else {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("please-select-position!")
|
||||
alertMessage: t("defaultSign-alert", { widgetsType: type })
|
||||
});
|
||||
}
|
||||
} else {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("please-select-position!")
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { getWidgetType, widgets } from "../../constant/Utils";
|
||||
import { widgets } from "../../constant/Utils";
|
||||
import getWidgetType from "./getWidgetType";
|
||||
|
||||
function DragElement(item) {
|
||||
const getWidgets = widgets;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { useDragLayer } from "react-dnd";
|
||||
import { defaultWidthHeight } from "../../constant/Utils";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
function DragGuideLinesLayer(props) {
|
||||
const { showGuidelines } = props;
|
||||
const prevDragState = useRef({ isDragging: false, x: 0, y: 0 });
|
||||
const { isDraggingWidgets, offset, itemType } = useDragLayer((monitor) => ({
|
||||
isDraggingWidgets: monitor.isDragging(),
|
||||
offset: monitor.getClientOffset(),
|
||||
itemType: monitor.getItem()
|
||||
}));
|
||||
useEffect(() => {
|
||||
if (!offset) return;
|
||||
//getting container by id
|
||||
const container = document.getElementById("container");
|
||||
if (!container) return;
|
||||
if (itemType?.text) {
|
||||
// Calculate the position of the dragged element relative to the container
|
||||
// Get the container's bounding rectangle (position and size in the viewport)
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
|
||||
// Compute the X and Y coordinates of the dragged item inside the container
|
||||
// by subtracting the container's top-left offset from the current drag offset
|
||||
const x = offset.x - containerRect.left;
|
||||
const y = offset.y - containerRect.top;
|
||||
|
||||
// Check if there’s any change in the dragging state or the dragged element’s position
|
||||
// Compare the current drag state with the previously stored one
|
||||
const hasStateChanged =
|
||||
prevDragState.current.isDragging !== isDraggingWidgets || // Drag started or stopped
|
||||
prevDragState.current.x !== x || // X position changed
|
||||
prevDragState.current.y !== y; // Y position changed
|
||||
|
||||
if (hasStateChanged) {
|
||||
// Update the previous drag state with the latest values
|
||||
prevDragState.current = { isDragging: isDraggingWidgets, x, y };
|
||||
|
||||
if (isDraggingWidgets) {
|
||||
// Adjust the current drag coordinates relative to the initial widgets button position
|
||||
const getXPosition = props?.signBtnPosition?.[0]
|
||||
? x - props.signBtnPosition[0].xPos
|
||||
: x;
|
||||
const getYPosition = props?.signBtnPosition?.[0]
|
||||
? y - props.signBtnPosition[0].yPos
|
||||
: y;
|
||||
|
||||
// Get the default width and height of the dragged checkbox/radio widget type
|
||||
const el = document.getElementById("checkbox&radio-preview");
|
||||
let rect;
|
||||
if (el) {
|
||||
rect = el.getBoundingClientRect();
|
||||
}
|
||||
const widget = {
|
||||
type: itemType?.text,
|
||||
width: defaultWidthHeight(itemType?.text).width,
|
||||
height: defaultWidthHeight(itemType?.text).height
|
||||
};
|
||||
|
||||
const getWidth =
|
||||
rect?.width || props?.posWidth(widget, props?.isSignYourself);
|
||||
const getHeight =
|
||||
rect?.height || props?.posHeight(widget, props?.isSignYourself);
|
||||
|
||||
// Display the alignment guidelines based on the current drag position and widget size
|
||||
showGuidelines(true, getXPosition, getYPosition, getWidth, getHeight);
|
||||
} else {
|
||||
// Hide guidelines when dragging stops
|
||||
showGuidelines(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isDraggingWidgets, offset, itemType, showGuidelines]);
|
||||
}
|
||||
|
||||
export default DragGuideLinesLayer;
|
||||
@@ -21,6 +21,7 @@ function DropdownWidgetOption(props) {
|
||||
const [layout, setLayout] = useState("vertical");
|
||||
const statusArr = ["required", "optional"];
|
||||
const layoutArr = ["vertical", "horizontal"];
|
||||
const isPrefillExist = props?.roleName === "prefill";
|
||||
|
||||
const resetState = () => {
|
||||
setDropdownOptionList(["Option-1", "Option-2"]);
|
||||
@@ -87,7 +88,7 @@ function DropdownWidgetOption(props) {
|
||||
const deleteOption = true;
|
||||
const addOption = false;
|
||||
const getUpdatedOptions = dropdownOptionList.filter(
|
||||
(data, index) => index !== ind
|
||||
(_, index) => index !== ind
|
||||
);
|
||||
setDropdownOptionList(getUpdatedOptions);
|
||||
props.handleSaveWidgetsOptions(
|
||||
@@ -102,6 +103,14 @@ function DropdownWidgetOption(props) {
|
||||
};
|
||||
|
||||
const handleSaveOption = () => {
|
||||
if (["checkbox", radioButtonWidget, "dropdown"].includes(props.type)) {
|
||||
const allUnique =
|
||||
new Set(dropdownOptionList).size === dropdownOptionList.length;
|
||||
if (!allUnique) {
|
||||
alert("Please remove duplicate option");
|
||||
return;
|
||||
}
|
||||
}
|
||||
const defaultData =
|
||||
defaultCheckbox && defaultCheckbox.length > 0
|
||||
? defaultCheckbox
|
||||
@@ -228,37 +237,40 @@ function DropdownWidgetOption(props) {
|
||||
></i>
|
||||
</div>
|
||||
))}
|
||||
<i
|
||||
onClick={handleAddInput}
|
||||
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
|
||||
></i>
|
||||
<div>
|
||||
<i
|
||||
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
|
||||
aria-label="Add option"
|
||||
onClick={handleAddInput}
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||
<>
|
||||
<label className="text-[13px] font-semibold mt-[5px]">
|
||||
{t("default-value")}
|
||||
</label>
|
||||
<select
|
||||
onChange={(e) => setDefaultValue(e.target.value)}
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="defaultvalue"
|
||||
value={defaultValue}
|
||||
placeholder="select default value"
|
||||
>
|
||||
<option value="" disabled hidden className="text-[13px]">
|
||||
{t("select")}...
|
||||
</option>
|
||||
{dropdownOptionList.map((data, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
{props.type !== "checkbox" && (
|
||||
{["dropdown", radioButtonWidget].includes(props.type) &&
|
||||
!isPrefillExist && (
|
||||
<>
|
||||
<label className="text-[13px] font-semibold mt-[5px]">
|
||||
{t("default-value")}
|
||||
</label>
|
||||
<select
|
||||
value={defaultValue}
|
||||
onChange={(e) => setDefaultValue(e.target.value)}
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="defaultvalue"
|
||||
>
|
||||
<option value="" disabled hidden className="text-[13px]">
|
||||
{t("select")}...
|
||||
</option>
|
||||
{dropdownOptionList.map((data, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
{props.type !== "checkbox" && !isPrefillExist && (
|
||||
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
|
||||
{statusArr.map((data, ind) => (
|
||||
<div
|
||||
@@ -332,7 +344,7 @@ function DropdownWidgetOption(props) {
|
||||
props.type
|
||||
) && (
|
||||
<div className="flex flex-row gap-5 my-2 items-center text-center">
|
||||
{props.isShowAdvanceFeature && (
|
||||
{props.isShowAdvanceFeature && !isPrefillExist && (
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="isreadonly"
|
||||
@@ -416,7 +428,7 @@ function DropdownWidgetOption(props) {
|
||||
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-ghost ml-1"
|
||||
className="op-btn op-btn-ghost text-base-content ml-1"
|
||||
onClick={() => {
|
||||
props.handleClose && props.handleClose();
|
||||
resetState();
|
||||
|
||||
@@ -28,6 +28,7 @@ const EditTemplate = ({
|
||||
onSuccess,
|
||||
setPdfArrayBuffer,
|
||||
setPdfBase64Url,
|
||||
isAddYourSelfCheckbox,
|
||||
}) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
@@ -51,7 +52,7 @@ const EditTemplate = ({
|
||||
Bcc: template?.Bcc,
|
||||
RedirectUrl: template?.RedirectUrl || "",
|
||||
AllowModifications: template?.AllowModifications || false,
|
||||
TimeToCompleteDays: template?.TimeToCompleteDays || 15
|
||||
TimeToCompleteDays: template?.TimeToCompleteDays || 15,
|
||||
});
|
||||
const [isUpdate, setIsUpdate] = useState(false);
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
@@ -71,7 +72,7 @@ const EditTemplate = ({
|
||||
handleReplaceFileValdition(file);
|
||||
// You can handle the file here
|
||||
} else {
|
||||
alert("Only pdf files are allowed.");
|
||||
alert(t("only-pdf-allowed"));
|
||||
if (inputFileRef.current) inputFileRef.current.value = "";
|
||||
}
|
||||
};
|
||||
@@ -181,7 +182,8 @@ const EditTemplate = ({
|
||||
localStorage.getItem("TenantId") ||
|
||||
template?.ExtUserPtr?.TenantId?.objectId;
|
||||
const buffer = atob(uploadPdf.base64);
|
||||
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||
const userId = template?.ExtUserPtr?.UserId?.objectId;
|
||||
SaveFileSize(buffer.length, pdfUrl, tenantId, userId);
|
||||
}
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
||||
@@ -242,6 +244,7 @@ const EditTemplate = ({
|
||||
setShowConfirm(false);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen
|
||||
@@ -286,7 +289,7 @@ const EditTemplate = ({
|
||||
htmlFor="fileUpload"
|
||||
className="cursor-pointer text-center mb-0"
|
||||
>
|
||||
Browse or drag & drop a new file to replace the existng one
|
||||
{t("browse-or-drag-to-replace-existing-file")}
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
@@ -490,10 +493,11 @@ const EditTemplate = ({
|
||||
helptextZindex={50}
|
||||
helpText={t("bcc-help")}
|
||||
isCaptureAllData
|
||||
isAddYourSelfCheckbox={isAddYourSelfCheckbox}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Redirect Url</label>
|
||||
<label className="block">{t("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"
|
||||
|
||||
@@ -18,7 +18,13 @@ export function EmailBody(props) {
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
value={props.requestSubject}
|
||||
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||
onChange={(e) => {
|
||||
props?.setRequestSubject(e.target.value);
|
||||
props?.setCustomizeMail((prev) => ({
|
||||
...prev,
|
||||
subject: e.target.value
|
||||
}));
|
||||
}}
|
||||
placeholder='${senderName} has requested you to sign "${documentName}"'
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
|
||||
@@ -31,7 +31,6 @@ function EmailComponent({
|
||||
setIsLoading(true);
|
||||
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
|
||||
const sendmail = await Parse.Cloud.run("forwarddoc", params);
|
||||
console.log("sendmail ", sendmail);
|
||||
if (sendmail?.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setIsEmail(false);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
const Guidelines = ({ x1, x2, y1, y2 }) => {
|
||||
return (
|
||||
<>
|
||||
{/* Horizontal guidelines */}
|
||||
{/* top guide */}{" "}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] left-0 w-full border-t-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ top: y1 }}
|
||||
/>
|
||||
{/* bottom guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] left-0 w-full border-t-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ top: y2 }}
|
||||
/>
|
||||
{/* Vertical guidelines */}
|
||||
{/* left guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] top-0 h-full border-l-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ left: x1 }}
|
||||
/>
|
||||
{/* right guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] top-0 h-full border-l-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ left: x2 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Guidelines;
|
||||
@@ -61,14 +61,14 @@ export default function PageReorderModal({
|
||||
</span>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className="op-btn op-btn-xs op-btn-ghost"
|
||||
className="op-btn op-btn-xs op-btn-ghost text-base-content"
|
||||
disabled={i === 0}
|
||||
onClick={() => move(i, -1)}
|
||||
>
|
||||
<i className="fa-light fa-arrow-up"></i>
|
||||
</button>
|
||||
<button
|
||||
className="op-btn op-btn-xs op-btn-ghost"
|
||||
className="op-btn op-btn-xs op-btn-ghost text-base-content"
|
||||
disabled={i === order.length - 1}
|
||||
onClick={() => move(i, 1)}
|
||||
>
|
||||
@@ -84,7 +84,7 @@ export default function PageReorderModal({
|
||||
<button
|
||||
onClick={handleClose}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-1"
|
||||
className="op-btn op-btn-ghost text-base-content ml-1"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
|
||||
@@ -23,9 +23,6 @@ import { maxFileSize } from "../../constant/const";
|
||||
|
||||
function Header(props) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
props?.signerPos &&
|
||||
props?.signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const [isDeletePage, setIsDeletePage] = useState(false);
|
||||
@@ -71,17 +68,19 @@ function Header(props) {
|
||||
const handleFileUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
alert("Please upload a valid PDF file.");
|
||||
alert(t("please-select-pdf"));
|
||||
return;
|
||||
}
|
||||
if (!file.type.includes("pdf")) {
|
||||
alert("Only PDF files are allowed.");
|
||||
alert(t("only-pdf-allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
const mb = Math.round(file?.size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
const fileSize =
|
||||
maxFileSize;
|
||||
const pdfsize = file?.size;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
@@ -108,18 +107,18 @@ function Header(props) {
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
alert(t("incorrect-password-or-decryption-failed"));
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
alert(t("provide-password"));
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
@@ -138,6 +137,13 @@ function Header(props) {
|
||||
useObjectStreams: false
|
||||
});
|
||||
const pdfBuffer = base64ToArrayBuffer(pdfBase64);
|
||||
const pdfsize = pdfBuffer?.byteLength;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
props.setPdfArrayBuffer(pdfBuffer);
|
||||
props.setPdfBase64Url(pdfBase64);
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
@@ -162,6 +168,23 @@ function Header(props) {
|
||||
}
|
||||
setIsReorderModal(false);
|
||||
};
|
||||
|
||||
const handleDownloadDoc = async () => {
|
||||
await handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
);
|
||||
};
|
||||
const handleDownloadBtn = async () => {
|
||||
if (
|
||||
props?.isCompleted
|
||||
) {
|
||||
props?.setIsDownloadModal(true);
|
||||
} else {
|
||||
await handleDownloadDoc();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex py-[5px]">
|
||||
{isMobile && props?.isShowHeader ? (
|
||||
@@ -201,17 +224,7 @@ function Header(props) {
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => {
|
||||
if (props?.isCompleted) {
|
||||
props?.setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
);
|
||||
}
|
||||
}}
|
||||
onClick={() => handleDownloadBtn()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
@@ -221,25 +234,27 @@ function Header(props) {
|
||||
{t("download")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{props?.isCompleted && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="border-none bg-[#fff]">
|
||||
<i
|
||||
className="fa-light fa-award mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("certificate")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
{
|
||||
props?.isCompleted && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="border-none bg-[#fff]">
|
||||
<i
|
||||
className="fa-light fa-award mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{t("certificate")}
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)
|
||||
}
|
||||
{props?.isSignYourself && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
@@ -290,7 +305,7 @@ function Header(props) {
|
||||
<div
|
||||
onClick={() => {
|
||||
if (!props?.isMailSend) {
|
||||
props?.alertSendEmail();
|
||||
props?.handleSaveDoc();
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
@@ -348,13 +363,7 @@ function Header(props) {
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
onClick={() => handleDownloadDoc()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
@@ -474,42 +483,21 @@ function Header(props) {
|
||||
/>
|
||||
{props?.isPlaceholder ? (
|
||||
<>
|
||||
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
|
||||
{!props?.isMailSend &&
|
||||
props?.signersdata.length > 0 &&
|
||||
props?.signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span className="text-[13px] text-[#f5405e]">
|
||||
{t("add")}{" "}
|
||||
{props?.signersdata.length - filterPrefill.length}{" "}
|
||||
{t("recipients")} {t("widgets-name.signature")}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-[13px] text-[#f5405e]">
|
||||
{t("add")}{" "}
|
||||
{props?.signersdata.length - filterPrefill.length}{" "}
|
||||
{t("more")}
|
||||
{t("recipients")} {t("widgets-name.signature")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none"></div>
|
||||
<div className="flex">
|
||||
{props?.setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => props?.setIsEditTemplate(true)}
|
||||
className="outline-none border-none text-center mr-[3px]"
|
||||
>
|
||||
<i className="fa-light fa-gear fa-lg"></i>
|
||||
<i className="fa-light fa-gear fa-lg text-base-content"></i>
|
||||
</button>
|
||||
)}
|
||||
{enabledBackBtn && (
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
className="op-btn op-btn-ghost text-base-content op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
@@ -518,7 +506,7 @@ function Header(props) {
|
||||
disabled={props?.isMailSend && true}
|
||||
data-tut="headerArea"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => props?.alertSendEmail()}
|
||||
onClick={() => props?.handleSaveDoc()}
|
||||
>
|
||||
{props?.completeBtnTitle
|
||||
? props?.completeBtnTitle
|
||||
@@ -544,38 +532,32 @@ function Header(props) {
|
||||
></i>
|
||||
<span className="hidden lg:block">{t("print")}</span>
|
||||
</button>
|
||||
{props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">{t("certificate")}</span>
|
||||
</button>
|
||||
)}
|
||||
{
|
||||
props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">
|
||||
{t("certificate")}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => {
|
||||
if (props?.isCompleted) {
|
||||
props?.setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
);
|
||||
}
|
||||
}}
|
||||
onClick={() => handleDownloadBtn()}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
@@ -590,15 +572,9 @@ function Header(props) {
|
||||
<>
|
||||
{props?.templateId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
onClick={() => handleDownloadDoc()}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
className="op-btn op-btn-ghost text-base-content op-btn-sm mr-[3px]"
|
||||
>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
@@ -614,14 +590,8 @@ function Header(props) {
|
||||
{!props?.templateId && (
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading,
|
||||
props.pdfBase64
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-ghost text-base-content op-btn-sm mr-[3px]"
|
||||
onClick={() => handleDownloadDoc()}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
@@ -640,23 +610,25 @@ function Header(props) {
|
||||
)
|
||||
) : props?.isCompleted ? (
|
||||
<div className="flex flex-row">
|
||||
{props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa-light fa-award" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
{t("certificate")}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
{
|
||||
props?.isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
props?.pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa-light fa-award" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
{t("certificate")}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, props?.pdfDetails)
|
||||
@@ -670,7 +642,8 @@ function Header(props) {
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() => props?.setIsDownloadModal(true)}
|
||||
// onClick={() => props?.setIsDownloadModal(true)}
|
||||
onClick={() => handleDownloadBtn()}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("download")}</span>
|
||||
@@ -699,7 +672,7 @@ function Header(props) {
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
className="op-btn op-btn-ghost text-base-content op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
@@ -744,8 +717,8 @@ function Header(props) {
|
||||
handleClose={() => setIsDeletePage(false)}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<p className="font-medium">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3">{t("delete-note")}</p>
|
||||
<p className="font-medium text-base-content">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3 text-base-content">{t("delete-note")}</p>
|
||||
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => handleDetelePage()}
|
||||
@@ -757,7 +730,7 @@ function Header(props) {
|
||||
<button
|
||||
onClick={() => setIsDeletePage(false)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PDFDocument } from "pdf-lib";
|
||||
import { maxFileSize } from "../../constant/const";
|
||||
import PageReorderModal from "./PageReorderModal";
|
||||
|
||||
function PdfZoom(props) {
|
||||
function PdfTools(props) {
|
||||
const { t } = useTranslation();
|
||||
const mergePdfInputRef = useRef(null);
|
||||
const [isDeletePage, setIsDeletePage] = useState(false);
|
||||
@@ -57,18 +57,22 @@ function PdfZoom(props) {
|
||||
};
|
||||
|
||||
const handleFileUpload = async (e) => {
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
alert("Please upload a valid PDF file.");
|
||||
alert(t("please-select-pdf"));
|
||||
return;
|
||||
}
|
||||
if (!file.type.includes("pdf")) {
|
||||
alert("Only PDF files are allowed.");
|
||||
alert(t("only-pdf-allowed"));
|
||||
return;
|
||||
}
|
||||
const mb = Math.round(file?.size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
const fileSize =
|
||||
maxFileSize;
|
||||
const pdfsize = file?.size;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
@@ -95,18 +99,18 @@ function PdfZoom(props) {
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
alert(t("incorrect-password-or-decryption-failed"));
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
alert(t("provide-password"));
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
@@ -125,6 +129,13 @@ function PdfZoom(props) {
|
||||
useObjectStreams: false
|
||||
});
|
||||
const pdfBuffer = base64ToArrayBuffer(pdfBase64);
|
||||
const pdfsize = pdfBuffer?.byteLength;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
props.setPdfArrayBuffer(pdfBuffer);
|
||||
props.setPdfBase64Url(pdfBase64);
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
@@ -150,13 +161,42 @@ function PdfZoom(props) {
|
||||
setIsReorderModal(false);
|
||||
};
|
||||
|
||||
const handleDeletePage = () => {
|
||||
setIsDeletePage(true);
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
|
||||
const handleReorderPages = () => {
|
||||
setIsReorderModal(true);
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
|
||||
const handleZoomIn = () => {
|
||||
props.clickOnZoomIn();
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
const handleZoomOut = () => {
|
||||
props.clickOnZoomOut();
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
const handleRotate = () => {
|
||||
props.handleRotationFun(90);
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
const handleAntiRotate = () => {
|
||||
props.handleRotationFun(-90);
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[42px]">
|
||||
<span
|
||||
data-tut="pdftools"
|
||||
className="hidden h-max md:flex flex-col gap-1 text-center md:w-[5%] mt-[42px]"
|
||||
>
|
||||
{!props.isDisableEditTools && (
|
||||
<>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => mergePdfInputRef.current.click()}
|
||||
title={t("add-pages")}
|
||||
>
|
||||
@@ -170,15 +210,15 @@ function PdfZoom(props) {
|
||||
<i className="fa-light fa-plus text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => setIsDeletePage(true)}
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={handleDeletePage}
|
||||
title={t("delete-page")}
|
||||
>
|
||||
<i className="fa-light fa-trash text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => setIsReorderModal(true)}
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={handleReorderPages}
|
||||
title={t("reorder-pages")}
|
||||
>
|
||||
<i className="fa-light fa-list-ol text-gray-500 2xl:text-[25px]"></i>
|
||||
@@ -187,7 +227,7 @@ function PdfZoom(props) {
|
||||
)}
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => props.clickOnZoomIn()}
|
||||
onClick={handleZoomIn}
|
||||
title={t("zoom-in")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[25px]"></i>
|
||||
@@ -197,7 +237,7 @@ function PdfZoom(props) {
|
||||
<>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => props.handleRotationFun(90)}
|
||||
onClick={handleRotate}
|
||||
title={t("rotate-right")}
|
||||
>
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[25px]"></i>
|
||||
@@ -205,18 +245,15 @@ function PdfZoom(props) {
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
title={t("rotate-left")}
|
||||
onClick={() => props.handleRotationFun(-90)}
|
||||
onClick={handleAntiRotate}
|
||||
>
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[25px]"></i>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px]"
|
||||
onClick={() => props.clickOnZoomOut()}
|
||||
style={{
|
||||
cursor: props.zoomPercent > 0 ? "pointer" : "default"
|
||||
}}
|
||||
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={handleZoomOut}
|
||||
title={t("zoom-out")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
|
||||
@@ -229,8 +266,8 @@ function PdfZoom(props) {
|
||||
handleClose={() => setIsDeletePage(false)}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<p className="font-medium">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3">{t("delete-note")}</p>
|
||||
<p className="font-medium text-base-content">{t("delete-alert-2")}</p>
|
||||
<p className="pt-3 text-base-content">{t("delete-note")}</p>
|
||||
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||
<button
|
||||
onClick={() => handleDetelePage()}
|
||||
@@ -242,7 +279,7 @@ function PdfZoom(props) {
|
||||
<button
|
||||
onClick={() => setIsDeletePage(false)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-1"
|
||||
className="op-btn op-btn-ghost text-base-content ml-1"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
@@ -258,4 +295,4 @@ function PdfZoom(props) {
|
||||
);
|
||||
}
|
||||
|
||||
export default PdfZoom;
|
||||
export default PdfTools;
|
||||
@@ -1,14 +1,13 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import {
|
||||
handleCopyNextToWidget,
|
||||
randomId,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import { handleCopyNextToWidget, randomId } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setPrefillImg } from "../../redux/reducers/widgetSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const copyType = [
|
||||
{ id: 1, type: "All pages" },
|
||||
{ id: 2, type: "All pages but last" },
|
||||
@@ -125,14 +124,19 @@ function PlaceholderCopy(props) {
|
||||
);
|
||||
for (let i = 0; i < props.allPages; i++) {
|
||||
const newId = randomId();
|
||||
const newPlaceholder = { ...currentPlaceholder, key: newId };
|
||||
const nameId = randomId(2);
|
||||
const widgetName = `${currentPlaceholder?.options?.name}${nameId}`;
|
||||
const newPlaceholder = {
|
||||
...currentPlaceholder,
|
||||
key: newId,
|
||||
options: { ...currentPlaceholder?.options, name: widgetName }
|
||||
};
|
||||
//get exist placeholder position for particular page
|
||||
const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
@@ -144,6 +148,18 @@ function PlaceholderCopy(props) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
newPageNumber++;
|
||||
if (
|
||||
filterSignerPosition[0]?.Role === "prefill" &&
|
||||
currentPlaceholder?.options?.response &&
|
||||
currentPlaceholder?.type === "image"
|
||||
) {
|
||||
dispatch(
|
||||
setPrefillImg({
|
||||
id: newId,
|
||||
base64: currentPlaceholder?.options?.response
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
let updatedSignerPlaceholder;
|
||||
if (props?.signerObjId) {
|
||||
@@ -213,6 +229,7 @@ function PlaceholderCopy(props) {
|
||||
|
||||
//function for getting selected type placeholder copy
|
||||
const handleApplyCopy = () => {
|
||||
const newId = randomId();
|
||||
if (selectCopyType === 4) {
|
||||
const signerPosition = props.xyPosition;
|
||||
let currentXYposition;
|
||||
@@ -230,13 +247,25 @@ function PlaceholderCopy(props) {
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
newId,
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPosition,
|
||||
props.pageNumber,
|
||||
props.setXyPosition,
|
||||
props?.Id
|
||||
);
|
||||
if (
|
||||
filterSignerPosition[0]?.Role === "prefill" &&
|
||||
currentXYposition?.options?.response &&
|
||||
currentXYposition?.type === "image"
|
||||
) {
|
||||
dispatch(
|
||||
setPrefillImg({
|
||||
id: newId,
|
||||
base64: currentXYposition?.options?.response
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const getIndex = props?.xyPosition.findIndex(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
@@ -248,8 +277,8 @@ function PlaceholderCopy(props) {
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
newId,
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPosition,
|
||||
getIndex,
|
||||
props.setXyPosition
|
||||
@@ -260,11 +289,6 @@ function PlaceholderCopy(props) {
|
||||
}
|
||||
};
|
||||
const handleUniqueId = () => {
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
if (signerId && props.widgetType === textWidget && props.setTempSignerId) {
|
||||
props.setUniqueId(props?.tempSignerId);
|
||||
props.setTempSignerId("");
|
||||
}
|
||||
props.setIsPageCopy(false);
|
||||
setSelectCopyType(1);
|
||||
};
|
||||
@@ -306,7 +330,7 @@ function PlaceholderCopy(props) {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
className="op-btn op-btn-ghost text-base-content ml-2"
|
||||
onClick={() => handleUniqueId()}
|
||||
>
|
||||
{t("cancel")}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState, forwardRef } from "react";
|
||||
import { useEffect, useMemo, useState, forwardRef } from "react";
|
||||
import {
|
||||
getMonth,
|
||||
getYear,
|
||||
@@ -9,29 +9,36 @@ import {
|
||||
months,
|
||||
years,
|
||||
selectCheckbox,
|
||||
checkRegularExpress
|
||||
checkRegularExpress,
|
||||
isBase64,
|
||||
getSignerPages
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import "../../styles/signature.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CellsWidget from "./CellsWidget";
|
||||
import { useSelector } from "react-redux";
|
||||
import Loader from "../../primitives/Loader";
|
||||
|
||||
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";
|
||||
const selectWidgetCls =
|
||||
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
|
||||
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap";
|
||||
const widgetCls =
|
||||
"select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center";
|
||||
function PlaceholderType(props) {
|
||||
const selectWidgetCls = `w-full h-full absolute left-0 top-0 focus:outline-none text-base-content`;
|
||||
const { t } = useTranslation();
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const prefillImgLoad = useSelector((state) => state.widget.prefillImgLoad);
|
||||
const type = props?.pos?.type;
|
||||
const iswidgetEnable =
|
||||
props.isSignYourself ||
|
||||
((props.isSelfSign || props.isNeedSign) &&
|
||||
props.data?.signerObjId === props.signerObjId);
|
||||
const isReadOnly =
|
||||
props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId;
|
||||
props?.data?.Role !== "prefill" &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId);
|
||||
// prefer the latest response value over any default value
|
||||
const widgetData =
|
||||
props.pos?.options?.response ?? props.pos?.options?.defaultValue ?? "";
|
||||
@@ -39,6 +46,7 @@ function PlaceholderType(props) {
|
||||
const [widgetValue, setwidgetValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
const [hint, setHint] = useState("");
|
||||
const [imgUrl, setImgUrl] = useState("");
|
||||
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||
const fontColor = props.pos.options?.fontColor || "black";
|
||||
const textWidgetStyle = {
|
||||
@@ -51,7 +59,6 @@ function PlaceholderType(props) {
|
||||
display: "flex",
|
||||
height: "100%"
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (type !== "date") {
|
||||
if (type && type === "checkbox") {
|
||||
@@ -60,18 +67,21 @@ function PlaceholderType(props) {
|
||||
props?.pos?.options?.defaultValue ||
|
||||
[]
|
||||
);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// keep displayed value in sync with the stored response
|
||||
setwidgetValue(widgetData);
|
||||
}
|
||||
if (props.pos?.options?.hint) {
|
||||
setHint(props.pos?.options.hint);
|
||||
} else if (props.pos?.options?.validation?.type) {
|
||||
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
||||
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos]);
|
||||
}, [props.pos, widgetData, type]);
|
||||
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
style={{
|
||||
@@ -79,8 +89,7 @@ function PlaceholderType(props) {
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
className={`${isReadOnly ? `select-none` : ``} ${selectWidgetCls} overflow-hidden`}
|
||||
disabled={isReadOnly}
|
||||
className={`${isReadOnly ? `select-none opacity-25` : ``} ${selectWidgetCls} overflow-hidden`}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
@@ -100,6 +109,26 @@ function PlaceholderType(props) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
//function is used to get prefill image's signedUrl after expired
|
||||
useEffect(() => {
|
||||
const loadImage = async () => {
|
||||
const isBase64Url = isBase64(props?.pos?.SignUrl);
|
||||
if (
|
||||
props.pos.SignUrl &&
|
||||
props.pos.type === "image" &&
|
||||
props?.data?.Role === "prefill" &&
|
||||
!isBase64Url
|
||||
) {
|
||||
const getPrefillImg = prefillImg?.find((x) => x.id === props.pos.key);
|
||||
if (getPrefillImg) {
|
||||
setImgUrl(getPrefillImg?.base64);
|
||||
}
|
||||
} else {
|
||||
setImgUrl(props.pos.SignUrl);
|
||||
}
|
||||
};
|
||||
loadImage();
|
||||
}, [props.pos.SignUrl]);
|
||||
|
||||
switch (type) {
|
||||
case "signature":
|
||||
@@ -108,7 +137,7 @@ function PlaceholderType(props) {
|
||||
alt="signature"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls "
|
||||
className={`${props.pos.signatureType !== "type" ? "object-contain" : ""} w-full h-full select-none-cls`}
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
@@ -132,7 +161,7 @@ function PlaceholderType(props) {
|
||||
alt="stamp"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
className="w-full h-full select-none-cls object-contain"
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
@@ -154,9 +183,9 @@ function PlaceholderType(props) {
|
||||
const checkBoxLayout = props.pos.options?.layout || "vertical";
|
||||
const isMultipleCheckbox =
|
||||
props.pos.options?.values?.length > 0 ? true : false;
|
||||
const checkBoxWrapperClass = `flex items-start ${
|
||||
const checkBoxWrapperClass = `flex items-start whitespace-pre-wrap ${
|
||||
checkBoxLayout === "horizontal"
|
||||
? `flex-row flex-wrap ${isMultipleCheckbox ? "gap-x-2" : ""}`
|
||||
? `flex-row flex-wrap lg:py-[1.6px] ${isMultipleCheckbox ? "gap-x-2" : ""}`
|
||||
: `flex-col ${isMultipleCheckbox ? "gap-y-[3px]" : ""}`
|
||||
}`; // Using gap-y-1 for consistency, adjust if needed
|
||||
|
||||
@@ -229,7 +258,7 @@ function PlaceholderType(props) {
|
||||
height={height}
|
||||
value={cells.join("")}
|
||||
editable={isEditable}
|
||||
resizable={isEditable}
|
||||
resizable={props?.isAllowModify}
|
||||
fontSize={fontSize}
|
||||
fontColor={fontColor}
|
||||
hint={hint}
|
||||
@@ -253,7 +282,7 @@ function PlaceholderType(props) {
|
||||
alt="initials"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
className={`${props.pos.signatureType !== "type" ? "object-contain" : ""} w-full h-full select-none-cls`}
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
@@ -338,7 +367,7 @@ function PlaceholderType(props) {
|
||||
</div>
|
||||
);
|
||||
case "date":
|
||||
return iswidgetEnable ? (
|
||||
return iswidgetEnable || props?.data?.Role === "prefill" ? (
|
||||
<DatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
<div className="flex justify-start ml-2 ">
|
||||
@@ -375,11 +404,9 @@ function PlaceholderType(props) {
|
||||
popperPlacement="top-end"
|
||||
customInput={<ExampleCustomInput />}
|
||||
dateFormat={
|
||||
props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/yyyy"
|
||||
props?.selectDate?.format ||
|
||||
props.pos?.options?.validation?.format ||
|
||||
"MM/dd/yyyy"
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
@@ -388,21 +415,23 @@ function PlaceholderType(props) {
|
||||
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"}
|
||||
{props?.selectDate?.format ||
|
||||
props.pos?.options?.validation?.format ||
|
||||
"MM/dd/yyyy"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
case "image":
|
||||
return props.pos.SignUrl ? (
|
||||
return prefillImgLoad[props.pos?.key] ? (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-white/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
) : imgUrl ? (
|
||||
<img
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
className="w-full h-full select-none-cls"
|
||||
src={imgUrl}
|
||||
className="w-full h-full select-none-cls object-contain"
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
@@ -445,9 +474,9 @@ function PlaceholderType(props) {
|
||||
case radioButtonWidget:
|
||||
const radioLayout = props.pos.options?.layout || "vertical";
|
||||
const isOnlyOneBtn = props.pos.options?.values?.length > 0 ? true : false;
|
||||
const radioWrapperClass = `flex items-start ${
|
||||
const radioWrapperClass = `flex items-start whitespace-pre-wrap ${
|
||||
radioLayout === "horizontal"
|
||||
? `flex-row flex-wrap ${isOnlyOneBtn ? "gap-x-[10px]" : ""}`
|
||||
? `flex-row flex-wrap lg:py-[1.6px] ${isOnlyOneBtn ? "gap-x-[10px]" : ""}`
|
||||
: `flex-col ${isOnlyOneBtn ? "gap-y-[5px]" : ""}`
|
||||
}`; // Using gap-y-1 for consistency, adjust if needed
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,762 @@
|
||||
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import {
|
||||
getMonth,
|
||||
getYear,
|
||||
radioButtonWidget,
|
||||
compressedFileSize,
|
||||
textWidget,
|
||||
months,
|
||||
changeDateToMomentFormat,
|
||||
convertBase64ToFile,
|
||||
generatePdfName,
|
||||
isBase64
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { range } from "pdf-lib";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import moment from "moment";
|
||||
import AsyncSelect from "react-select/async";
|
||||
import axios from "axios";
|
||||
import AddContact from "../../primitives/AddContact";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
resetWidgetState,
|
||||
setPrefillImg
|
||||
} from "../../redux/reducers/widgetSlice";
|
||||
import * as utils from "../../utils";
|
||||
|
||||
const widgetTitle = "font-medium";
|
||||
|
||||
const ShowTextWidget = ({ position, handleWidgetDetails }) => {
|
||||
const inputRef = useRef(null);
|
||||
const [inputValue, setInputValue] = useState(position.options.response || "");
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={inputRef}
|
||||
rows={1}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
handleWidgetDetails(position, e.target.value);
|
||||
}}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ImageComponent = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const imageRefs = useRef({});
|
||||
|
||||
let imgUrl = "";
|
||||
const isBase64Url = isBase64(props?.position?.SignUrl);
|
||||
if (isBase64Url) {
|
||||
imgUrl = props?.position?.SignUrl;
|
||||
} else {
|
||||
const getPrefillImg = prefillImg?.find(
|
||||
(x) => x.id === props?.position?.key
|
||||
);
|
||||
imgUrl = getPrefillImg?.base64;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{props?.position.options?.name}</span>
|
||||
{imgUrl ? (
|
||||
<>
|
||||
<div className="cursor-pointer op-card border-[1px] border-gray-400 flex flex-col w-full h-full justify-center items-center ">
|
||||
<img
|
||||
alt="print img"
|
||||
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamicallys
|
||||
src={imgUrl}
|
||||
draggable="false"
|
||||
className="object-contain h-full w-full aspect-[5/2]"
|
||||
onLoad={() => props?.handleImageLoaded?.(props?.position.key)}
|
||||
onError={() => props?.handleImageLoaded?.(props?.position.key)}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
onClick={() => props?.handleClearImage(props?.position)}
|
||||
className="flex justify-start text-blue-500 underline cursor-pointer"
|
||||
>
|
||||
{t("clear")}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className="cursor-pointer op-card border-[1px] op-border-hover flex flex-col overflow-hidden w-full h-full aspect-[5/2] justify-center items-center"
|
||||
onClick={() => imageRefs.current[props?.position.key]?.click()}
|
||||
>
|
||||
{props?.imageLoaders[props?.position?.key] && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-white/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => props?.onImageChange?.(e, props?.position)}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={(el) => (imageRefs.current[props?.position.key] = el)} // Assign ref dynamically
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light text-base-content fa-cloud-upload-alt text-[25px]"></i>
|
||||
<div className="text-[10px] text-base-content">{t("upload")}</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function PrefillWidgetModal(props) {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
// Track already loaded image keys so they don't increment multiple times
|
||||
const loadedSet = useRef(new Set());
|
||||
const initializedRef = useRef(false); // prevent rerun on state updates
|
||||
const [image, setImage] = useState(null);
|
||||
const [imageLoaders, setImageLoaders] = useState({});
|
||||
const [currentWidget, setCurrentWidget] = useState("");
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [totalImages, setTotalImages] = useState(0);
|
||||
const [loadedImages, setLoadedImages] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const years = range(1950, getYear(new Date()) + 16, 1);
|
||||
const isAnyLoaderActive = Object.values(imageLoaders).some(
|
||||
(val) => val === true
|
||||
);
|
||||
|
||||
// useMemo to memoize the calculation of unique widgets
|
||||
const uniqueWidget = useMemo(() => {
|
||||
//functions to used remove duplicate name values across all pages
|
||||
if (!props.prefillData) return [];
|
||||
//This will help us track which name values have already been encountered across all pages.
|
||||
const uniqueNames = new Set();
|
||||
//Filter and flatten placeholder widgets while keeping unique names
|
||||
const filteredArray = props.prefillData?.placeHolder?.map((item) => ({
|
||||
...item,
|
||||
pos: item.pos.filter((curr) => {
|
||||
if (uniqueNames.has(curr?.options?.name)) return false; //Duplicate name found, remove it
|
||||
uniqueNames.add(curr?.options?.name); //First time seen, add to set
|
||||
return true;
|
||||
})
|
||||
}));
|
||||
//latten the filtered array and exclude read-only widgets
|
||||
const flatArray = filteredArray?.flatMap((page) =>
|
||||
page.pos
|
||||
.filter((widget) => !widget.options?.isReadOnly)
|
||||
.map((widget) => ({
|
||||
widget,
|
||||
pageNumber: page.pageNumber
|
||||
}))
|
||||
);
|
||||
|
||||
return flatArray || [];
|
||||
}, [props.prefillData]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(resetWidgetState([]));
|
||||
}, []);
|
||||
// Reset loader state when modal closes
|
||||
useEffect(() => {
|
||||
if (!props?.isPrefillModal) {
|
||||
initializedRef.current = false;
|
||||
setTotalImages(0);
|
||||
setLoadedImages(0);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [props?.isPrefillModal]);
|
||||
|
||||
useEffect(() => {
|
||||
//function is used to save all image base64 in redux state to display prefill images
|
||||
const savePrefillImg = async () => {
|
||||
const prefillImg = await utils?.savePrefillImg(props.xyPosition);
|
||||
if (Array.isArray(prefillImg)) {
|
||||
prefillImg.forEach((img) => dispatch(setPrefillImg(img)));
|
||||
}
|
||||
setImageLoaders({});
|
||||
};
|
||||
savePrefillImg();
|
||||
}, [props.xyPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
if (totalImages > 0 && loadedImages === totalImages) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [loadedImages, totalImages]);
|
||||
useEffect(() => {
|
||||
if (image?.src) {
|
||||
handleWidgetDetails(currentWidget);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [image]);
|
||||
|
||||
// Run only once per modal open
|
||||
useEffect(() => {
|
||||
if (props?.isPrefillModal && !initializedRef.current) {
|
||||
const getImgWidgets = uniqueWidget?.filter(
|
||||
(w) => w.widget?.type === "image" && w.widget?.options?.response
|
||||
);
|
||||
if (getImgWidgets?.length > 0) {
|
||||
const imgCount = getImgWidgets.length;
|
||||
setTotalImages(imgCount);
|
||||
setLoadedImages(0);
|
||||
setLoading(true);
|
||||
initializedRef.current = true; // mark as initialized
|
||||
}
|
||||
}
|
||||
}, [props?.isPrefillModal, uniqueWidget]);
|
||||
|
||||
// The getDatePickerDate function retrieves the date in the correct format supported by the DatePicker.
|
||||
const getDatePickerDate = (selectedDate, format = "dd-MM-yyyy") => {
|
||||
let date;
|
||||
if (format && format === "dd-MM-yyyy") {
|
||||
const [day, month, year] = selectedDate?.split("-");
|
||||
date = new Date(`${year}-${month}-${day}`);
|
||||
} else if (format && format === "dd.MM.yyyy") {
|
||||
const [day, month, year] = selectedDate?.split(".");
|
||||
date = new Date(`${year}.${month}.${day}`);
|
||||
} else if (format && format === "dd/MM/yyyy") {
|
||||
const [day, month, year] = selectedDate?.split("/");
|
||||
date = new Date(`${year}/${month}/${day}`);
|
||||
} else {
|
||||
date = new Date(selectedDate);
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
style={{ fontFamily: "Arial, sans-serif" }}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full"
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className="fa-light fa-calendar ml-[5px]"></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
|
||||
const handleDate = (position) => {
|
||||
// The getDatePickerDate function retrieves the date in the correct format supported by the DatePicker.
|
||||
const getDate = getDatePickerDate(
|
||||
position?.options.response || new Date(),
|
||||
position?.options?.validation?.format
|
||||
);
|
||||
return getDate;
|
||||
};
|
||||
//function to set date with required date format onchange date
|
||||
const handleOnDateChange = (date, position) => {
|
||||
const format = position?.options?.validation?.format || "MM/dd/yyyy";
|
||||
let updateDate = date;
|
||||
let newDate;
|
||||
const isSpecialDateFormat =
|
||||
format && ["dd-MM-yyyy", "dd.MM.yyyy", "dd/MM/yyyy"].includes(format);
|
||||
if (isSpecialDateFormat) {
|
||||
newDate = moment(updateDate).format(changeDateToMomentFormat(format));
|
||||
} else {
|
||||
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
|
||||
newDate = new Date(updateDate);
|
||||
newDate = moment(newDate.getTime()).format(
|
||||
changeDateToMomentFormat(format)
|
||||
);
|
||||
}
|
||||
handleWidgetDetails(position, newDate);
|
||||
};
|
||||
|
||||
const handleSavePrefillImg = async (widgetDetails) => {
|
||||
setImageLoaders((prev) => ({ ...prev, [widgetDetails?.key]: true }));
|
||||
try {
|
||||
const imageName = generatePdfName(16);
|
||||
const imageUrl = await convertBase64ToFile(
|
||||
imageName,
|
||||
image.src,
|
||||
image.imgType
|
||||
);
|
||||
if (imageUrl) {
|
||||
return imageUrl;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error in handleSavePrefillImg function ", e);
|
||||
}
|
||||
};
|
||||
//function is used to handle prefill widgets details and check if there are any duplicate widget name field exist then update all duplicate value
|
||||
const handleWidgetDetails = async (widgetDetails, response) => {
|
||||
const widgetName = widgetDetails?.options?.name;
|
||||
const getPrefill = props.xyPosition.find((x) => x?.Role === "prefill");
|
||||
const getPlaceholder = getPrefill?.placeHolder;
|
||||
let imgUrl;
|
||||
if (widgetDetails?.type === "image") {
|
||||
imgUrl = await handleSavePrefillImg(widgetDetails);
|
||||
}
|
||||
const updatedData = getPlaceholder.map((page) => ({
|
||||
...page,
|
||||
pos: page.pos.map((item) => {
|
||||
if (item.options.name === widgetName) {
|
||||
if (widgetDetails?.type === "image") {
|
||||
return {
|
||||
...item,
|
||||
SignUrl: imgUrl,
|
||||
ImageType: image.imgType,
|
||||
options: { ...item.options, response: imgUrl }
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...item,
|
||||
options: { ...item.options, response: response }
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
})
|
||||
}));
|
||||
const newUpdateSigner = props.xyPosition.map((obj) => {
|
||||
if (obj.Role === "prefill") {
|
||||
return { ...obj, placeHolder: updatedData };
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
props.setXyPosition(newUpdateSigner);
|
||||
};
|
||||
|
||||
//function for set checked and unchecked value of checkbox
|
||||
const handleCheckboxValue = (isChecked, ind, position) => {
|
||||
let updateSelectedCheckbox = [];
|
||||
updateSelectedCheckbox =
|
||||
position.options?.defaultValue || position.options?.response || [];
|
||||
if (isChecked) {
|
||||
updateSelectedCheckbox.push(ind);
|
||||
} else {
|
||||
updateSelectedCheckbox = updateSelectedCheckbox.filter(
|
||||
(data) => data !== ind
|
||||
);
|
||||
}
|
||||
handleWidgetDetails(position, updateSelectedCheckbox);
|
||||
};
|
||||
|
||||
//function for image upload or update
|
||||
const onImageChange = (event, position) => {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
const file = event.target.files[0];
|
||||
compressedFileSize(file, setImage);
|
||||
setCurrentWidget(position);
|
||||
}
|
||||
};
|
||||
const handleRadioCheck = (data, position) => {
|
||||
const res = position?.options?.response;
|
||||
const defaultCheck = position?.options?.defaultValue;
|
||||
if (res === data || defaultCheck === data) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//function for show checked checkbox
|
||||
const selectCheckbox = (ind, position) => {
|
||||
const res = position?.options?.response;
|
||||
const defaultCheck = position?.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 handleClearImage = (position) => {
|
||||
let prefillPlaceholder = props?.xyPosition.filter(
|
||||
(data) => data?.Role === "prefill"
|
||||
);
|
||||
const updatedArray = prefillPlaceholder[0]?.placeHolder?.map((page) => ({
|
||||
...page,
|
||||
pos: page.pos.map((item) => {
|
||||
if (item.options.name === position.options.name) {
|
||||
return {
|
||||
...item,
|
||||
SignUrl: "",
|
||||
options: {
|
||||
...position.options,
|
||||
response: ""
|
||||
}
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
}));
|
||||
const newUpdateSigner = props.xyPosition.map((obj) => {
|
||||
if (obj.Role === "prefill") {
|
||||
return { ...obj, placeHolder: updatedArray };
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
props.setXyPosition(newUpdateSigner);
|
||||
};
|
||||
const handleImageLoaded = (key) => {
|
||||
// Prevent counting the same image multiple times.
|
||||
// If this image (key) has not already been marked as loaded...
|
||||
if (!loadedSet.current.has(key)) {
|
||||
// Mark this image as loaded by adding its key to the Set
|
||||
loadedSet.current.add(key);
|
||||
// Increment the loadedImages state by 1
|
||||
// (tracks how many images have finished loading)
|
||||
setLoadedImages((prev) => prev + 1);
|
||||
}
|
||||
setImageLoaders({});
|
||||
};
|
||||
const handleWidgetType = (position) => {
|
||||
switch (position?.type) {
|
||||
case "checkbox":
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<div className="flex flex-col gap-y-1">
|
||||
{position.options?.values?.map((data, ind) => (
|
||||
<div
|
||||
key={ind}
|
||||
className="select-none-cls flex items-center text-center gap-0.5"
|
||||
>
|
||||
<input
|
||||
id={`checkbox-${position.key + ind}`}
|
||||
className="mt-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
checked={selectCheckbox(ind, position)}
|
||||
onChange={(e) =>
|
||||
handleCheckboxValue(e.target.checked, ind, position)
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`checkbox-${position.key + ind}`}
|
||||
className="text-xs mb-0 text-center ml-[3px] cursor-pointer"
|
||||
>
|
||||
{data}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case textWidget:
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<ShowTextWidget
|
||||
position={position}
|
||||
handleWidgetDetails={handleWidgetDetails}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case "dropdown":
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<select
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-base-content w-full"
|
||||
id="myDropdown"
|
||||
value={
|
||||
position?.options?.response || position?.options?.defaultValue
|
||||
}
|
||||
onChange={(e) => handleWidgetDetails(position, e.target.value)}
|
||||
>
|
||||
{/* Default/Title option */}
|
||||
<option value="" disabled hidden>
|
||||
{t("choose-one")}
|
||||
</option>
|
||||
{position?.options?.values?.map((data, ind) => (
|
||||
<option key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<DatePicker
|
||||
portalId="datepicker-portal-root"
|
||||
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>
|
||||
)}
|
||||
closeOnScroll={true}
|
||||
selected={handleDate(position)}
|
||||
onChange={(date) => handleOnDateChange(date, position)}
|
||||
customInput={<ExampleCustomInput />}
|
||||
dateFormat={position?.options?.validation?.format || "MM/dd/yyyy"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
<ImageComponent
|
||||
position={position}
|
||||
imageLoaders={imageLoaders}
|
||||
onImageChange={onImageChange}
|
||||
handleImageLoaded={handleImageLoaded}
|
||||
handleClearImage={handleClearImage}
|
||||
/>
|
||||
);
|
||||
case radioButtonWidget:
|
||||
return (
|
||||
<>
|
||||
<span className={widgetTitle}>{position.options?.name}</span>
|
||||
<div className="flex flex-col gap-y-1">
|
||||
{position.options?.values.map((data, ind) => (
|
||||
<div
|
||||
key={ind}
|
||||
className="select-none-cls flex items-center text-center gap-0.5"
|
||||
>
|
||||
<input
|
||||
id={`radio-${position.key + ind}`}
|
||||
className="mt-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
checked={handleRadioCheck(data, position)}
|
||||
onChange={() => handleWidgetDetails(position, data)}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`radio-${position.key + ind}`}
|
||||
className="text-xs mb-0 ml-[2px] cursor-pointer"
|
||||
>
|
||||
{data}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return position?.SignUrl ? (
|
||||
<div className="pointer-events-none">
|
||||
<img
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={position?.SignUrl}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full">
|
||||
No widget
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEmbedPrefill = async (item) => {
|
||||
await props.handleCreateDocument();
|
||||
};
|
||||
//`loadOptions` function to use show all list of signer in dropdown
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsigners`;
|
||||
const token = {
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const search = inputValue;
|
||||
const axiosRes = await axios.post(url, { search }, { headers });
|
||||
const contactRes = axiosRes?.data?.result || [];
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
const result = res;
|
||||
setUserList(result);
|
||||
return await result.map((item) => ({
|
||||
label: `${item.Name}<${item.Email}>`,
|
||||
value: item.objectId
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
//`handleInputChange` function to get signers list from dropdown
|
||||
const handleInputChange = (item, id) => {
|
||||
const signerExist = props.forms.some((x) => x.label === item.label);
|
||||
if (signerExist) {
|
||||
alert(t("already-exist-signer"));
|
||||
} else {
|
||||
let newForm = [...props.forms];
|
||||
let signerId = newForm[id].value;
|
||||
newForm[id].label = item?.label;
|
||||
// newForm[id].value = item?.value;
|
||||
props.setForms(newForm);
|
||||
const getSigner = userList.find((x) => x.objectId === item.value);
|
||||
props.handleAddUser(getSigner, signerId);
|
||||
}
|
||||
};
|
||||
//show modal to create new contact
|
||||
const handleCreateNew = (e, id) => {
|
||||
e.preventDefault();
|
||||
props.setIsNewContact({ status: true, id: id });
|
||||
};
|
||||
const closePopup = () => {
|
||||
props.setIsNewContact({ status: false, id: "" });
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ModalUi
|
||||
title={uniqueWidget?.length > 0 ? t("prefill-widget") : "Recipients"}
|
||||
isOpen={true}
|
||||
handleClose={props.handleClosePrefillModal}
|
||||
>
|
||||
<div className="relative">
|
||||
{(props?.isSubmit || loading) && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/70 z-[9999]">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{uniqueWidget?.length > 0 && (
|
||||
<div className="py-3 px-[10px] op-card border-[1px] border-gray-400 m-3 md:m-6 text-base-content flex flex-col relative">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-4 w-full">
|
||||
{uniqueWidget.map((x, id) => (
|
||||
<div key={id} className="flex flex-col gap-2 w-full">
|
||||
{handleWidgetType(x.widget)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
{props.forms.length > 0 && (
|
||||
<div className="overflow-y-auto m-3">
|
||||
{uniqueWidget?.length > 0 && (
|
||||
<h1 className="font-medium text-[15px] mb-2">
|
||||
{t("recipients")}
|
||||
</h1>
|
||||
)}
|
||||
<div className="py-3 px-[10px] op-card border-[1px] border-gray-400 md:mx-3 text-base-content flex flex-col relative">
|
||||
{props.forms?.map((field, id) => {
|
||||
return (
|
||||
<div className="flex flex-col" key={field?.value}>
|
||||
<label>{field?.role}</label>
|
||||
<div className="flex justify-between items-center gap-1">
|
||||
<div className="flex-1">
|
||||
<AsyncSelect
|
||||
cacheOptions
|
||||
defaultOptions
|
||||
value={field}
|
||||
loadingMessage={() => t("loading")}
|
||||
noOptionsMessage={() => t("contact-not-found")}
|
||||
loadOptions={loadOptions}
|
||||
onChange={(item) => handleInputChange(item, id)}
|
||||
unstyled
|
||||
onFocus={() => loadOptions()}
|
||||
classNames={{
|
||||
control: () =>
|
||||
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
|
||||
valueContainer: () =>
|
||||
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
|
||||
multiValue: () =>
|
||||
"op-badge op-badge-primary h-full text-[11px]",
|
||||
multiValueLabel: () => "mb-[2px]",
|
||||
menu: () =>
|
||||
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content absolute z-9999",
|
||||
menuList: () => "shadow-md rounded-lg",
|
||||
option: () =>
|
||||
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
|
||||
noOptionsMessage: () =>
|
||||
"p-2 bg-base-200 rounded-lg m-1 p-2"
|
||||
}}
|
||||
menuPortalTarget={document.getElementById(
|
||||
"selectSignerModal"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => handleCreateNew(e, field.value)}
|
||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm"
|
||||
>
|
||||
<i className="fa-light fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 mx-4 mb-3">
|
||||
<button
|
||||
disabled={isAnyLoaderActive || props?.isSubmit}
|
||||
className="op-btn op-btn-primary op-btn-sm w-[80px]"
|
||||
onClick={() => handleEmbedPrefill(props?.item)}
|
||||
>
|
||||
<span>{t("next")}</span>
|
||||
</button>
|
||||
<button
|
||||
className="op-btn op-btn-ghost op-btn-sm"
|
||||
onClick={() => props.navigatePageToDoc()}
|
||||
>
|
||||
<span>{t("edit-draft")}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
title={t("add-contact")}
|
||||
isOpen={props.isNewContact.status}
|
||||
handleClose={closePopup}
|
||||
>
|
||||
<AddContact
|
||||
isDisableTitle
|
||||
isAddYourSelfCheckbox
|
||||
details={props.handleAddUser}
|
||||
closePopup={closePopup}
|
||||
newContactId={props.isNewContact.id}
|
||||
/>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PrefillWidgetModal;
|
||||
@@ -20,7 +20,7 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
onClick={previousPage}
|
||||
>
|
||||
<span className="block">
|
||||
<i className="fa-light fa-backward" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-chevron-up" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
<span className="text-xs text-base-content font-medium mx-2 2xl:text-[20px]">
|
||||
@@ -32,7 +32,7 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
onClick={nextPage}
|
||||
>
|
||||
<span className="block">
|
||||
<i className="fa-light fa-forward" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-chevron-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import {
|
||||
color,
|
||||
@@ -12,6 +12,7 @@ const cursor =
|
||||
const RecipientList = (props) => {
|
||||
const [animationParent] = useAutoAnimate();
|
||||
const [isHover, setIsHover] = useState();
|
||||
const [isPrefill, setIsPrefill] = useState(false);
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
//function for onhover signer name change background color
|
||||
const inputRef = useRef(null);
|
||||
@@ -80,9 +81,43 @@ const RecipientList = (props) => {
|
||||
});
|
||||
props?.setSignerPos(changeOrderSignerList);
|
||||
};
|
||||
const handleSelectRecipient = (e, index, obj, prefill) => {
|
||||
e.preventDefault();
|
||||
props?.setIsSelectId(index);
|
||||
props?.setUniqueId(obj.Id);
|
||||
props?.setRoleName(obj.Role);
|
||||
props?.setBlockColor(obj?.blockColor);
|
||||
props?.handleModal && props?.handleModal();
|
||||
setIsPrefill(prefill ?? false);
|
||||
props.setIsTour && props?.setIsTour(false);
|
||||
};
|
||||
const isSelected = (ind) => {
|
||||
const isUserSelected =
|
||||
(!isMobile && isHover === ind) ||
|
||||
(!isPrefill && props.isSelectListId === ind);
|
||||
return isUserSelected;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{props.signersdata?.length > 0 &&
|
||||
{props?.prefillSigner?.length > 0 &&
|
||||
props?.prefillSigner?.map((obj, ind) => (
|
||||
<div
|
||||
key={ind}
|
||||
data-tut="prefillTour"
|
||||
className={`${
|
||||
props.uniqueId === obj.Id
|
||||
? "op-bg-primary text-white"
|
||||
: "transparent text-base-content"
|
||||
} cursor-pointer px-2 py-1 m-1 mb-2 border-[1px] gap-1 rounded-xl flex justify-center items-center op-border-primary text-[12px] font-bold whitespace-nowrap text-ellipsis`}
|
||||
onClick={(e) => handleSelectRecipient(e, ind, obj, true)}
|
||||
>
|
||||
<i
|
||||
className={`${props.uniqueId === obj.Id ? "bg-white op-text-primary" : "op-bg-primary text-white"} w-[20px] h-[20px] flex justify-center items-center text-[10px] fa-light fa-signature rounded-full`}
|
||||
></i>
|
||||
<span>{obj.Name}</span>
|
||||
</div>
|
||||
))}
|
||||
{props.signersdata.length > 0 &&
|
||||
props.signersdata.map((obj, ind) => {
|
||||
return (
|
||||
<div
|
||||
@@ -118,20 +153,11 @@ const RecipientList = (props) => {
|
||||
: color[ind % color.length]
|
||||
: "transparent"
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
props.setIsSelectId(ind);
|
||||
props.setUniqueId(obj.Id);
|
||||
props.setRoleName(obj.Role);
|
||||
props.setBlockColor(obj?.blockColor);
|
||||
if (props.handleModal) {
|
||||
props.handleModal();
|
||||
}
|
||||
}}
|
||||
onClick={(e) => handleSelectRecipient(e, ind, obj)}
|
||||
>
|
||||
<div className="flex flex-row items-center w-full">
|
||||
<div className="flex flex-row items-center w-full overflow-hidden pr-2">
|
||||
<div
|
||||
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
|
||||
className="flex flex-shrink-0 w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
|
||||
style={{
|
||||
background: obj?.blockColor
|
||||
? darkenColor(obj?.blockColor, 0.4)
|
||||
@@ -142,38 +168,22 @@ const RecipientList = (props) => {
|
||||
{isWidgetExist(obj.Id) ? (
|
||||
<i className="fa-light fa-check"></i>
|
||||
) : (
|
||||
<>
|
||||
{obj.Name
|
||||
? getFirstLetter(obj.Name)
|
||||
: getFirstLetter(obj.Role)}
|
||||
</>
|
||||
<>{getFirstLetter(obj?.Name ?? obj?.Role)}</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
obj.Name ? "flex-col" : "flex-row"
|
||||
} flex items-center`}
|
||||
className={`${obj.Name ? "flex-col" : "flex-row"} ${
|
||||
isSelected(ind) ? "text-[#424242]" : "text-base-content"
|
||||
} flex overflow-hidden flex-grow-0`}
|
||||
>
|
||||
{obj.Name ? (
|
||||
<span
|
||||
className={`${
|
||||
(!isMobile && isHover === ind) ||
|
||||
props.isSelectListId === ind
|
||||
? "text-[#424242]"
|
||||
: "text-base-content"
|
||||
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
|
||||
>
|
||||
<span className="text-[12px] font-bold truncate whitespace-nowrap">
|
||||
{obj.Name}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className={`${
|
||||
(!isMobile && isHover === ind) ||
|
||||
props.isSelectListId === ind
|
||||
? "text-[#424242]"
|
||||
: "text-base-content"
|
||||
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
|
||||
className="text-[12px] font-bold truncate whitespace-nowrap cursor-pointer"
|
||||
onClick={() => {
|
||||
setIsEdit({ [obj.Id]: true });
|
||||
props.setRoleName(obj.Role);
|
||||
@@ -182,7 +192,7 @@ const RecipientList = (props) => {
|
||||
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="bg-transparent p-[3px]"
|
||||
className="bg-transparent p-[3px] w-full"
|
||||
value={obj.Role}
|
||||
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||
onBlur={() => {
|
||||
@@ -204,14 +214,7 @@ const RecipientList = (props) => {
|
||||
</span>
|
||||
)}
|
||||
{obj.Name && (
|
||||
<span
|
||||
className={` ${
|
||||
(!isMobile && isHover === ind) ||
|
||||
props.isSelectListId === ind
|
||||
? "text-[#424242]"
|
||||
: "text-base-content"
|
||||
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
|
||||
>
|
||||
<span className="text-[10px] font-medium truncate whitespace-nowrap">
|
||||
{obj?.Role || obj?.Email}
|
||||
</span>
|
||||
)}
|
||||
@@ -247,18 +250,13 @@ const RecipientList = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.handleDeleteUser && (
|
||||
{props.handleDeleteUser && obj?.Role !== "prefill" && (
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleDeleteUser(obj.Id);
|
||||
}}
|
||||
className={`${
|
||||
(!isMobile && isHover === ind) ||
|
||||
props.isSelectListId === ind
|
||||
? "text-[#424242]"
|
||||
: "text-base-content"
|
||||
} cursor-pointer`}
|
||||
className={`${isSelected(ind) ? "text-[#424242]" : "text-base-content"} cursor-pointer`}
|
||||
>
|
||||
<i className="fa-light fa-trash-can 2xl:text-[22px]"></i>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ function RenderAllPdfPage(props) {
|
||||
const mergePdfInputRef = useRef(null);
|
||||
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const isSidebar = useSelector((state) => state.sidebar.isOpen);
|
||||
const [pageWidth, setPageWidth] = useState("");
|
||||
|
||||
//set all number of pages after load pdf
|
||||
@@ -51,7 +51,7 @@ function RenderAllPdfPage(props) {
|
||||
const timer = setTimeout(updateSize, 100); // match the transition duration
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isHeader, pageContainer, props?.containerWH]);
|
||||
}, [isSidebar, pageContainer, props?.containerWH]);
|
||||
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
|
||||
const addSignatureBookmark = (index) => {
|
||||
const ispageNumber = signPageNumber.includes(index + 1);
|
||||
@@ -78,16 +78,19 @@ function RenderAllPdfPage(props) {
|
||||
const handleFileUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
alert("Please upload a valid PDF file.");
|
||||
alert(t("please-select-pdf"));
|
||||
return;
|
||||
}
|
||||
if (!file.type.includes("pdf")) {
|
||||
alert("Only PDF files are allowed.");
|
||||
alert(t("only-pdf-allowed"));
|
||||
return;
|
||||
}
|
||||
const mb = Math.round(file?.size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
const fileSize =
|
||||
maxFileSize;
|
||||
const pdfsize = file?.size;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
@@ -114,18 +117,18 @@ function RenderAllPdfPage(props) {
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
alert(t("incorrect-password-or-decryption-failed"));
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
alert(t("provide-password"));
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
alert(t("error-uploading-pdf"));
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
@@ -144,6 +147,13 @@ function RenderAllPdfPage(props) {
|
||||
useObjectStreams: false
|
||||
});
|
||||
const pdfBuffer = base64ToArrayBuffer(pdfBase64);
|
||||
const pdfsize = pdfBuffer?.byteLength;
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (pdfsize > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
removeFile(e);
|
||||
return;
|
||||
}
|
||||
props.setPdfArrayBuffer(pdfBuffer);
|
||||
props.setPdfBase64Url(pdfBase64);
|
||||
props.setIsUploadPdf && props.setIsUploadPdf(true);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useRef,
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useEffect
|
||||
} from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import {
|
||||
@@ -12,21 +18,35 @@ import Placeholder from "./Placeholder";
|
||||
import Alert from "../../primitives/Alert";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import usePdfPinchZoom from "../../hook/usePdfPinchZoom";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Guidelines from "./Guidelines";
|
||||
import { useGuidelinesContext } from "../../context/GuidelinesContext";
|
||||
import { toggleSidebar } from "../../redux/reducers/sidebarReducer";
|
||||
import DragGuidelinesLayer from "./DragGridLinesLayer";
|
||||
import { useDrop } from "react-dnd";
|
||||
import WidgetsDragPreview from "./WidgetsDragPreview";
|
||||
|
||||
function RenderPdf(props) {
|
||||
const { t } = useTranslation();
|
||||
const [scaledHeight, setScaledHeight] = useState();
|
||||
const [guideline, setGuideline] = useState({
|
||||
show: false,
|
||||
x1: 0,
|
||||
x2: 0,
|
||||
y1: 0,
|
||||
y2: 0
|
||||
const dispatch = useDispatch();
|
||||
const [, drop] = useDrop({
|
||||
accept: "BOX",
|
||||
drop: (item, monitor) => props?.addPositionOfSignature(item, monitor)
|
||||
});
|
||||
const [scaledHeight, setScaledHeight] = useState();
|
||||
const { guideline, showGuidelines } = useGuidelinesContext();
|
||||
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
const scrollTriggerId = useSelector((state) => state.widget.scrollTriggerId);
|
||||
const isOpen = useSelector((state) => state.sidebar.isOpen);
|
||||
const scrollRef = useRef(null);
|
||||
const pdfContainerRef = useRef(null);
|
||||
useEffect(() => {
|
||||
dispatch(toggleSidebar(false));
|
||||
return () => {
|
||||
dispatch(toggleSidebar(true));
|
||||
};
|
||||
}, []);
|
||||
|
||||
// enable pinch to zoom only on actual pdf wrapper
|
||||
usePdfPinchZoom(
|
||||
@@ -36,20 +56,6 @@ function RenderPdf(props) {
|
||||
props.setZoomPercent
|
||||
);
|
||||
|
||||
const handleGuideline = (isShow, x = 0, y = 0, width = 0, height = 0) => {
|
||||
if (isShow) {
|
||||
setGuideline({
|
||||
show: true,
|
||||
x1: x,
|
||||
x2: x + width,
|
||||
y1: y,
|
||||
y2: y + height
|
||||
});
|
||||
} else {
|
||||
setGuideline({ show: false, x1: 0, x2: 0, y1: 0, y2: 0 });
|
||||
}
|
||||
};
|
||||
|
||||
// handle signature block width and height according to screen
|
||||
const posWidth = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
@@ -111,6 +117,98 @@ function RenderPdf(props) {
|
||||
}
|
||||
};
|
||||
|
||||
// `smoothScrollTo` is used to provide smooth scrolling while focus on widget
|
||||
const smoothScrollTo = (targetY, duration = 500) => {
|
||||
const sb = scrollRef.current;
|
||||
if (!sb) return;
|
||||
const start = sb.scrollTop;
|
||||
const change = targetY - start;
|
||||
const startTime = performance.now();
|
||||
|
||||
const animate = (now) => {
|
||||
const elapsed = now - startTime;
|
||||
const t = Math.min(1, elapsed / duration);
|
||||
// easeInOutQuad
|
||||
const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
sb.scrollTo(sb.scrollLeft, start + change * ease);
|
||||
if (t < 1) requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
// `scrollToTarget` is used to focus on widget and scroll to top
|
||||
const scrollToTarget = useCallback(() => {
|
||||
// Get the scrollbar container ref
|
||||
const sb = scrollRef.current;
|
||||
// If there's no content element or no widget details, bail out
|
||||
if (!sb?.contentElement || !props.currWidgetsDetails) return;
|
||||
|
||||
// The absolute Y position (relative to the document) where we want to scroll
|
||||
const yPosition = props?.currWidgetsDetails?.yPosition || 0;
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
|
||||
const targetTop = yPosition * containerScale * props.scale;
|
||||
// The Y offset of the scrollable content container itself
|
||||
const { offsetTop } = sb.contentElement;
|
||||
|
||||
// Account for the header height + a little extra padding
|
||||
// Different header height if user is a guest signer
|
||||
const headerOffset = isGuestSigner ? 10 : 79;
|
||||
// Compute the scroll position inside the container
|
||||
const positionTop = targetTop - offsetTop - headerOffset;
|
||||
|
||||
const pageNumber = props.pageNumber > 0 ? props.pageNumber - 1 : 0;
|
||||
const ogH = props.pdfOriginalWH[pageNumber]?.height;
|
||||
// If the modal for this widget is open, we may need to expand the PDF container
|
||||
if (props.isShowModal[scrollTriggerId]) {
|
||||
// Original PDF height for this page
|
||||
if (pdfContainerRef?.current) {
|
||||
// Increase container height to include the area up to the target
|
||||
pdfContainerRef.current.style.height = `${ogH + (targetTop - offsetTop)}px`;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, reset any inline height override
|
||||
if (pdfContainerRef?.current) {
|
||||
pdfContainerRef.current.style.height = "";
|
||||
}
|
||||
}
|
||||
// Actually perform the scroll: keep the same horizontal scroll, scroll vertically
|
||||
if (targetTop > ogH * 0.75 && !isGuestSigner && isOpen) {
|
||||
smoothScrollTo(positionTop - 300);
|
||||
} else if (targetTop > ogH * 0.75 && !isGuestSigner && !isOpen) {
|
||||
smoothScrollTo(positionTop - 100);
|
||||
} else {
|
||||
smoothScrollTo(positionTop);
|
||||
}
|
||||
|
||||
// Highlight only the target widget; reset others to default border
|
||||
document.querySelectorAll(".signYourselfBlock").forEach((w) => {
|
||||
w.style.border =
|
||||
w.id === String(scrollTriggerId)
|
||||
? "1.5px solid red" // active widget in red
|
||||
: "1.5px solid #007bff"; // others in blue
|
||||
});
|
||||
}, [
|
||||
scrollTriggerId,
|
||||
props.currWidgetsDetails?.yPosition,
|
||||
props.isShowModal,
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber
|
||||
]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
// Whenever scrollTriggerId changes, fire off the scroll in the next rAF
|
||||
// to ensure the DOM has painted/layout is stable before scrolling
|
||||
if (scrollTriggerId) {
|
||||
scrollToTarget();
|
||||
}
|
||||
}, [scrollTriggerId, scrollToTarget]);
|
||||
|
||||
// function for render placeholder block over pdf document (all signing flow)
|
||||
const checkSignedSigners = (data) => {
|
||||
let checkSign = [];
|
||||
@@ -147,7 +245,6 @@ function RenderPdf(props) {
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
showGuidelines={handleGuideline}
|
||||
isDragging={props.isDragging}
|
||||
pdfDetails={props.pdfDetails}
|
||||
unSignedWidgetId={props.unSignedWidgetId}
|
||||
@@ -160,13 +257,11 @@ function RenderPdf(props) {
|
||||
ispublicTemplate={props.ispublicTemplate}
|
||||
handleUserDetails={props.handleUserDetails}
|
||||
isResize={props.isResize}
|
||||
setIsAgreeTour={props.setIsAgreeTour}
|
||||
isAgree={props.isAgree}
|
||||
handleTabDrag={props.handleTabDrag}
|
||||
handleStop={props.handleStop}
|
||||
setUniqueId={props.setUniqueId}
|
||||
setIsSelectId={props.setIsSelectId}
|
||||
handleDeleteSign={props.handleDeleteSign}
|
||||
handleDeleteWidget={props.handleDeleteWidget}
|
||||
setIsPageCopy={props.setIsPageCopy}
|
||||
handleTextSettingModal={props.handleTextSettingModal}
|
||||
handleCellSettingModal={props.handleCellSettingModal}
|
||||
@@ -174,16 +269,15 @@ function RenderPdf(props) {
|
||||
isFreeResize={props.isSelfSign ? true : false}
|
||||
isOpenSignPad={true}
|
||||
assignedWidgetId={props.assignedWidgetId}
|
||||
isApplyAll={true}
|
||||
setCellCount={props.setCellCount}
|
||||
setFontSize={props.setFontSize}
|
||||
fontSize={props.fontSize}
|
||||
fontColor={props.fontColor}
|
||||
setFontColor={props.setFontColor}
|
||||
setRequestSignTour={props.setRequestSignTour}
|
||||
setIsReqSignTourDisabled={props.setIsReqSignTourDisabled}
|
||||
calculateFontsize={calculateFontsize}
|
||||
currWidgetsDetails={props?.currWidgetsDetails}
|
||||
setTempSignerId={props.setTempSignerId}
|
||||
handleNameModal={props?.handleNameModal}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -220,6 +314,7 @@ function RenderPdf(props) {
|
||||
<Alert type={"success"}>{t("success-email-alert")}</Alert>
|
||||
)}
|
||||
<RSC
|
||||
ref={scrollRef}
|
||||
style={{
|
||||
position: "relative",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
@@ -246,7 +341,7 @@ function RenderPdf(props) {
|
||||
}}
|
||||
ref={(node) => {
|
||||
pdfContainerRef.current = node;
|
||||
props.drop && props.drop(node);
|
||||
drop && drop(node);
|
||||
}}
|
||||
id="container"
|
||||
>
|
||||
@@ -273,8 +368,8 @@ function RenderPdf(props) {
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={props.setIsPageCopy}
|
||||
handleDeleteSign={
|
||||
props.handleDeleteSign
|
||||
handleDeleteWidget={
|
||||
props.handleDeleteWidget
|
||||
}
|
||||
handleTabDrag={props.handleTabDrag}
|
||||
handleStop={props.handleStop}
|
||||
@@ -294,7 +389,6 @@ function RenderPdf(props) {
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
showGuidelines={handleGuideline}
|
||||
isDragging={props.isDragging}
|
||||
setIsValidate={props.setIsValidate}
|
||||
setIsRadio={props.setIsRadio}
|
||||
@@ -303,7 +397,6 @@ function RenderPdf(props) {
|
||||
props.setCurrWidgetsDetails
|
||||
}
|
||||
handleNameModal={props.handleNameModal}
|
||||
setTempSignerId={props.setTempSignerId}
|
||||
uniqueId={props.uniqueId}
|
||||
handleTextSettingModal={
|
||||
props.handleTextSettingModal
|
||||
@@ -330,6 +423,11 @@ function RenderPdf(props) {
|
||||
currWidgetsDetails={
|
||||
props?.currWidgetsDetails
|
||||
}
|
||||
setRoleName={props?.setRoleName}
|
||||
pdfDetails={props.pdfDetails}
|
||||
setIsReqSignTourDisabled={
|
||||
props.setIsReqSignTourDisabled
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
@@ -348,7 +446,9 @@ function RenderPdf(props) {
|
||||
key={id}
|
||||
pos={pos}
|
||||
setIsPageCopy={props.setIsPageCopy}
|
||||
handleDeleteSign={props.handleDeleteSign}
|
||||
handleDeleteWidget={
|
||||
props.handleDeleteWidget
|
||||
}
|
||||
handleTabDrag={props.handleTabDrag}
|
||||
handleStop={props.handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
@@ -362,8 +462,6 @@ function RenderPdf(props) {
|
||||
isSignYourself={true}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
showGuidelines={handleGuideline}
|
||||
pdfDetails={props.pdfDetails[0]}
|
||||
isDragging={props.isDragging}
|
||||
setIsCheckbox={props.setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
@@ -390,6 +488,11 @@ function RenderPdf(props) {
|
||||
currWidgetsDetails={
|
||||
props?.currWidgetsDetails
|
||||
}
|
||||
setIsReqSignTourDisabled={
|
||||
props.setIsReqSignTourDisabled
|
||||
}
|
||||
unSignedWidgetId={props.unSignedWidgetId}
|
||||
handleNameModal={props.handleNameModal}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@@ -428,29 +531,32 @@ function RenderPdf(props) {
|
||||
/>
|
||||
</Document>
|
||||
{guideline.show && (
|
||||
<>
|
||||
{/* top guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] left-0 w-full border-t-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ top: guideline.y1 }}
|
||||
/>
|
||||
{/* bottom guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] left-0 w-full border-t-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ top: guideline.y2 }}
|
||||
/>
|
||||
{/* left guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] top-0 h-full border-l-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ left: guideline.x1 }}
|
||||
/>
|
||||
{/* right guide */}
|
||||
<div
|
||||
className="absolute pointer-events-none z-[1000] top-0 h-full border-l-[1px] border-dashed border-[#3b82f6]"
|
||||
style={{ left: guideline.x2 }}
|
||||
/>
|
||||
</>
|
||||
<Guidelines
|
||||
x1={guideline.x1}
|
||||
x2={guideline.x2}
|
||||
y1={guideline.y1}
|
||||
y2={guideline.y2}
|
||||
/>
|
||||
)}
|
||||
<DragGuidelinesLayer
|
||||
signBtnPosition={props.signBtnPosition}
|
||||
showGuidelines={showGuidelines}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isSignYourself={props.signerPos?.length > 0 ? true : false}
|
||||
/>
|
||||
<WidgetsDragPreview
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isSignYourself={props.signerPos?.length > 0 ? true : false}
|
||||
uniqueId={props?.uniqueId}
|
||||
data={props?.signerPos?.find((x) => x.Id === props?.uniqueId)}
|
||||
isNeedSign={props?.pdfRequest}
|
||||
pdfOriginalWH={props.pdfOriginalWH}
|
||||
pageNumber={props.pageNumber}
|
||||
containerWH={props.containerWH}
|
||||
scale={props?.scale}
|
||||
/>
|
||||
</div>
|
||||
</RSC>
|
||||
</>
|
||||
|
||||
@@ -27,12 +27,12 @@ function SignerListComponent(props) {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rounded-xl mx-1 flex flex-row items-center py-[10px] mt-1"
|
||||
className="rounded-xl mx-1 flex flex-row flex-grow-0 items-center py-[10px] mt-1"
|
||||
style={{ background: checkSignerBackColor(props.obj) }}
|
||||
>
|
||||
<div
|
||||
style={{ background: checkUserNameColor(props.obj) }}
|
||||
className="flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1"
|
||||
className="flex flex-shrink-0 w-[30px] h-[30px] rounded-full justify-center items-center mx-1"
|
||||
>
|
||||
<span className="text-[12px] text-center font-bold text-black uppercase">
|
||||
{getFirstLetter(
|
||||
@@ -40,11 +40,11 @@ function SignerListComponent(props) {
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<div className="flex flex-grow-0 flex-col overflow-hidden pr-2">
|
||||
<span className="text-[12px] font-bold truncate whitespace-nowrap">
|
||||
{props.obj?.Name || props?.obj?.Role}
|
||||
</span>
|
||||
<span className="text-[10px] font-medium text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<span className="text-[10px] font-medium truncate whitespace-nowrap">
|
||||
{props.obj?.Email || props.obj?.email}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import React from "react";
|
||||
import RecipientList from "./RecipientList";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
// import { Tooltip } from "react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function SignerListPlace(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleAddRecipient = () => {
|
||||
props?.setIsAddSigner(true);
|
||||
props.setIsTour && props.setIsTour(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
<span className="relative">
|
||||
{props.title ? props.title : "Recipients"}
|
||||
<span className="absolute text-xs z-[30] mt-1 ml-0.5">
|
||||
<sup onClick={() => props.setIsTour && props.setIsTour(true)}>
|
||||
<i className="ml-1 cursor-pointer fa-light fa-question rounded-full border-[1px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
|
||||
</sup>
|
||||
{/* <span className="absolute text-xs z-[30] mt-1 ml-0.5">
|
||||
{props?.title === "Roles" && (
|
||||
<>
|
||||
<a data-tooltip-id="my-tooltip">
|
||||
@@ -30,7 +37,7 @@ function SignerListPlace(props) {
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</span> */}
|
||||
</span>
|
||||
</div>
|
||||
<div className="overflow-auto hide-scrollbar max-h-[180px]">
|
||||
@@ -53,7 +60,7 @@ function SignerListPlace(props) {
|
||||
data-tut="addRecipient"
|
||||
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||
disabled={props?.isMailSend ? true : false}
|
||||
onClick={() => props.setIsAddSigner(true)}
|
||||
onClick={handleAddRecipient}
|
||||
>
|
||||
<i className="fa-light fa-plus"></i> {t("add-recipients")}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import RecipientList from "./RecipientList";
|
||||
import { useDrag } from "react-dnd";
|
||||
import WidgetList from "./WidgetList";
|
||||
import {
|
||||
color,
|
||||
isMobile,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
@@ -12,105 +10,54 @@ import {
|
||||
textWidget,
|
||||
widgets
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useWidgetDrag } from "../../hook/useWidgetDrag";
|
||||
|
||||
function WidgetComponent(props) {
|
||||
const { t } = useTranslation();
|
||||
const signRef = useRef(null);
|
||||
const userInformation = localStorage.getItem("UserInformation");
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
const [, signature] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 1, text: "signature" }
|
||||
});
|
||||
const [, stamp] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 2, text: "stamp" }
|
||||
});
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 5, text: "dropdown" }
|
||||
});
|
||||
const [, checkbox] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 6, text: "checkbox" }
|
||||
});
|
||||
const [, textInput] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 7, text: textInputWidget }
|
||||
});
|
||||
const [, cells] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 17, text: cellsWidget }
|
||||
});
|
||||
const [, initials] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 8, text: "initials" }
|
||||
});
|
||||
const [, name] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 9, text: "name" }
|
||||
});
|
||||
const [, company] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 10, text: "company" }
|
||||
});
|
||||
const [, jobTitle] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 11, text: "job title" }
|
||||
});
|
||||
const [, date] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 12, text: "date" }
|
||||
});
|
||||
const [, image] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 13, text: "image" }
|
||||
});
|
||||
const [, email] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 14, text: "email" }
|
||||
});
|
||||
const [, radioButton] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 15, text: radioButtonWidget }
|
||||
});
|
||||
const [, text] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 16, text: textWidget }
|
||||
});
|
||||
const [widget, setWidget] = useState([]);
|
||||
// Define all draggable widget configurations
|
||||
const draggableItems = [
|
||||
{ id: 1, text: "signature" },
|
||||
{ id: 2, text: "stamp" },
|
||||
{ id: 3, text: "initials" },
|
||||
{ id: 4, text: textInputWidget },
|
||||
{ id: 6, text: "name" },
|
||||
{ id: 7, text: "job title" },
|
||||
{ id: 8, text: "company" },
|
||||
{ id: 9, text: "email" },
|
||||
{ id: 10, text: "date" },
|
||||
{ id: 11, text: textWidget },
|
||||
{ id: 12, text: cellsWidget },
|
||||
{ id: 13, text: "checkbox" },
|
||||
{ id: 14, text: "dropdown" },
|
||||
{ id: 15, text: radioButtonWidget },
|
||||
{ id: 16, text: "image" }
|
||||
];
|
||||
|
||||
// Create all drag refs in one go
|
||||
const widgetRefs = draggableItems.map((item) =>
|
||||
useWidgetDrag({ type: "BOX", ...item })
|
||||
);
|
||||
|
||||
// Map your widgets with the generated dragRefs
|
||||
const [widgetList, setWidgetList] = useState([]);
|
||||
const handleModal = () => {
|
||||
setIsSignersModal(!isSignersModal);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const widgetRef = [
|
||||
signature,
|
||||
stamp,
|
||||
initials,
|
||||
name,
|
||||
jobTitle,
|
||||
company,
|
||||
date,
|
||||
text,
|
||||
textInput,
|
||||
cells,
|
||||
checkbox,
|
||||
dropdown,
|
||||
radioButton,
|
||||
image,
|
||||
email
|
||||
];
|
||||
const getWidgetArray = widgets;
|
||||
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
||||
return { ...obj, ref: widgetRef[ind] };
|
||||
});
|
||||
|
||||
setWidget(newUpdateSigner);
|
||||
const updated = widgets.map((obj, index) => ({
|
||||
...obj,
|
||||
ref: widgetRefs[index]?.dragRef || null
|
||||
}));
|
||||
setWidgetList(updated);
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const modifiedWidgets = widget.filter(
|
||||
// allow only (signature, stamp, initials, text, name, job title, company, email, cells) widget when isAllowModification true and user have session token
|
||||
const modifiedWidgets = widgetList.filter(
|
||||
(data) =>
|
||||
![
|
||||
"dropdown",
|
||||
@@ -121,7 +68,8 @@ function WidgetComponent(props) {
|
||||
"checkbox"
|
||||
].includes(data.type)
|
||||
);
|
||||
const unlogedInUserWidgets = widget.filter(
|
||||
// allow only (signature, stamp, initials, text, cells) widget when isAllowModification true and user does not have session token
|
||||
const unlogedInUserWidgets = widgetList.filter(
|
||||
(data) =>
|
||||
![
|
||||
"dropdown",
|
||||
@@ -136,25 +84,37 @@ function WidgetComponent(props) {
|
||||
"company"
|
||||
].includes(data.type)
|
||||
);
|
||||
const filterWidgets = widget.filter(
|
||||
const selfSignWidgets = widgetList.filter(
|
||||
(data) =>
|
||||
!["dropdown", radioButtonWidget, textInputWidget].includes(
|
||||
data.type
|
||||
)
|
||||
!["dropdown", radioButtonWidget, textInputWidget].includes(data.type)
|
||||
);
|
||||
const textWidgetData = widget.filter((data) => data.type !== textWidget);
|
||||
const updateWidgets = props.isSignYourself
|
||||
? filterWidgets
|
||||
: props.isTemplateFlow
|
||||
? textWidgetData
|
||||
: props.isAlllowModify
|
||||
? userInformation
|
||||
? modifiedWidgets
|
||||
: unlogedInUserWidgets
|
||||
: widget;
|
||||
|
||||
//if user select prefill role then allow only date,image,text,checkbox,radio,dropdownAdd commentMore actions
|
||||
//dropdown widget should only be show in template flow
|
||||
const prefillAllowWidgets = widgetList.filter((data) =>
|
||||
(props.isPrefillDropdown ? ["dropdown"] : [])
|
||||
.concat([radioButtonWidget, textWidget, "date", "image", "checkbox"])
|
||||
.includes(data.type)
|
||||
);
|
||||
//function to show widget on the base of conditionAdd commentMore actions
|
||||
const handleWidgetType = () => {
|
||||
if (props.isSignYourself) {
|
||||
return selfSignWidgets;
|
||||
} else if (props?.roleName === "prefill") {
|
||||
return prefillAllowWidgets;
|
||||
} else if (props.isAlllowModify) {
|
||||
if (userInformation) {
|
||||
return modifiedWidgets;
|
||||
} else {
|
||||
return unlogedInUserWidgets;
|
||||
}
|
||||
} else if (props?.roleName !== "prefill") {
|
||||
return widgetList.filter((data) => ![textWidget].includes(data.type));
|
||||
}
|
||||
};
|
||||
const handleSelectRecipient = () => {
|
||||
if (
|
||||
if (props?.roleName === "prefill") {
|
||||
return "Prefill by owner";
|
||||
} else if (
|
||||
props.signersdata[props.isSelectListId]?.Email ||
|
||||
props.signersdata[props.isSelectListId]?.Role
|
||||
) {
|
||||
@@ -180,9 +140,10 @@ function WidgetComponent(props) {
|
||||
className="w-full op-select op-select-bordered pointer-events-none"
|
||||
value={handleSelectRecipient()}
|
||||
style={{
|
||||
backgroundColor: props.blockColor
|
||||
? props.blockColor
|
||||
: color[0]
|
||||
backgroundColor:
|
||||
props.roleName === "prefill"
|
||||
? "#edf6fc"
|
||||
: props?.blockColor || "#edf6fc"
|
||||
}}
|
||||
>
|
||||
<option value={handleSelectRecipient()}>
|
||||
@@ -221,7 +182,7 @@ function WidgetComponent(props) {
|
||||
>
|
||||
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
updateWidgets={handleWidgetType}
|
||||
handleDivClick={props.handleDivClick}
|
||||
handleMouseLeave={props.handleMouseLeave}
|
||||
signRef={signRef}
|
||||
@@ -240,12 +201,23 @@ function WidgetComponent(props) {
|
||||
} hidden md:block h-full bg-base-100`}
|
||||
>
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
<span>{t("fields")}</span>
|
||||
<span>
|
||||
{t("widgets")}
|
||||
{props?.isSignYourself && (
|
||||
<sup onClick={() => props.setIsTour && props.setIsTour(true)}>
|
||||
<i className="ml-1 cursor-pointer fa-light fa-question rounded-full border-[1px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
|
||||
</sup>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-[15px] flex flex-col pt-4" data-tut="addWidgets">
|
||||
<div
|
||||
className="p-[12px] grid lg:grid-cols-2 gap-x-2 lg:gap-y-1.5 pt-3"
|
||||
data-tut="addWidgets"
|
||||
role="list"
|
||||
aria-label="Add widgets"
|
||||
>
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
updateWidgets={handleWidgetType}
|
||||
handleDivClick={props.handleDivClick}
|
||||
handleMouseLeave={props.handleMouseLeave}
|
||||
signRef={signRef}
|
||||
@@ -260,7 +232,7 @@ function WidgetComponent(props) {
|
||||
isOpen={isSignersModal}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
{props.signersdata.length > 0 ? (
|
||||
{props.signersdata.length > 0 || props.prefillSigner.length > 0 ? (
|
||||
<div className="max-h-[600px] overflow-auto pb-1">
|
||||
<RecipientList
|
||||
signerPos={props.signerPos}
|
||||
@@ -278,6 +250,7 @@ function WidgetComponent(props) {
|
||||
setBlockColor={props.setBlockColor}
|
||||
uniqueId={props.uniqueId}
|
||||
setSignerPos={props.setSignerPos}
|
||||
prefillSigner={props.prefillSigner}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
import { getWidgetType, isMobile } from "../../constant/Utils";
|
||||
import { isMobile } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import getWidgetType from "./getWidgetType";
|
||||
|
||||
function WidgetList(props) {
|
||||
const { t } = useTranslation();
|
||||
return props.updateWidgets.map((item, ind) => {
|
||||
const getWidgetList = props.updateWidgets();
|
||||
return getWidgetList?.map((item, ind) => {
|
||||
return (
|
||||
<div className="2xl:p-1 mb-[5px]" key={ind}>
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
@@ -21,25 +21,44 @@ const WidgetNameModal = (props) => {
|
||||
hint: "",
|
||||
textvalidate: "",
|
||||
isReadOnly: false,
|
||||
cellCount: 5
|
||||
cellCount: 5,
|
||||
});
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const statusArr = ["Required", "Optional"];
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const type = props?.defaultdata?.type;
|
||||
const isCellWidget = useMemo(() => type === cellsWidget, [type]);
|
||||
const isSignOrInitials = useMemo(
|
||||
() => ["signature", "initials"].includes(type),
|
||||
[type]
|
||||
);
|
||||
const showFontControls = useMemo(
|
||||
() =>
|
||||
[
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
cellsWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(props.defaultdata?.type),
|
||||
[type]
|
||||
);
|
||||
|
||||
const handleHint = () => {
|
||||
const type = props.defaultdata?.type;
|
||||
|
||||
if (type === "signature") {
|
||||
return "Draw signature";
|
||||
return t("draw-signature");
|
||||
} else if (type === "stamp" || type === "image") {
|
||||
return `Upload ${type}`;
|
||||
return type === "stamp" ? t("upload-stamp-image") : t("upload-image");
|
||||
} else if (type === "initials") {
|
||||
return "Draw initial";
|
||||
return t("draw-initials");
|
||||
} else if (type === textInputWidget) {
|
||||
return "Enter text";
|
||||
return t("enter-text");
|
||||
} else {
|
||||
return `Enter ${type}`;
|
||||
return t("enter-widgettype", { widgetType: type });
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@@ -54,13 +73,13 @@ const WidgetNameModal = (props) => {
|
||||
? props.defaultdata?.options?.validation?.pattern
|
||||
: props.defaultdata?.options?.validation?.type || "",
|
||||
isReadOnly: props.defaultdata?.options?.isReadOnly || false,
|
||||
cellCount: props.defaultdata?.options?.cellCount || 5
|
||||
cellCount: props.defaultdata?.options?.cellCount || 5,
|
||||
});
|
||||
} else {
|
||||
setFormdata({
|
||||
...formdata,
|
||||
name: props.defaultdata?.options?.name || "",
|
||||
cellCount: props.defaultdata?.options?.cellCount || 5
|
||||
cellCount: props.defaultdata?.options?.cellCount || 5,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,12 +87,13 @@ const WidgetNameModal = (props) => {
|
||||
const defaultSignatureType = signTypes || [];
|
||||
setSignatureType(defaultSignatureType);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.defaultdata]);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
if (["signature", "initials"].includes(props.defaultdata?.type)) {
|
||||
if (isSignOrInitials) {
|
||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||
const isDefaultSignTypeOnly =
|
||||
enabledSignTypes?.length === 1 &&
|
||||
@@ -81,15 +101,17 @@ const WidgetNameModal = (props) => {
|
||||
if (enabledSignTypes.length === 0) {
|
||||
alert(t("at-least-one-signature-type"));
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
alert(t("expect-default-one-more-signature-type"));
|
||||
alert(t("expect-default-one-signature-type"));
|
||||
} else {
|
||||
const data = { ...formdata, signatureType };
|
||||
props.handleData(data, props.defaultdata?.type);
|
||||
}
|
||||
} else {
|
||||
const isTextInput = [textInputWidget, cellsWidget].includes(
|
||||
props.defaultdata?.type
|
||||
);
|
||||
|
||||
const isTextInput = [
|
||||
textInputWidget,
|
||||
cellsWidget,
|
||||
].includes(props.defaultdata?.type);
|
||||
const { isReadOnly, defaultValue, status } = formdata;
|
||||
// If it’s a text‐input widget, enforce that read-only fields have
|
||||
// either a defaultValue or an "optional" status.
|
||||
@@ -110,7 +132,7 @@ const WidgetNameModal = (props) => {
|
||||
status: "required",
|
||||
hint: "",
|
||||
textvalidate: "",
|
||||
cellCount: 5
|
||||
cellCount: 5,
|
||||
});
|
||||
setSignatureType(signTypes);
|
||||
}
|
||||
@@ -148,10 +170,11 @@ const WidgetNameModal = (props) => {
|
||||
} else {
|
||||
setIsValid(true);
|
||||
}
|
||||
const val =
|
||||
props.defaultdata?.type === cellsWidget
|
||||
? e.target.value.slice(0, formdata.cellCount)
|
||||
: e.target.value;
|
||||
|
||||
const val = isCellWidget
|
||||
? e.target.value.slice(0, formdata.cellCount)
|
||||
: e.target.value;
|
||||
|
||||
setFormdata({ ...formdata, [e.target.name]: val });
|
||||
};
|
||||
|
||||
@@ -160,7 +183,7 @@ const WidgetNameModal = (props) => {
|
||||
case "email":
|
||||
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||
case "number":
|
||||
return "/^\\d+$/";
|
||||
return "^\\d+(?:\\.\\d+)?$"; // "/^\\d+$/";
|
||||
case "text":
|
||||
//allow space in text regex
|
||||
return "/^[a-zA-Z ]+$/";
|
||||
@@ -184,23 +207,19 @@ const WidgetNameModal = (props) => {
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
handleClose={props.handleClose && props.handleClose}
|
||||
title={
|
||||
["signature", "initials"].includes(props.defaultdata?.type)
|
||||
? t("signature-setting")
|
||||
: t("widget-info")
|
||||
}
|
||||
title={isSignOrInitials ? t("signature-setting") : t("widget-info")}
|
||||
>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
[textInputWidget, cellsWidget].includes(props.defaultdata?.type)
|
||||
? "pt-0"
|
||||
: ["signature", "initials"].includes(props.defaultdata?.type)
|
||||
: isSignOrInitials
|
||||
? "pt-2"
|
||||
: ""
|
||||
} p-[20px] text-base-content`}
|
||||
>
|
||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
{!isSignOrInitials && (
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="name">
|
||||
{t("name")}
|
||||
@@ -217,7 +236,7 @@ const WidgetNameModal = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === cellsWidget && (
|
||||
{isCellWidget && (
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="cellCount">{t("cell-count")}</label>
|
||||
<input
|
||||
@@ -231,102 +250,107 @@ const WidgetNameModal = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{[textInputWidget, cellsWidget].includes(props.defaultdata?.type) && (
|
||||
<>
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("default-value")}
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="defaultValue"
|
||||
value={formdata.defaultValue}
|
||||
onChange={(e) => handledefaultChange(e)}
|
||||
autoComplete="off"
|
||||
maxLength={
|
||||
props.defaultdata?.type === cellsWidget
|
||||
? formdata.cellCount
|
||||
: undefined
|
||||
}
|
||||
onBlur={() => {
|
||||
if (isValid === false) {
|
||||
setFormdata({ ...formdata, defaultValue: "" });
|
||||
setIsValid(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isValid === false && (
|
||||
<div
|
||||
className="warning defaultvalueWarning"
|
||||
style={{ fontSize: 12 }}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-exclamation-circle text-[15px]"
|
||||
style={{ color: "#fab005" }}
|
||||
></i>
|
||||
{t("invalid-default-value")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!["signature", "initials", textWidget].includes(
|
||||
props.defaultdata?.type
|
||||
) && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
{[
|
||||
textInputWidget,
|
||||
cellsWidget,
|
||||
].includes(props.defaultdata?.type) &&
|
||||
props?.roleName !== "prefill" && (
|
||||
<>
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("default-value")}
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="defaultValue"
|
||||
value={formdata.defaultValue}
|
||||
onChange={(e) => handledefaultChange(e)}
|
||||
autoComplete="off"
|
||||
maxLength={isCellWidget ? formdata.cellCount : undefined}
|
||||
onBlur={() => {
|
||||
if (isValid === false) {
|
||||
setFormdata({ ...formdata, defaultValue: "" });
|
||||
setIsValid(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isValid === false && (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
className="warning defaultvalueWarning"
|
||||
style={{ fontSize: 12 }}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-exclamation-circle text-[15px] mr-1"
|
||||
style={{ color: "#fab005" }}
|
||||
></i>
|
||||
{t("invalid-default-value")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!props?.isSelfSign &&
|
||||
!isSignOrInitials &&
|
||||
props?.roleName !== "prefill" && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({
|
||||
...formdata,
|
||||
status: data.toLowerCase()
|
||||
})
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{[
|
||||
textInputWidget,
|
||||
cellsWidget,
|
||||
].includes(props.defaultdata?.type) &&
|
||||
props?.roleName !== "prefill" && (
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
id="isReadOnly"
|
||||
name="isReadOnly"
|
||||
type="checkbox"
|
||||
checked={formdata.isReadOnly}
|
||||
className="op-checkbox op-checkbox-xs"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
setFormdata((prev) => ({
|
||||
...formdata,
|
||||
isReadOnly: !prev.isReadOnly
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
<label
|
||||
className="ml-1.5 mb-0 capitalize text-[13px]"
|
||||
htmlFor="isreadonly"
|
||||
>
|
||||
{t("read-only")}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
)}
|
||||
</div>
|
||||
{[textInputWidget, cellsWidget].includes(
|
||||
props.defaultdata?.type
|
||||
) && (
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="isReadOnly"
|
||||
name="isReadOnly"
|
||||
type="checkbox"
|
||||
checked={formdata.isReadOnly}
|
||||
className="op-checkbox op-checkbox-xs"
|
||||
onChange={() =>
|
||||
setFormdata((prev) => ({
|
||||
...formdata,
|
||||
isReadOnly: !prev.isReadOnly
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<label
|
||||
className="ml-1.5 mb-0 capitalize text-[13px]"
|
||||
htmlFor="isreadonly"
|
||||
>
|
||||
{t("read-only")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
)}
|
||||
{isSignOrInitials && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
||||
{t("allowed-signature-types")}
|
||||
@@ -354,7 +378,7 @@ const WidgetNameModal = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type !== textWidget && (
|
||||
{!props?.isSelfSign && props?.roleName !== "prefill" && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="hint" className="text-[13px]">
|
||||
{t("hint")}
|
||||
@@ -368,15 +392,7 @@ const WidgetNameModal = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{[
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
cellsWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(props.defaultdata?.type) && (
|
||||
{showFontControls && (
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
|
||||
<div className="flex items-center gap-2 ">
|
||||
<span className="whitespace-nowrap">{t("font-size")}: </span>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useDragLayer } from "react-dnd";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
getContainerScale,
|
||||
handleBackground,
|
||||
radioButtonWidget
|
||||
} from "../../constant/Utils";
|
||||
|
||||
export default function WidgetsDragPreview(props) {
|
||||
const { widgetType, isDragging, initialOffset, currentOffset } = useDragLayer(
|
||||
(monitor) => ({
|
||||
widgetType: monitor.getItem(),
|
||||
isDragging: monitor.isDragging(),
|
||||
initialOffset: monitor.getInitialSourceClientOffset(),
|
||||
currentOffset: monitor.getSourceClientOffset()
|
||||
})
|
||||
);
|
||||
const options = [{ name: "Option-1" }, { name: "Option-2" }];
|
||||
if (!isDragging) return null;
|
||||
|
||||
if (!initialOffset || !currentOffset) return null;
|
||||
|
||||
const { x, y } = currentOffset;
|
||||
const getWidgetDetails = defaultWidthHeight(widgetType?.text);
|
||||
const widget = {
|
||||
type: widgetType?.text,
|
||||
width: getWidgetDetails?.width,
|
||||
height: getWidgetDetails?.height
|
||||
};
|
||||
const calculateFont = () => {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
const fontSize = 12 * containerScale * props.scale;
|
||||
return fontSize;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none z-[9999]">
|
||||
<div
|
||||
className="absolute"
|
||||
style={{
|
||||
transform: `translate(${x}px, ${y}px)`
|
||||
}}
|
||||
>
|
||||
{widgetType.text === "checkbox" ||
|
||||
widgetType.text === radioButtonWidget ? (
|
||||
<div
|
||||
id="checkbox&radio-preview"
|
||||
style={{
|
||||
background: handleBackground(
|
||||
props?.data,
|
||||
props?.isNeedSign,
|
||||
props?.uniqueId
|
||||
)
|
||||
}}
|
||||
className="border-red-600 rounded-[2px] border-[1px]"
|
||||
>
|
||||
{options.map((x, id) => {
|
||||
return (
|
||||
<div key={id}>
|
||||
<label
|
||||
style={{ fontSize: calculateFont(), color: "black" }}
|
||||
className={`mb-0 flex items-center gap-1`}
|
||||
>
|
||||
<input
|
||||
style={{
|
||||
width: calculateFont(),
|
||||
height: calculateFont()
|
||||
}}
|
||||
className={`${widgetType.text === "checkbox" ? "op-checkbox" : "op-radio rounded-full border-black appearance-none bg-white inline-block align-middle relative"} rounded-[1px]`}
|
||||
type={
|
||||
widgetType.text === "checkbox" ? "checkbox" : "radio"
|
||||
}
|
||||
/>
|
||||
<span>{x?.name}</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: props?.posWidth(widget, props?.isSignYourself),
|
||||
height: props?.posHeight(widget, props?.isSignYourself),
|
||||
background: handleBackground(
|
||||
props?.data,
|
||||
props?.isNeedSign,
|
||||
props?.uniqueId
|
||||
)
|
||||
}}
|
||||
className="border-red-600 rounded-[2px] border-[1px] flex items-center justify-center"
|
||||
>
|
||||
<span className="text-center text-[10px] font-medium">
|
||||
{widgetType?.text}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { isMobile } from "../../constant/Utils";
|
||||
|
||||
// `getWidgetType` is used to load ui of widget in side list
|
||||
const getWidgetType = (item, widgetName) => {
|
||||
return (
|
||||
<div
|
||||
title={widgetName}
|
||||
className="op-btn w-fit md:w-[100%] op-btn-primary op-btn-outline op-btn-sm focus:outline-none ml-[6px] outline outline-[1px] md:ml-0 p-0 overflow-hidden"
|
||||
>
|
||||
<div className="w-full h-full flex md:justify-between items-center">
|
||||
<div className="flex justify-start items-center ml-1 overflow-hidden">
|
||||
{!isMobile && (
|
||||
<i className="fa-light fa-grip-vertical ml-[3px] text-[13px] lg:text-[13px]"></i>
|
||||
)}
|
||||
<span className="md:inline-block text-center text-[11px] lg:text-[13px] mx-[3px] truncate font-medium capitalize">
|
||||
{widgetName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[13px] lg:text-[14px] op-btn op-btn-primary rounded-none w-[20px] h-full flex justify-center items-center">
|
||||
<i className={item.icon}></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default getWidgetType;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { formatDateTime } from "../../../constant/Utils";
|
||||
import { formatDateTime } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const DateFormatSelector = (props) => {
|
||||
@@ -17,7 +17,9 @@ const DateFormatSelector = (props) => {
|
||||
"YYYY-MM-DD",
|
||||
"MM-DD-YYYY",
|
||||
"MM.DD.YYYY",
|
||||
"MMM DD, YYYY"
|
||||
"MMM DD, YYYY",
|
||||
"DD.MM.YYYY",
|
||||
"DD/MM/YYYY"
|
||||
];
|
||||
|
||||
// Handle format change
|
||||
@@ -0,0 +1,99 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import Parse from "parse";
|
||||
import { buildDownloadFilename } from "../../utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
|
||||
/**
|
||||
* Enum-like list of supported filename format IDs and their labels
|
||||
* Keep IDs stable; you can freely change labels for UX.
|
||||
*/
|
||||
const FILENAME_FORMATS = [
|
||||
{ id: "DOCNAME", label: "document Name.pdf" },
|
||||
{ id: "DOCNAME_SIGNED", label: "document Name - Signed.pdf" },
|
||||
{ id: "DOCNAME_EMAIL", label: "document Name - name@domain.com.pdf" },
|
||||
{
|
||||
id: "DOCNAME_EMAIL_DATE",
|
||||
label: "document Name - name@domain.com - date.pdf"
|
||||
}
|
||||
];
|
||||
|
||||
const FilenameFormatSelector = ({ fileNameFormat, setFileNameFormat }) => {
|
||||
const { t } = useTranslation();
|
||||
const sampleDocName = "Agreement";
|
||||
const [value, setValue] = useState(fileNameFormat);
|
||||
const [error, setError] = useState("");
|
||||
const currentUser = Parse?.User?.current();
|
||||
const email = currentUser?.get("email") || "user@example.com";
|
||||
|
||||
// Load preference from contracts_User
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
if (!currentUser) return;
|
||||
const rec = await Parse.Cloud.run("getUserDetails");
|
||||
if (rec) {
|
||||
const fmt = rec.get("DownloadFilenameFormat");
|
||||
if (fmt) setValue(fmt);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Load filename pref failed", e);
|
||||
setError(e?.message || String(e));
|
||||
}
|
||||
})();
|
||||
}, [currentUser]);
|
||||
|
||||
const preview = useMemo(() => {
|
||||
return buildDownloadFilename(value, {
|
||||
docName: sampleDocName,
|
||||
email,
|
||||
isSigned: true // preview with signed true for that option
|
||||
});
|
||||
}, [value, email, sampleDocName]);
|
||||
|
||||
async function savePreference(nextValue) {
|
||||
setFileNameFormat(nextValue);
|
||||
}
|
||||
return (
|
||||
<div className="max-w-[400px] pr-[20px]">
|
||||
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||
{t("document-download-filename-format")}
|
||||
<span className="text-sm">
|
||||
<a data-tooltip-id="filename-tooltip" className="ml-1" href="/">
|
||||
<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="filename-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p>{t("download-filename-format-help")}</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</span>
|
||||
</label>
|
||||
<select
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]"
|
||||
value={value}
|
||||
onChange={async (e) => {
|
||||
const v = e.target.value;
|
||||
setValue(v);
|
||||
await savePreference(v);
|
||||
}}
|
||||
>
|
||||
{FILENAME_FORMATS.map((opt) => (
|
||||
<option key={opt.id} value={opt.id}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<div className="mt-2 text-xs opacity-80">
|
||||
{t("preview")}
|
||||
<span className="font-medium">{preview}</span>
|
||||
</div>
|
||||
{error && <div className="mt-2 text-xs text-red-600">{error}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilenameFormatSelector;
|
||||
@@ -0,0 +1,368 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
import ReactQuill from "react-quill-new";
|
||||
import "../../styles/quill.css";
|
||||
import EditorToolbar, { module1, module2, formats } from "../pdf/EditorToolbar";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import Alert from "../../primitives/Alert";
|
||||
import Loader from "../../primitives/Loader";
|
||||
|
||||
const MailTemplateEditor = ({
|
||||
info,
|
||||
tenantId,
|
||||
}) => {
|
||||
const appName = localStorage.getItem("appname") || "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const editorRef = useRef();
|
||||
const editorRefCom = useRef();
|
||||
const [requestBody, setRequestBody] = useState("");
|
||||
const [requestSubject, setRequestSubject] = useState("");
|
||||
const [completionBody, setCompletionBody] = useState("");
|
||||
const [completionSubject, setCompletionSubject] = useState("");
|
||||
const [isDefaultMail, setIsDefaultMail] = useState({
|
||||
requestMail: false,
|
||||
completionMail: false
|
||||
});
|
||||
const [isMailLoader, setIsMailLoader] = useState({
|
||||
request: false,
|
||||
completion: false
|
||||
});
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const defaultRequestSubject = `{{sender_name}} has requested you to sign {{document_title}}`;
|
||||
const defaultRequestBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
const defaultCompletionSubject = `Document {{document_title}} has been signed by all parties`;
|
||||
const defaultCompletionBody = `<p>Hi {{sender_name}},</p><br><p>All parties have successfully signed the document {{document_title}}. Kindly download the document from the attachment.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
const cloudfunction =
|
||||
"updatetenant";
|
||||
|
||||
useEffect(() => {
|
||||
fetchSubscription();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
info
|
||||
]);
|
||||
|
||||
const handleModifyMail = (mode) => {
|
||||
mode === "request"
|
||||
? setIsDefaultMail((p) => ({ ...p, requestMail: !p?.requestMail }))
|
||||
: setIsDefaultMail((p) => ({ ...p, completionMail: !p?.completionMail }));
|
||||
};
|
||||
const fetchSubscription = async () => {
|
||||
await tenantEmailTemplate(info);
|
||||
};
|
||||
|
||||
const tenantEmailTemplate = async (tenantRes) => {
|
||||
if (tenantRes === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantRes) {
|
||||
const updateRes = tenantRes;
|
||||
const defaultRequestBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
if (updateRes?.RequestBody) {
|
||||
setRequestBody(updateRes?.RequestBody);
|
||||
setRequestSubject(updateRes?.RequestSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, requestMail: false }));
|
||||
} else {
|
||||
setRequestBody(defaultRequestBody);
|
||||
setRequestSubject(defaultRequestSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, requestMail: true }));
|
||||
}
|
||||
if (updateRes?.CompletionBody) {
|
||||
setCompletionBody(updateRes?.CompletionBody);
|
||||
setCompletionSubject(updateRes?.CompletionSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, completionMail: false }));
|
||||
} else {
|
||||
setCompletionBody(defaultCompletionBody);
|
||||
setCompletionSubject(defaultCompletionSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, completionMail: true }));
|
||||
}
|
||||
}
|
||||
};
|
||||
//function to save completion email template
|
||||
const handleSaveCompletionEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = completionBody.replace(/"/g, "'");
|
||||
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
|
||||
const updateTenant = await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: {
|
||||
CompletionBody: htmlBody,
|
||||
CompletionSubject: completionSubject
|
||||
}
|
||||
});
|
||||
if (updateTenant) {
|
||||
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
||||
setCompletionBody(updateRes?.CompletionBody);
|
||||
setCompletionSubject(updateRes?.CompletionSubject);
|
||||
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error while saving completion email template: ", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
//function to save request email template
|
||||
const handleSaveRequestEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = requestBody.replace(/"/g, "'");
|
||||
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
|
||||
const updateTenant = await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { RequestBody: htmlBody, RequestSubject: requestSubject }
|
||||
});
|
||||
if (updateTenant) {
|
||||
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
||||
setRequestBody(updateRes?.RequestBody);
|
||||
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.RequestSubject = updateRes?.RequestSubject;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error while saving request email template: ", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
|
||||
//function to use reset form
|
||||
const handleReset = async (request, completion) => {
|
||||
let extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
handleModifyMail(request);
|
||||
if (request && !isDefaultMail?.requestMail) {
|
||||
setRequestBody(defaultRequestBody);
|
||||
setRequestSubject(defaultRequestSubject);
|
||||
setIsMailLoader((p) => ({ ...p, request: true }));
|
||||
try {
|
||||
await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { RequestBody: "", RequestSubject: "" }
|
||||
});
|
||||
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.TenantId.RequestBody = "";
|
||||
extUser.TenantId.RequestSubject = "";
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error while resetting request mail: ", err);
|
||||
} finally {
|
||||
setIsMailLoader((p) => ({ ...p, request: false }));
|
||||
}
|
||||
} else if (completion && !isDefaultMail?.completionMail) {
|
||||
setCompletionSubject(defaultCompletionSubject);
|
||||
setCompletionBody(defaultCompletionBody);
|
||||
setIsMailLoader((p) => ({ ...p, completion: true }));
|
||||
try {
|
||||
await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { CompletionBody: "", CompletionSubject: "" }
|
||||
});
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.TenantId.CompletionBody = "";
|
||||
extUser.TenantId.CompletionSubject = "";
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error while resetting completion mail: ", err);
|
||||
} finally {
|
||||
setIsMailLoader((p) => ({ ...p, completion: false }));
|
||||
}
|
||||
}
|
||||
};
|
||||
//function for handle ontext change and save again text in delta
|
||||
const handleOnchangeRequest = () => {
|
||||
if (editorRef.current) {
|
||||
const html = editorRef.current.editor.root.innerHTML;
|
||||
setRequestBody(html);
|
||||
}
|
||||
};
|
||||
const handleOnchangeCompletion = () => {
|
||||
if (editorRefCom.current) {
|
||||
const html = editorRefCom.current.editor.root.innerHTML;
|
||||
setCompletionBody(html);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
|
||||
<div className="flex flex-col mb-4">
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-[14px] mb-[0.7rem] font-medium">
|
||||
{t("request-email")}
|
||||
</h1>
|
||||
<div className="relative mt-2 mb-4">
|
||||
{isMailLoader.request && (
|
||||
<div className="flex z-[100] justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
isDefaultMail?.requestMail && (
|
||||
<div className="absolute backdrop-blur-[2px] flex w-full h-full justify-center items-center bg-black/10 rounded-box select-none z-20">
|
||||
<button
|
||||
onClick={() => handleModifyMail("request")}
|
||||
className="op-btn op-btn-primary shadow-lg"
|
||||
>
|
||||
{t("modify")}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<form
|
||||
onSubmit={handleSaveRequestEmail}
|
||||
className="p-3 border-[1px] border-base-content rounded-box"
|
||||
>
|
||||
<div className="text-lg font-normal">
|
||||
<label className="text-sm">
|
||||
{t("subject")}{" "}
|
||||
<Tooltip
|
||||
id={"request-sub-tooltip"}
|
||||
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
value={requestSubject}
|
||||
onChange={(e) => setRequestSubject(e.target.value)}
|
||||
placeholder={`{{sender_name}} ${t("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"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-lg font-normal py-2">
|
||||
<label className="text-sm mt-3">
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"request-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={requestBody}
|
||||
placeholder="add body of email"
|
||||
ref={editorRef}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={handleOnchangeRequest}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
disabled={!requestBody || !requestSubject}
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => handleReset("request")}
|
||||
>
|
||||
{t("reset")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<h1 className="text-[14px] mb-[0.7rem] font-medium">
|
||||
{t("completion-email")}
|
||||
</h1>
|
||||
<div className="relative my-2">
|
||||
{isMailLoader.completion && (
|
||||
<div className="flex z-[100] justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{
|
||||
isDefaultMail?.completionMail && (
|
||||
<div className="absolute backdrop-blur-[2px] flex w-full h-full justify-center items-center bg-black/10 rounded-box select-none z-20">
|
||||
<button
|
||||
onClick={() => handleModifyMail("completion")}
|
||||
className="op-btn op-btn-primary shadow-lg"
|
||||
>
|
||||
{t("modify")}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<form
|
||||
onSubmit={handleSaveCompletionEmail}
|
||||
className="p-3 border-[1px] border-base-content rounded-box"
|
||||
>
|
||||
<div className="text-lg font-normal">
|
||||
<label className="text-sm">
|
||||
{t("subject")}{" "}
|
||||
<Tooltip
|
||||
id={"complete-sub-tooltip"}
|
||||
message={`${t("variables-use")}:{{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
value={completionSubject}
|
||||
onChange={(e) => setCompletionSubject(e.target.value)}
|
||||
placeholder={`{{sender_name}} ${t("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"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-lg font-normal py-2">
|
||||
<label className="text-sm mt-3">
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"complete-body-tooltip"}
|
||||
message={`${t("variables-use")}:{{sender_name}} {{document_title}} {{signing_url}}`}
|
||||
/>
|
||||
</label>
|
||||
<EditorToolbar containerId="toolbar2" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={completionBody}
|
||||
placeholder="add body of email"
|
||||
ref={editorRefCom}
|
||||
modules={module2}
|
||||
formats={formats}
|
||||
onChange={handleOnchangeCompletion}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
disabled={!completionBody || !completionSubject}
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => handleReset(null, "completion")}
|
||||
>
|
||||
{t("reset")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default MailTemplateEditor;
|
||||
@@ -197,7 +197,7 @@ const FolderModal = (props) => {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current"
|
||||
>
|
||||
<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>
|
||||
|
||||
@@ -183,7 +183,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[40px] h-[40px] fill-current op-text-secondary"
|
||||
className="w-[40px] h-[40px] fill-current"
|
||||
>
|
||||
<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>
|
||||
@@ -262,7 +262,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current"
|
||||
>
|
||||
<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>
|
||||
|
||||
@@ -63,6 +63,10 @@ const SelectSigners = (props) => {
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
} else if (selected?.value) {
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
} else {
|
||||
setIsError(true);
|
||||
setTimeout(() => setIsError(false), 1000);
|
||||
|
||||
@@ -217,6 +217,7 @@ const SignersInput = (props) => {
|
||||
{isModal && (
|
||||
<AddContact
|
||||
isDisableTitle
|
||||
isAddYourSelfCheckbox={props?.isAddYourSelfCheckbox}
|
||||
details={handleNewDetails}
|
||||
closePopup={handleModalCloseClick}
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Menu from "./Menu";
|
||||
import Submenu from "./SubMenu";
|
||||
import SocialMedia from "../SocialMedia";
|
||||
import dp from "../../assets/images/dp.png";
|
||||
import sidebarList, { subSetting } from "../../json/menuJson";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useWindowSize } from "../../hook/useWindowSize";
|
||||
import { toggleSidebar } from "../../redux/reducers/sidebarReducer";
|
||||
|
||||
const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
const Sidebar = () => {
|
||||
const { width } = useWindowSize();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const isOpen = useSelector((state) => state.sidebar.isOpen);
|
||||
const [menuList, setmenuList] = useState([]);
|
||||
const [submenuOpen, setSubmenuOpen] = useState(false);
|
||||
const username = localStorage.getItem("username");
|
||||
@@ -22,44 +28,29 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const closeSidebar = () => {
|
||||
if (width <= 1023) {
|
||||
dispatch(toggleSidebar(false));
|
||||
}
|
||||
};
|
||||
|
||||
const menuItem = async () => {
|
||||
try {
|
||||
if (localStorage.getItem("defaultmenuid")) {
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
// console.log("extClass ", extClass);
|
||||
let userRole = "contracts_User";
|
||||
if (extClass && extClass.length > 0) {
|
||||
userRole = extClass[0].UserRole;
|
||||
}
|
||||
if (
|
||||
userRole === "contracts_Admin" ||
|
||||
userRole === "contracts_OrgAdmin"
|
||||
) {
|
||||
const newSidebarList = sidebarList.map((item) => {
|
||||
if (item.title === "Settings") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
const arr = newItem.children.slice(0, 1);
|
||||
newItem.children = [...arr, ...subSetting];
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setmenuList(newSidebarList);
|
||||
} else {
|
||||
const newSidebarList = sidebarList.map((item) => {
|
||||
if (item.title === "Settings") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
const arr = newItem.children.slice(0, 1);
|
||||
newItem.children = arr;
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setmenuList(newSidebarList);
|
||||
}
|
||||
const userRole = extClass?.[0]?.UserRole || "contracts_User";
|
||||
const isAdmin =
|
||||
userRole === "contracts_Admin" || userRole === "contracts_OrgAdmin";
|
||||
const newSidebarList = sidebarList.map((item) => {
|
||||
if (item.title !== "Settings") return item;
|
||||
const newItem = { ...item };
|
||||
const baseChildren = isAdmin ? subSetting : subSetting?.slice(0, 1);
|
||||
const mysignature = newItem.children.slice(0, 1);
|
||||
newItem.children = [...mysignature, ...baseChildren];
|
||||
return newItem;
|
||||
});
|
||||
setmenuList(newSidebarList);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
@@ -80,8 +71,8 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
};
|
||||
return (
|
||||
<aside
|
||||
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[500] shadow-lg hide-scrollbar
|
||||
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
|
||||
className={`absolute max-lg:min-h-screen lg:relative bg-base-100 overflow-y-auto transition-all z-[500] shadow-lg hide-scrollbar
|
||||
${isOpen ? "w-full md:w-64" : "w-0"}`}
|
||||
>
|
||||
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
||||
<div
|
||||
@@ -140,7 +131,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
<footer className="mt-4 flex justify-center items-center text-[25px] text-base-content gap-3">
|
||||
<footer className="my-3 flex justify-center items-center text-[25px] text-base-content gap-3">
|
||||
<SocialMedia />
|
||||
</footer>
|
||||
</aside>
|
||||
|
||||
@@ -9,7 +9,7 @@ export const getThemeIconColor = () => {
|
||||
return theme === "opensigndark" ? "#CCCCCC" : "#686968";
|
||||
};
|
||||
export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
export const maxFileSize = 10; // 10MB
|
||||
export const maxTitleLength = 250; // 250 characters
|
||||
export const maxNoteLength = 200; // 200 characters
|
||||
export const maxDescriptionLength = 500; // 500 characters
|
||||
export const maxFileSize = 80; // for cloud 10MB / 80MB for self-hosted
|
||||
|
||||
@@ -4,13 +4,22 @@ const parseAppId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const serverUrl = serverUrl_fn();
|
||||
export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||
const commonheader = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
};
|
||||
export const SaveFileSize = async (size, imageUrl, tenantId, userId) => {
|
||||
//checking server url and save file's size
|
||||
const tenantPtr = {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: tenantId
|
||||
};
|
||||
const UserPtr = userId && {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userId
|
||||
};
|
||||
const _tenantPtr = JSON.stringify(tenantPtr);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
@@ -24,7 +33,6 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||
);
|
||||
const response = res.data.results;
|
||||
let data;
|
||||
// console.log("response", response);
|
||||
if (response && response.length > 0) {
|
||||
data = {
|
||||
usedStorage: response[0].usedStorage
|
||||
@@ -34,43 +42,31 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||
await axios.put(
|
||||
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
}
|
||||
{ headers: commonheader }
|
||||
);
|
||||
} else {
|
||||
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
||||
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
headers: commonheader
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in save usage", err);
|
||||
}
|
||||
saveDataFile(size, imageUrl, tenantPtr);
|
||||
saveDataFile(size, imageUrl, tenantPtr, UserPtr);
|
||||
};
|
||||
|
||||
//function for save fileUrl and file size in particular client db class partners_DataFiles
|
||||
const saveDataFile = async (size, imageUrl, tenantPtr) => {
|
||||
const saveDataFile = async (size, imageUrl, tenantPtr, UserId) => {
|
||||
const data = {
|
||||
FileUrl: imageUrl,
|
||||
FileSize: size,
|
||||
TenantPtr: tenantPtr
|
||||
TenantPtr: tenantPtr,
|
||||
...(UserId ? { UserId: UserId } : {})
|
||||
};
|
||||
|
||||
// console.log("data save",file, data)
|
||||
try {
|
||||
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
headers: commonheader
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in save usage ", err);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createContext, useContext, useState, useCallback } from "react";
|
||||
|
||||
const GuidelinesContext = createContext();
|
||||
|
||||
export const GuidelinesProvider = ({ children }) => {
|
||||
const [guideline, setGuideline] = useState({
|
||||
show: false,
|
||||
x1: 0,
|
||||
x2: 0,
|
||||
y1: 0,
|
||||
y2: 0
|
||||
});
|
||||
|
||||
const showGuidelines = useCallback((show, x, y, width, height) => {
|
||||
if (show) {
|
||||
setGuideline({
|
||||
show: true,
|
||||
x1: x,
|
||||
x2: x + width - 1.5,
|
||||
y1: y,
|
||||
y2: y + height - 1
|
||||
});
|
||||
} else {
|
||||
setGuideline((prev) => ({ ...prev, show: false }));
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<GuidelinesContext.Provider value={{ guideline, showGuidelines }}>
|
||||
{children}
|
||||
</GuidelinesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useGuidelinesContext = () => {
|
||||
const context = useContext(GuidelinesContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useGuidelinesContext must be used within a GuidelinesProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
export function useManifestUrl(appName, logo) {
|
||||
const url = useMemo(() => {
|
||||
const start_url = window.location.origin || ".";
|
||||
const manifest = {
|
||||
short_name: appName,
|
||||
name: appName,
|
||||
start_url: start_url,
|
||||
display: "standalone",
|
||||
theme_color: "#000000",
|
||||
background_color: "#ffffff",
|
||||
...(logo && {
|
||||
icons: [
|
||||
{ src: logo, type: "image/png", sizes: "64x64 32x32 24x24 16x16" },
|
||||
{ src: logo, type: "image/png", sizes: "192x192" },
|
||||
{ src: logo, type: "image/png", sizes: "512x512" }
|
||||
]
|
||||
})
|
||||
};
|
||||
const blob = new Blob([JSON.stringify(manifest)], {
|
||||
type: "application/json"
|
||||
});
|
||||
return URL.createObjectURL(blob);
|
||||
}, [appName, logo]);
|
||||
|
||||
useEffect(() => {
|
||||
// cleanup when unmounting or when url changes
|
||||
return () => {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
return url;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useEffect } from "react";
|
||||
import { useDrag } from "react-dnd";
|
||||
import { getEmptyImage } from "react-dnd-html5-backend";
|
||||
import { useGuidelinesContext } from "../context/GuidelinesContext";
|
||||
|
||||
export const useWidgetDrag = (item) => {
|
||||
const { showGuidelines } = useGuidelinesContext();
|
||||
const [, dragRef, preview] = useDrag({
|
||||
type: "BOX",
|
||||
item,
|
||||
collect: (monitor) => ({
|
||||
isDragging: monitor.isDragging()
|
||||
}),
|
||||
end: () => {
|
||||
showGuidelines(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Hide browser default drag preview
|
||||
useEffect(() => {
|
||||
preview(getEmptyImage(), { captureDraggingState: true });
|
||||
}, [preview]);
|
||||
|
||||
return { dragRef };
|
||||
};
|
||||
@@ -24,7 +24,7 @@ body {
|
||||
@media screen and (max-width: 766px) {
|
||||
.reactour__close {
|
||||
width: 17px !important;
|
||||
padding-left: 3px !important;
|
||||
padding-left: 3px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,49 +100,62 @@ body {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.op-border-hover {
|
||||
@apply border-gray-300 hover:border-base-content;
|
||||
}
|
||||
|
||||
/* Note: Dark mode styling is now handled via Tailwind utilities in tailwind.config.js */
|
||||
/* You can use classes like: icon-improved, icon-muted, icon-disabled, op-btn-vscode-disabled */
|
||||
|
||||
/* React-tour and ReactTooltip dark mode styling */
|
||||
[data-theme="opensigndark"] {
|
||||
.op-border-hover {
|
||||
@apply border-gray-700 hover:border-base-content;
|
||||
}
|
||||
|
||||
/* for change calender icon of input date type */
|
||||
::-webkit-calendar-picker-indicator {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
/* React-tour modal styling */
|
||||
.reactour__helper {
|
||||
background-color: #1F2937 !important;
|
||||
color: #E5E7EB !important;
|
||||
background-color: #1f2937 !important;
|
||||
color: #e5e7eb !important;
|
||||
border: 1px solid #374151 !important;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.reactour__close {
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
background-color: #374151 !important;
|
||||
border: 1px solid #4B5563 !important;
|
||||
border: 1px solid #4b5563 !important;
|
||||
}
|
||||
|
||||
.reactour__close:hover {
|
||||
background-color: #4B5563 !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #4b5563 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* React-tour navigation buttons */
|
||||
.reactour__controls {
|
||||
background-color: #1F2937 !important;
|
||||
background-color: #1f2937 !important;
|
||||
border-top: 1px solid #374151 !important;
|
||||
}
|
||||
|
||||
.reactour__controls button {
|
||||
background-color: #374151 !important;
|
||||
color: #E5E7EB !important;
|
||||
border: 1px solid #4B5563 !important;
|
||||
color: #e5e7eb !important;
|
||||
border: 1px solid #4b5563 !important;
|
||||
}
|
||||
|
||||
.reactour__controls button:hover {
|
||||
background-color: #4B5563 !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #4b5563 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.reactour__controls button:disabled {
|
||||
background-color: #3C3C3C !important;
|
||||
background-color: #3c3c3c !important;
|
||||
color: #858585 !important;
|
||||
border-color: #565656 !important;
|
||||
cursor: not-allowed !important;
|
||||
@@ -150,28 +163,28 @@ body {
|
||||
|
||||
/* ReactTooltip styling */
|
||||
.react-tooltip {
|
||||
background-color: #1F2937 !important;
|
||||
color: #E5E7EB !important;
|
||||
background-color: #1f2937 !important;
|
||||
color: #e5e7eb !important;
|
||||
border: 1px solid #374151 !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.react-tooltip.type-dark {
|
||||
background-color: #1F2937 !important;
|
||||
color: #E5E7EB !important;
|
||||
background-color: #1f2937 !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-tooltip.place-top:after,
|
||||
.react-tooltip.place-bottom:after,
|
||||
.react-tooltip.place-left:after,
|
||||
.react-tooltip.place-right:after {
|
||||
border-color: #1F2937 !important;
|
||||
border-color: #1f2937 !important;
|
||||
}
|
||||
|
||||
/* Tour content improvements */
|
||||
.reactour__badge {
|
||||
background-color: #007ACC !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #007acc !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.reactour__helper h1,
|
||||
@@ -180,82 +193,82 @@ body {
|
||||
.reactour__helper h4,
|
||||
.reactour__helper h5,
|
||||
.reactour__helper h6 {
|
||||
color: #FFFFFF !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.reactour__helper p {
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
/* VS Code-style tour buttons */
|
||||
.reactour__controls .op-btn {
|
||||
background-color: #007ACC !important;
|
||||
color: #FFFFFF !important;
|
||||
border: 1px solid #007ACC !important;
|
||||
background-color: #007acc !important;
|
||||
color: #ffffff !important;
|
||||
border: 1px solid #007acc !important;
|
||||
}
|
||||
|
||||
.reactour__controls .op-btn:hover {
|
||||
background-color: #0086D1 !important;
|
||||
border-color: #0086D1 !important;
|
||||
background-color: #0086d1 !important;
|
||||
border-color: #0086d1 !important;
|
||||
}
|
||||
|
||||
.reactour__controls .op-btn-secondary {
|
||||
background-color: #374151 !important;
|
||||
color: #E5E7EB !important;
|
||||
border: 1px solid #4B5563 !important;
|
||||
color: #e5e7eb !important;
|
||||
border: 1px solid #4b5563 !important;
|
||||
}
|
||||
|
||||
.reactour__controls .op-btn-secondary:hover {
|
||||
background-color: #4B5563 !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #4b5563 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* React-datepicker dark mode styling */
|
||||
.react-datepicker {
|
||||
background-color: #1F2937 !important;
|
||||
background-color: #1f2937 !important;
|
||||
border: 1px solid #374151 !important;
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__header {
|
||||
background-color: #374151 !important;
|
||||
border-bottom: 1px solid #4B5563 !important;
|
||||
color: #E5E7EB !important;
|
||||
border-bottom: 1px solid #4b5563 !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__current-month,
|
||||
.react-datepicker__day-name {
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day {
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day:hover {
|
||||
background-color: #4B5563 !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #4b5563 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day--selected {
|
||||
background-color: #007ACC !important;
|
||||
color: #FFFFFF !important;
|
||||
background-color: #007acc !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day--keyboard-selected {
|
||||
background-color: #374151 !important;
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day--outside-month {
|
||||
color: #6B7280 !important;
|
||||
color: #6b7280 !important;
|
||||
}
|
||||
|
||||
.react-datepicker__navigation {
|
||||
color: #E5E7EB !important;
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
.react-datepicker__navigation:hover {
|
||||
background-color: #4B5563 !important;
|
||||
background-color: #4b5563 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import "./styles/dark-theme-improvements.css";
|
||||
import App from "./App";
|
||||
import { showUpgradeProgress, hideUpgradeProgress } from "./utils";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { TouchBackend } from "react-dnd-touch-backend";
|
||||
import {
|
||||
DndProvider,
|
||||
TouchTransition,
|
||||
MouseTransition,
|
||||
Preview
|
||||
} from "react-dnd-multi-backend";
|
||||
import DragElement from "./components/pdf/DragElement.jsx";
|
||||
import Parse from "parse";
|
||||
import "./polyfills";
|
||||
import { serverUrl_fn } from "./constant/appinfo";
|
||||
@@ -25,47 +16,22 @@ const serverUrl = serverUrl_fn();
|
||||
Parse.initialize(appId);
|
||||
Parse.serverURL = serverUrl;
|
||||
|
||||
if (localStorage.getItem("showUpgradeProgress")) {
|
||||
showUpgradeProgress();
|
||||
}
|
||||
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
if (savedTheme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "opensigndark");
|
||||
}
|
||||
|
||||
const HTML5toTouch = {
|
||||
backends: [
|
||||
{
|
||||
id: "html5",
|
||||
backend: HTML5Backend,
|
||||
transition: MouseTransition
|
||||
},
|
||||
{
|
||||
id: "touch",
|
||||
backend: TouchBackend,
|
||||
options: { enableMouseEvents: true },
|
||||
preview: true,
|
||||
transition: TouchTransition
|
||||
}
|
||||
]
|
||||
};
|
||||
const generatePreview = (props) => {
|
||||
const { item, style } = props;
|
||||
const newStyle = {
|
||||
...style
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={newStyle}>
|
||||
<DragElement {...item} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
hideUpgradeProgress();
|
||||
localStorage.removeItem("showUpgradeProgress");
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
export const extraCols = [
|
||||
"Note",
|
||||
"Time to complete (Days)",
|
||||
"Enable Tour",
|
||||
"Notify on signatures",
|
||||
"Redirect url",
|
||||
"Created Date",
|
||||
"Updated Date"
|
||||
];
|
||||
|
||||
export default function reportJson(id) {
|
||||
// console.log("json ", json);
|
||||
const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const declineHead = ["Title", "Reason", "Folder", "File", "Owner", "Signers"];
|
||||
const iphead = ["Title", "Note", "Folder", "File", "Signers"];
|
||||
const contactbook = ["Name", "Email", "Phone"];
|
||||
const iphead = ["Title", "Note", "Folder", "File", "Signers", "Sent Date"];
|
||||
const contactbook = ["Name", "Email", "Phone", "Company", "JobTitle"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Title", "File", "Owner", "Signers"];
|
||||
switch (id) {
|
||||
@@ -28,25 +37,6 @@ export default function reportJson(id) {
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
},
|
||||
{
|
||||
btnId: "22534",
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
restrictBtn: true,
|
||||
textColor: "black",
|
||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "1630",
|
||||
btnLabel: "Save as template",
|
||||
hoverLabel: "Save as template",
|
||||
btnIcon: "fa-light fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "saveastemplate"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
@@ -81,7 +71,7 @@ export default function reportJson(id) {
|
||||
btnId: "8901",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-light fa-share",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
@@ -317,7 +307,7 @@ export default function reportJson(id) {
|
||||
btnId: "1999",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-light fa-share",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
@@ -419,25 +409,6 @@ export default function reportJson(id) {
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
},
|
||||
{
|
||||
btnId: "55534",
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
restrictBtn: true,
|
||||
textColor: "black",
|
||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "6630",
|
||||
btnLabel: "Save as template",
|
||||
hoverLabel: "Save as template",
|
||||
btnIcon: "fa-light fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "saveastemplate"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -462,8 +433,6 @@ export default function reportJson(id) {
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
import: true,
|
||||
form: "ContactBook",
|
||||
helpMsg:
|
||||
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
|
||||
};
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
export const templateReportTour = [
|
||||
{
|
||||
selector: "[data-tut=reactourFirst]",
|
||||
content: <Trans i18nKey="tour-mssg.report-1" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=reactourSecond]",
|
||||
content: <Trans i18nKey="tour-mssg.redirect" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=tourbulksend]",
|
||||
content: <Trans i18nKey="tour-mssg.bulksend" />,
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: "[data-tut=reactourThird]",
|
||||
content: (
|
||||
<Trans i18nKey="tour-mssg.option">
|
||||
This menu reveals more options such as Edit, Delete, Rename, Duplicate,
|
||||
Share, etc.
|
||||
<a
|
||||
className="cursor-pointer op-text-primary"
|
||||
href="https://docs.opensignlabs.com/docs/help/Templates/manage-templates"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Click here
|
||||
</a>
|
||||
to read more about all available options.
|
||||
<p className="pt-2">
|
||||
Note: Changes to an existing template will apply to all future
|
||||
documents created from that template but won't affect documents that
|
||||
are already sent out.
|
||||
</p>
|
||||
</Trans>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
@@ -36,7 +36,7 @@ const dashboardJson = [
|
||||
queryType: "",
|
||||
class: "contracts_Document",
|
||||
query:
|
||||
'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',
|
||||
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true},"Placeholders":{"#*exists":true},"SignedUrl":{"#*exists":true},"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&limit=0',
|
||||
key: "count",
|
||||
Redirect_type: "Report",
|
||||
Redirect_id: "1MwEuxLEkF",
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import Header from "../components/Header";
|
||||
import Footer from "../components/Footer";
|
||||
import Sidebar from "../components/sidebar/Sidebar";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import Tour from "../primitives/Tour";
|
||||
import axios from "axios";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useNavigate, useLocation, Outlet } from "react-router";
|
||||
import { useNavigate, Outlet } from "react-router";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { showHeader } from "../redux/reducers/showHeader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
import SessionExpiredModal from "../primitives/SessionExpiredModal";
|
||||
|
||||
const HomeLayout = () => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const { width } = useWindowSize();
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const arr = useSelector((state) => state.TourSteps);
|
||||
const [isUserValid, setIsUserValid] = useState(true);
|
||||
const isValidSession = useSelector((state) => state.user.isValidSession);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||
const [tourConfigs, setTourConfigs] = useState([]);
|
||||
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -40,42 +36,36 @@ const HomeLayout = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!tenantId) {
|
||||
setIsUserValid(false);
|
||||
} else {
|
||||
(async () => {
|
||||
try {
|
||||
// Use the session token to validate the user
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(Parse?.User?.current()?.id, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
setIsUserValid(false);
|
||||
if (localStorage.getItem("accesstoken")) {
|
||||
if (!tenantId) {
|
||||
dispatch(sessionStatus(false));
|
||||
} else {
|
||||
(async () => {
|
||||
try {
|
||||
// Use the session token to validate the user
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(Parse?.User?.current()?.id, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||
dispatch(sessionStatus(true));
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
dispatch(sessionStatus(true));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("catch");
|
||||
// Session token is invalid or there was an error
|
||||
dispatch(sessionStatus(false));
|
||||
}
|
||||
} catch (error) {
|
||||
// Session token is invalid or there was an error
|
||||
setIsUserValid(false);
|
||||
}
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [tenantId]);
|
||||
|
||||
const showSidebar = () => {
|
||||
setIsOpen((value) => !value);
|
||||
dispatch(showHeader(!isOpen));
|
||||
};
|
||||
useEffect(() => {
|
||||
if (width && width <= 768) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}, [width]);
|
||||
|
||||
useEffect(() => {
|
||||
if (arr && arr.length > 0) {
|
||||
@@ -87,6 +77,7 @@ const HomeLayout = () => {
|
||||
}, [arr]);
|
||||
|
||||
const handleDynamicSteps = () => {
|
||||
const github = "https://github.com/OpenSignLabs/OpenSign";
|
||||
if (arr && arr.length > 0) {
|
||||
// const resArr = arr;
|
||||
const resArr = arr.map((obj, index) => {
|
||||
@@ -105,7 +96,7 @@ const HomeLayout = () => {
|
||||
{
|
||||
selector: '[data-tut="nonpresentmask"]',
|
||||
content: t("tour-mssg.home-layout-1"),
|
||||
position: "center",
|
||||
position: "center"
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="tourbutton"]',
|
||||
@@ -115,8 +106,23 @@ const HomeLayout = () => {
|
||||
...resArr,
|
||||
{
|
||||
selector: '[data-tut="nonpresentmask"]',
|
||||
content: t("tour-mssg.home-layout-3", { appName }),
|
||||
position: "center",
|
||||
content: () => (
|
||||
<div>
|
||||
{t("tour-mssg.home-layout-3", { appName })}
|
||||
<p className="mt-[3px]">
|
||||
⭐ Star us on
|
||||
<a
|
||||
href={github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline font-medium pl-1 cursor-pointer"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
position: "center"
|
||||
}
|
||||
]);
|
||||
checkTourStatus();
|
||||
@@ -146,19 +152,11 @@ const HomeLayout = () => {
|
||||
updatedTourStatus = [{ loginTour: true }];
|
||||
}
|
||||
|
||||
// console.log("updatedTourStatus ", updatedTourStatus);
|
||||
await axios.put(
|
||||
serverUrl + "classes/contracts_Users/" + extUserId,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"X-Parse-Application-Id": appId
|
||||
}
|
||||
}
|
||||
{ TourStatus: updatedTourStatus },
|
||||
{ headers: { "X-Parse-Application-Id": appId } }
|
||||
);
|
||||
// console.log("updatedRes ", updatedRes);
|
||||
};
|
||||
|
||||
async function checkTourStatus() {
|
||||
@@ -175,74 +173,56 @@ const HomeLayout = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const closeSidebar = () => {
|
||||
if (width <= 1023) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = async () => {
|
||||
try {
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
localStorage.removeItem("accesstoken");
|
||||
navigate("/", { replace: true, state: { from: location } });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-[501]">
|
||||
{!isLoader && (
|
||||
<Header showSidebar={showSidebar} setIsMenu={setIsOpen} />
|
||||
)}
|
||||
</div>
|
||||
{isUserValid ? (
|
||||
return isValidSession && localStorage.getItem("accesstoken") ? (
|
||||
<div className="flex flex-col h-screen overflow-hidden">
|
||||
{/* HEADER */}
|
||||
<header className="z-[501]">
|
||||
{!isLoader && <Header setIsLoggingOut={setIsLoggingOut} />}
|
||||
</header>
|
||||
{isLoader ? (
|
||||
<div className="flex h-[100vh] justify-center items-center">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{isLoader ? (
|
||||
<div className="flex h-[100vh] justify-center items-center">
|
||||
{isLoggingOut && (
|
||||
<div className="inset-0 bg-black/30 z-[1000] fixed flex justify-center items-center">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex md:flex-row flex-col z-50">
|
||||
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||
<div
|
||||
id="renderList"
|
||||
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
|
||||
>
|
||||
<div className="bg-base-200 p-3">{<Outlet />}</div>
|
||||
<div className="z-30">
|
||||
<Footer />
|
||||
</div>
|
||||
)}
|
||||
{/* BODY */}
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* SIDEBAR with width animation */}
|
||||
<Sidebar />
|
||||
{/* MAIN (includes both content + footer in one scrollable column) */}
|
||||
<main
|
||||
id="renderList"
|
||||
className="flex-1 overflow-auto transition-all duration-300 ease-in-out"
|
||||
>
|
||||
<div className="flex flex-col min-h-full">
|
||||
{/* your page content */}
|
||||
<div className="p-3">{<Outlet />}</div>
|
||||
{/* sticky-but-scrollable footer */}
|
||||
<div className="mt-auto z-30">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
// disableInteraction={true}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
||||
<p className="text-xl font-medium">Your session has expired.</p>
|
||||
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
|
||||
{t("login")}
|
||||
</button>
|
||||
</main>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
// disableInteraction={true}
|
||||
scrollOffset={-100}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<SessionExpiredModal />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { NavLink, useNavigate } from "react-router";
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { useDispatch } from "react-redux";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
@@ -73,6 +72,7 @@ const AddAdmin = () => {
|
||||
const defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
const PageLanding = localStorage.getItem("PageLanding");
|
||||
const userSettings = localStorage.getItem("userSettings");
|
||||
const favicon = localStorage.getItem("favicon");
|
||||
|
||||
localStorage.clear();
|
||||
saveLanguageInLocal(i18n);
|
||||
@@ -84,12 +84,13 @@ const AddAdmin = () => {
|
||||
localStorage.setItem("userSettings", userSettings);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("favicon", favicon);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
alert(t("valid-email-alert"));
|
||||
} else {
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
@@ -265,7 +266,6 @@ const AddAdmin = () => {
|
||||
};
|
||||
return (
|
||||
<div className="h-screen flex justify-center">
|
||||
<Title title="Add admin" />
|
||||
{state.loading ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
<Loader />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import { Navigate } from "react-router";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function ChangePassword() {
|
||||
@@ -9,41 +8,70 @@ function ChangePassword() {
|
||||
const [currentpassword, setCurrentPassword] = useState("");
|
||||
const [newpassword, setnewpassword] = useState("");
|
||||
const [confirmpassword, setconfirmpassword] = useState("");
|
||||
const [lengthValid, setLengthValid] = useState(false);
|
||||
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
||||
const [specialCharValid, setSpecialCharValid] = useState(false);
|
||||
const [showNewPassword, setNewShowPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const toggleNewPasswordVisibility = () => {
|
||||
setNewShowPassword(!showNewPassword);
|
||||
};
|
||||
const toggleConfirmPasswordVisibility = () => {
|
||||
setShowConfirmPassword(!showConfirmPassword);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (e) => {
|
||||
const newPassword = e.target.value;
|
||||
setconfirmpassword(newPassword);
|
||||
// Check conditions separately
|
||||
setLengthValid(newPassword.length >= 8);
|
||||
setCaseDigitValid(
|
||||
/[a-z]/.test(newPassword) &&
|
||||
/[A-Z]/.test(newPassword) &&
|
||||
/\d/.test(newPassword)
|
||||
);
|
||||
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
|
||||
};
|
||||
const handleSubmit = async (evt) => {
|
||||
evt.preventDefault();
|
||||
try {
|
||||
if (newpassword === confirmpassword) {
|
||||
Parse.User.logIn(localStorage.getItem("userEmail"), currentpassword)
|
||||
.then(async (user) => {
|
||||
if (user) {
|
||||
const User = new Parse.User();
|
||||
const query = new Parse.Query(User);
|
||||
await query.get(user.id).then((user) => {
|
||||
// Updates the data we want
|
||||
user.set("password", newpassword);
|
||||
user
|
||||
.save()
|
||||
.then(async () => {
|
||||
let _user = user.toJSON();
|
||||
if (_user) {
|
||||
await Parse.User.become(_user.sessionToken);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
}
|
||||
alert(t("password-update-alert-1"));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err", error);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alert(t("password-update-alert-2"));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(t("password-update-alert-3"));
|
||||
console.error("Error while logging in user", error);
|
||||
});
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
Parse.User.logIn(localStorage.getItem("userEmail"), currentpassword)
|
||||
.then(async (user) => {
|
||||
if (user) {
|
||||
const User = new Parse.User();
|
||||
const query = new Parse.Query(User);
|
||||
await query.get(user.id).then((user) => {
|
||||
// Updates the data we want
|
||||
user.set("password", newpassword);
|
||||
user
|
||||
.save()
|
||||
.then(async () => {
|
||||
let _user = user.toJSON();
|
||||
if (_user) {
|
||||
await Parse.User.become(_user.sessionToken);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
}
|
||||
setCurrentPassword("");
|
||||
setnewpassword("");
|
||||
setconfirmpassword("");
|
||||
alert(t("password-update-alert-1"));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err", error);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
alert(t("password-update-alert-2"));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert(t("password-update-alert-3"));
|
||||
console.error("Error while logging in user", error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alert(t("password-update-alert-4"));
|
||||
}
|
||||
@@ -56,7 +84,6 @@ function ChangePassword() {
|
||||
}
|
||||
return (
|
||||
<div className="w-full bg-base-100 text-base-content shadow rounded-box p-2">
|
||||
<Title title="Change Password" />
|
||||
<div className="text-xl font-bold border-b-[1px] border-gray-300">
|
||||
{t("change-password")}
|
||||
</div>
|
||||
@@ -82,34 +109,93 @@ function ChangePassword() {
|
||||
<label htmlFor="newpassword" className="text-xs block ml-1">
|
||||
{t("new-password")}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="newpassword"
|
||||
value={newpassword}
|
||||
onChange={(e) => setnewpassword(e.target.value)}
|
||||
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||
placeholder={t("new-password")}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showNewPassword ? "text" : "password"}
|
||||
name="newpassword"
|
||||
value={newpassword}
|
||||
onChange={(e) => setnewpassword(e.target.value)}
|
||||
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||
placeholder={t("new-password")}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
<span
|
||||
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
|
||||
onClick={toggleNewPasswordVisibility}
|
||||
>
|
||||
{showNewPassword ? (
|
||||
<i className="fa fa-eye-slash" /> // Close eye icon
|
||||
) : (
|
||||
<i className="fa fa-eye" /> // Open eye icon
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="newpassword" className="text-xs block ml-1">
|
||||
<label htmlFor="confirmpassword" className="text-xs block ml-1">
|
||||
{t("confirm-password")}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="confirmpassword"
|
||||
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||
value={confirmpassword}
|
||||
onChange={(e) => setconfirmpassword(e.target.value)}
|
||||
placeholder={t("confirm-password")}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
name="confirmpassword"
|
||||
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||
value={confirmpassword}
|
||||
onChange={handlePasswordChange}
|
||||
placeholder={t("confirm-password")}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
<span
|
||||
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
|
||||
onClick={toggleConfirmPasswordVisibility}
|
||||
>
|
||||
{showConfirmPassword ? (
|
||||
<i className="fa fa-eye-slash" /> // Close eye icon
|
||||
) : (
|
||||
<i className="fa fa-eye" /> // Open eye icon
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{confirmpassword.length > 0 && (
|
||||
<div className="mt-1 text-[11px]">
|
||||
{newpassword.length > 0 && (
|
||||
<p
|
||||
className={`${newpassword === confirmpassword ? "text-green-600" : "text-red-600"} text-[11px] mt-1`}
|
||||
>
|
||||
{newpassword === confirmpassword ? "✓" : "✗"}{" "}
|
||||
{t("password-match-length")}
|
||||
</p>
|
||||
)}
|
||||
<p
|
||||
className={`${lengthValid ? "text-green-600" : "text-red-600"}`}
|
||||
>
|
||||
{lengthValid ? "✓" : "✗"} {t("password-length")}
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
caseDigitValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{caseDigitValid ? "✓" : "✗"} {t("password-case")}
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
specialCharValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{specialCharValid ? "✓" : "✗"} {t("password-special-char")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary shadow-md mt-2"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import GetDashboard from "../components/dashboard/GetDashboard";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import Title from "../components/Title";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
|
||||
import dashboardJson from "../json/dashboardJson";
|
||||
@@ -57,7 +56,6 @@ const Dashboard = () => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title="Dashboard" />
|
||||
{loading ? (
|
||||
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
|
||||
<Loader />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import Title from "../components/Title";
|
||||
import RenderDebugPdf from "../components/RenderDebugPdf";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Alert from "../primitives/Alert";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function processDimensions(x, y, width, height) {
|
||||
if (width < 0) {
|
||||
@@ -27,6 +27,7 @@ function processDimensions(x, y, width, height) {
|
||||
};
|
||||
}
|
||||
const DebugPdf = () => {
|
||||
const { t } = useTranslation();
|
||||
const { width } = useWindowSize();
|
||||
const [pdf, setPdf] = useState("");
|
||||
const [isModal, setIsModal] = useState(true);
|
||||
@@ -215,8 +216,7 @@ const DebugPdf = () => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
<Title title={"Debug Pdf"} />
|
||||
{copied && <Alert type="success">{t("copied")}</Alert>}
|
||||
{width < 800 ? (
|
||||
<HandleError handleError={"Debug PDF only availble for PC"} />
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Confetti from "react-confetti"; // Import the confetti library
|
||||
import {
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Loader from "../primitives/Loader";
|
||||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||||
import Title from "../components/Title";
|
||||
import CheckCircle from "../primitives/CheckCircle";
|
||||
|
||||
const DocSuccessPage = () => {
|
||||
@@ -42,7 +41,7 @@ const DocSuccessPage = () => {
|
||||
objectId: docId,
|
||||
SignedUrl: docUrl,
|
||||
CertificateUrl: certificate,
|
||||
IsCompleted: completed
|
||||
IsCompleted: completed,
|
||||
};
|
||||
setPdfDetails([details]);
|
||||
const base64Pdf = await getBase64FromUrl(docUrl);
|
||||
@@ -53,7 +52,9 @@ const DocSuccessPage = () => {
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
if (pdfDetails?.[0]?.IsCompleted) {
|
||||
if (
|
||||
pdfDetails?.[0]?.IsCompleted
|
||||
) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url);
|
||||
@@ -62,7 +63,6 @@ const DocSuccessPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title title="Success" />
|
||||
{/* Confetti Effect */}
|
||||
{showConfetti && (
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
@@ -101,21 +101,26 @@ const DocSuccessPage = () => {
|
||||
<span>{t("download")}</span>
|
||||
</button>
|
||||
|
||||
{pdfDetails?.[0]?.IsCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||
}
|
||||
className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-award mx-[3px] md:mx-0"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{t("certificate")}</span>
|
||||
</button>
|
||||
)}
|
||||
{
|
||||
pdfDetails?.[0]?.IsCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-award mx-[3px] md:mx-0"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{t("certificate")}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, setIsDownloading, pdfDetails)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import Parse from "parse";
|
||||
@@ -40,7 +39,7 @@ function ForgotPassword() {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
alert(t("valid-email-alert"));
|
||||
} else {
|
||||
setIsLoading(true);
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
@@ -87,7 +86,6 @@ function ForgotPassword() {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<Title title="Forgot password" />
|
||||
{toast?.message && <Alert type={toast.type}>{toast.message}</Alert>}
|
||||
<div className="md:p-10 lg:p-16">
|
||||
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
||||
|
||||
@@ -5,7 +5,6 @@ import Parse from "parse";
|
||||
import Alert from "../primitives/Alert";
|
||||
import SelectFolder from "../components/shared/fields/SelectFolder";
|
||||
import SignersInput from "../components/shared/fields/SignersInput";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import {
|
||||
@@ -31,6 +30,8 @@ import ModalUi from "../primitives/ModalUi";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { sessionStatus } from "../redux/reducers/userReducer";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
@@ -45,12 +46,12 @@ function Form() {
|
||||
}
|
||||
|
||||
const Forms = (props) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const abortController = new AbortController();
|
||||
const inputFileRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [signers, setSigners] = useState([]);
|
||||
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -99,6 +100,7 @@ const Forms = (props) => {
|
||||
extUserData?.IsTourEnabled === false
|
||||
? "false"
|
||||
: "true";
|
||||
const fileSize = maxFileSize;
|
||||
useEffect(() => {
|
||||
handleReset();
|
||||
return () => abortController.abort();
|
||||
@@ -110,12 +112,12 @@ const Forms = (props) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const initializeValues = async () => {
|
||||
setFormData((obj) => ({
|
||||
...obj,
|
||||
NotifyOnSignatures: true,
|
||||
SendinOrder: sendinorder,
|
||||
IsTourEnabled: istourenabled
|
||||
}));
|
||||
setFormData((obj) => ({
|
||||
...obj,
|
||||
NotifyOnSignatures: true,
|
||||
SendinOrder: sendinorder,
|
||||
IsTourEnabled: istourenabled
|
||||
}));
|
||||
};
|
||||
|
||||
// `removeFile` is used to reset progress, percentage and remove file if exists
|
||||
@@ -137,11 +139,10 @@ const Forms = (props) => {
|
||||
return;
|
||||
}
|
||||
// setFormData((prev) => ({ ...prev, file: files[0] }));
|
||||
const totalMb = Math.round(
|
||||
files.reduce((sum, f) => sum + f.size, 0) / Math.pow(1024, 2)
|
||||
);
|
||||
if (totalMb > maxFileSize) {
|
||||
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
|
||||
const totalBytes = Math.round(files.reduce((sum, f) => sum + f.size, 0)); // in bytes
|
||||
const fileSizeBytes = fileSize * 1024 * 1024;
|
||||
if (totalBytes > fileSizeBytes) {
|
||||
alert(`${t("file-alert-1")} ${fileSize} MB`);
|
||||
setFileUpload("");
|
||||
setSelectedFiles([]);
|
||||
removeFile(e);
|
||||
@@ -195,7 +196,9 @@ const Forms = (props) => {
|
||||
setIsDecrypting(false);
|
||||
setfileload(false);
|
||||
removeFile(e);
|
||||
alert(`Incorrect password for file: ${file.name}`);
|
||||
alert(
|
||||
t("incorrect-password-for-file", { file: file.name })
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error("password not provided");
|
||||
@@ -273,10 +276,12 @@ const Forms = (props) => {
|
||||
setfileload(false);
|
||||
removeFile(e);
|
||||
console.log("err in docx to pdf ", err);
|
||||
const error = isOpenSignDomain
|
||||
? `${t("docx-error")} ${t("docx-error-contact")}`
|
||||
: t("docx-error");
|
||||
alert(error);
|
||||
const error = t("docx-error");
|
||||
if (err?.code === 209) {
|
||||
dispatch(sessionStatus(false));
|
||||
} else {
|
||||
alert(error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -300,28 +305,29 @@ const Forms = (props) => {
|
||||
const name = generatePdfName(16);
|
||||
const pdfName = `${name}.pdf`;
|
||||
let uploadedUrl = "";
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...pdfBytes],
|
||||
"application/pdf"
|
||||
);
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
if (fileRes.url) {
|
||||
uploadedUrl = fileRes.url;
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...pdfBytes],
|
||||
"application/pdf"
|
||||
);
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total) => {
|
||||
if (progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
if (fileRes.url) {
|
||||
uploadedUrl = fileRes.url;
|
||||
}
|
||||
}
|
||||
if (uploadedUrl) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(pdfBytes.byteLength, uploadedUrl, tenantId);
|
||||
const userId = extUserData?.UserId?.objectId;
|
||||
SaveFileSize(pdfBytes.byteLength, uploadedUrl, tenantId, userId);
|
||||
setFileUpload(uploadedUrl);
|
||||
setfileload(false);
|
||||
const title = generateTitleFromFilename(filesNameArr?.[0]);
|
||||
@@ -333,8 +339,13 @@ const Forms = (props) => {
|
||||
setSelectedFiles([]);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
if (error?.code === 209) {
|
||||
dispatch(sessionStatus(false));
|
||||
} else {
|
||||
alert(error.message);
|
||||
}
|
||||
setSelectedFiles([]);
|
||||
removeFile(e);
|
||||
}
|
||||
};
|
||||
// `isValidURL` is used to check valid webhook url
|
||||
@@ -399,11 +410,11 @@ const Forms = (props) => {
|
||||
object.set("RemindOnceInEvery", remindOnceInEvery);
|
||||
object.set("IsTourEnabled", isTourEnabled);
|
||||
object.set("TimeToCompleteDays", TimeToCompleteDays);
|
||||
object.set("AllowModifications", false);
|
||||
object.set("IsEnableOTP", false);
|
||||
if (formData.NotifyOnSignatures !== undefined) {
|
||||
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
||||
}
|
||||
object.set("AllowModifications", false);
|
||||
object.set("IsEnableOTP", false);
|
||||
if (formData.NotifyOnSignatures !== undefined) {
|
||||
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
||||
}
|
||||
if (formData?.RedirectUrl) {
|
||||
object.set("RedirectUrl", formData.RedirectUrl);
|
||||
}
|
||||
@@ -440,9 +451,9 @@ const Forms = (props) => {
|
||||
setBcc([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
setFormData({
|
||||
Name: "",
|
||||
Description: "",
|
||||
@@ -469,7 +480,9 @@ const Forms = (props) => {
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.message === "only 15 reminder allowed") {
|
||||
if (err?.code === 209) {
|
||||
dispatch(sessionStatus(false));
|
||||
} else if (err.message === "only 15 reminder allowed") {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: t("only-15-reminder-allowed")
|
||||
@@ -520,9 +533,9 @@ const Forms = (props) => {
|
||||
setBcc([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
extUserData?.NotifyOnSignatures !== undefined
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
let obj = {
|
||||
Name: "",
|
||||
Description: "",
|
||||
@@ -555,41 +568,34 @@ const Forms = (props) => {
|
||||
e.preventDefault();
|
||||
setIsPassword(false);
|
||||
setfileload(true);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const userId = extUserData?.UserId?.objectId;
|
||||
try {
|
||||
const size = formData?.file?.size;
|
||||
const name = generatePdfName(16);
|
||||
const pdfFile = await decryptPdf(formData?.file, formData?.password);
|
||||
setIsDecrypting(false);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { 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(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
|
||||
}
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total) => {
|
||||
if (progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
const fileRes = await getSecureUrl(parseFile.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
removeFile();
|
||||
const title = generateTitleFromFilename(formData?.file?.name);
|
||||
setFormData((obj) => ({ ...obj, password: "", Name: title }));
|
||||
SaveFileSize(size, fileRes?.url, tenantId, userId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile();
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
@@ -598,9 +604,19 @@ const Forms = (props) => {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removeFile();
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setIsDecrypting(false);
|
||||
if (inputFileRef.current) {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
removeFile();
|
||||
if (err?.response?.status === 401) {
|
||||
if (err?.code === 209) {
|
||||
dispatch(sessionStatus(false));
|
||||
} else if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
setIsCorrectPass(false);
|
||||
} else {
|
||||
@@ -629,11 +645,12 @@ const Forms = (props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content">
|
||||
<Title title={props?.title} />
|
||||
<div
|
||||
className={`${isSubmit ? "" : "shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content"}`}
|
||||
>
|
||||
{isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
|
||||
{isSubmit ? (
|
||||
<div className="h-[300px] flex justify-center items-center">
|
||||
<div className="flex flex-col justify-center items-center h-[100vh]">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
@@ -680,9 +697,7 @@ const Forms = (props) => {
|
||||
</ModalUi>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-[11px]">
|
||||
<h1 className="text-[20px] font-semibold">
|
||||
{t(`form-name.${props?.title}`)}
|
||||
</h1>
|
||||
<h1 className="text-[20px] font-semibold">{t(props?.title)}</h1>
|
||||
{props.title === "Sign Yourself" && (
|
||||
<div className="text-gray-500 text-xs mt-1">
|
||||
{t("signyour-self-description")}
|
||||
@@ -793,6 +808,7 @@ const Forms = (props) => {
|
||||
onChange={handleSigners}
|
||||
isReset={isReset}
|
||||
helptextZindex={50}
|
||||
isAddYourSelfCheckbox
|
||||
required
|
||||
/>
|
||||
)}
|
||||
@@ -1029,6 +1045,7 @@ const Forms = (props) => {
|
||||
helptextZindex={50}
|
||||
helpText={t("bcc-help")}
|
||||
isCaptureAllData
|
||||
isAddYourSelfCheckbox
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -1037,16 +1054,13 @@ const Forms = (props) => {
|
||||
{isAdvanceOpt && (
|
||||
<div
|
||||
style={{
|
||||
height:
|
||||
props.title === "New Template"
|
||||
? "100px"
|
||||
: "280px"
|
||||
height: props.title === "New Template" ? "100px" : "280px"
|
||||
}}
|
||||
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
|
||||
></div>
|
||||
)}
|
||||
{isAdvanceOpt && (
|
||||
<div className="card bg-base-100 rounded-box p-3 flex-grow ">
|
||||
<div className="card bg-base-100 rounded-box p-3 flex-grow ">
|
||||
{formData?.autoreminder === true &&
|
||||
props.title !== "New Template" && (
|
||||
<div className="text-xs mt-2">
|
||||
@@ -1149,11 +1163,7 @@ const Forms = (props) => {
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
@@ -1162,11 +1172,7 @@ const Forms = (props) => {
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
@@ -1223,7 +1229,7 @@ const Forms = (props) => {
|
||||
{t("next")}
|
||||
</button>
|
||||
<div
|
||||
className="op-btn op-btn-ghost"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
onClick={() => handleCancel()}
|
||||
>
|
||||
{t("cancel")}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router";
|
||||
import axios from "axios";
|
||||
import {
|
||||
@@ -14,7 +14,6 @@ import Parse from "parse";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SelectLanguage from "../components/pdf/SelectLanguage";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
@@ -36,8 +35,14 @@ function GuestLogin() {
|
||||
const [documentId, setDocumentId] = useState(id);
|
||||
const [contactId, setContactId] = useState(contactBookId);
|
||||
const [sendmail, setSendmail] = useState();
|
||||
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||
|
||||
const [contact, setContact] = useState({
|
||||
name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
jobTitle: "",
|
||||
company: ""
|
||||
});
|
||||
const [isOptionalDetails, setIsOptionalDetails] = useState(false);
|
||||
|
||||
const navigateToDoc = async (docId, contactId) => {
|
||||
try {
|
||||
@@ -72,8 +77,10 @@ function GuestLogin() {
|
||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||
const handleServerUrl = async () => {
|
||||
setAppLogo(logo);
|
||||
const favicon = localStorage.getItem("favicon");
|
||||
|
||||
localStorage.clear(); // Clears everything
|
||||
localStorage.setItem("favicon", favicon);
|
||||
localStorage.setItem(
|
||||
"appname",
|
||||
"OpenSign™"
|
||||
@@ -214,7 +221,7 @@ function GuestLogin() {
|
||||
const handleUserData = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) {
|
||||
alert("Please enter a valid email address.");
|
||||
alert(t("valid-email-alert"));
|
||||
} else {
|
||||
const params = { ...contact, docId: documentId };
|
||||
try {
|
||||
@@ -253,7 +260,6 @@ function GuestLogin() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title title="Request Sign" />
|
||||
|
||||
{/* OTP Verification Modal */}
|
||||
{EnterOTP && (
|
||||
@@ -356,7 +362,7 @@ function GuestLogin() {
|
||||
<div className="mb-2">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
@@ -372,13 +378,14 @@ function GuestLogin() {
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
placeholder={t("enter-name")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
@@ -389,27 +396,79 @@ function GuestLogin() {
|
||||
value={contact.email}
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
placeholder={t("enter-email")}
|
||||
required
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2.5">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
value={contact.phone}
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
disabled={loading}
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
{isOptionalDetails && (
|
||||
<>
|
||||
<div className="mb-2">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
value={contact.phone}
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
disabled={loading}
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label
|
||||
htmlFor="company"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("company")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
name="company"
|
||||
value={contact.company}
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
disabled={loading}
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label
|
||||
htmlFor="jobTitle"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("job-title")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="jobTitle"
|
||||
name="jobTitle"
|
||||
value={contact.jobTitle}
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
disabled={loading}
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsOptionalDetails(!isOptionalDetails);
|
||||
}}
|
||||
className="op-link op-link-secondary max-w-fit text-xs"
|
||||
>
|
||||
{isOptionalDetails
|
||||
? t("hide-optional-details")
|
||||
: t("optional-details")}
|
||||
</button>
|
||||
<div className="mt-2 flex justify-start">
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import { useDispatch } from "react-redux";
|
||||
import axios from "axios";
|
||||
import Title from "../components/Title";
|
||||
import { NavLink, useNavigate, useLocation } from "react-router";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
@@ -48,10 +47,14 @@ function Login() {
|
||||
const [image, setImage] = useState();
|
||||
const [errMsg, setErrMsg] = useState();
|
||||
useEffect(() => {
|
||||
checkUserExt();
|
||||
handleUserExist();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const handleUserExist = async () => {
|
||||
checkUserExt();
|
||||
};
|
||||
|
||||
|
||||
const setLocalVar = (user) => {
|
||||
localStorage.setItem("accesstoken", user.sessionToken);
|
||||
@@ -125,13 +128,17 @@ function Login() {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error while logging in user", error);
|
||||
showToast("danger", "Invalid username/password or region");
|
||||
if (error?.code === 1001) {
|
||||
showToast("danger", t("action-prohibited"));
|
||||
} else {
|
||||
showToast("danger", t("invalid-username-password-region"));
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleLoginBtn = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
alert(t("valid-email-alert"));
|
||||
return;
|
||||
}
|
||||
await handleLogin();
|
||||
@@ -329,6 +336,7 @@ function Login() {
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
let favicon = localStorage.getItem("favicon");
|
||||
|
||||
localStorage.clear();
|
||||
saveLanguageInLocal(i18n);
|
||||
@@ -339,6 +347,7 @@ function Login() {
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("favicon", favicon);
|
||||
};
|
||||
|
||||
const continueLoginFlow = async () => {
|
||||
@@ -403,8 +412,7 @@ function Login() {
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Title title="Login" />
|
||||
<>
|
||||
{state.loading && (
|
||||
<div
|
||||
aria-live="assertive"
|
||||
@@ -495,7 +503,7 @@ function Login() {
|
||||
to="/forgetpassword"
|
||||
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
|
||||
>
|
||||
{t("forgot-password")}
|
||||
{t("forgot-password")}?
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
@@ -539,7 +547,7 @@ function Login() {
|
||||
<label
|
||||
htmlFor="Company"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("company")}{" "}
|
||||
<span className="text-[red] text-[13px]">*</span>
|
||||
@@ -566,7 +574,7 @@ function Login() {
|
||||
<label
|
||||
htmlFor="JobTitle"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
className="block text-xs font-semibold"
|
||||
>
|
||||
{t("job-title")}
|
||||
<span className="text-[red] text-[13px]">*</span>
|
||||
@@ -599,7 +607,7 @@ function Login() {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost"
|
||||
className="op-btn op-btn-ghost text-base-content"
|
||||
onClick={logOutUser}
|
||||
>
|
||||
{t("cancel")}
|
||||
@@ -616,7 +624,7 @@ function Login() {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default Login;
|
||||
|
||||