Compare commits

...
Author SHA1 Message Date
prafull-opensignlabsandGitHub ca1b87309f Merge pull request #2011 from OpenSignLabs/updates-18996309218
feat: handle undefine username/password for SMTP
2025-11-01 17:24:58 +05:30
prafull-opensignlabs 0b4548130d feat: handle undefine username/password for SMTP 2025-11-01 11:53:04 +00:00
prafull-opensignlabsandGitHub e8b3c7fed8 Merge pull request #1994 from OpenSignLabs/updates-18555669223
v2.30.0
2025-10-16 14:19:28 +05:30
prafull-opensignlabs 2ce43ec2c8 v2.30.0 2025-10-16 08:48:28 +00:00
prafull-opensignlabsandGitHub d5af1e8a17 Merge pull request #1973 from OpenSignLabs/updates-18164868546
v2.29.2
2025-10-01 19:38:24 +05:30
prafull-opensignlabs fab7998e22 refactor: handle session expired modal 2025-10-01 14:07:45 +00:00
prafull-opensignlabsandGitHub 4a4db6851e Merge pull request #1969 from OpenSignLabs/updates-18129802734
v2.29.1
2025-09-30 17:57:55 +05:30
prafull-opensignlabs 870973e7f3 Merge pull request 2025-09-30 12:26:28 +00:00
prafull-opensignlabsandGitHub 4146680c1e Merge pull request #1966 from OpenSignLabs/updates-18086083096
v2.29.0
2025-09-29 10:24:16 +05:30
prafull-opensignlabs d9fcc69ca4 v2.29.0 2025-09-29 04:47:23 +00:00
prafull-opensignlabsandGitHub d1c405912b Merge pull request #1945 from OpenSignLabs/updates-17798985943
refactor: remove unnecessary code
2025-09-17 18:51:19 +05:30
prafull-opensignlabs 745aca1754 Merge pull request #1298 from nxglabs/sync-to-public_repo-17798472199
Merge pull request #1297 from nxglabs/staging
2025-09-17 13:19:46 +00:00
prafull-opensignlabsandGitHub 672eeea851 Merge pull request #1943 from OpenSignLabs/updates-17788011031
build(deps): build dependencies and dark mode ui
2025-09-17 11:21:02 +05:30
prafull-opensignlabs 98efff65c9 Merge pull request #1295 from nxglabs/sync-to-public_repo-17787708673
fix: show signer name in typed signature instead in selfsign flow
2025-09-17 05:48:01 +00:00
prafull-opensignlabsandGitHub fe21978538 Merge pull request #1941 from OpenSignLabs/updates-17737216200
v2.28.0
2025-09-15 20:23:17 +05:30
prafull-opensignlabs ca4ccfd889 Merge pull request #1292 from nxglabs/sync-to-public_repo-17730712175
Merge pull request #1291 from nxglabs/staging
2025-09-15 14:51:16 +00:00
prafull-opensignlabsandGitHub 6cb2d69566 Merge pull request #1931 from OpenSignLabs/updates-17548337132
fix: getting error 'user not found' while deleting user
2025-09-08 16:22:46 +05:30
prafull-opensignlabs 98b9ebb228 fix: getting error 'user not found' while deleting user 2025-09-08 10:51:52 +00:00
prafull-opensignlabsandGitHub 1c2f453496 Merge pull request #1929 from OpenSignLabs/updates-17540166746
v2.27.2
2025-09-08 10:30:18 +05:30
prafull-opensignlabs b01d8125d9 Merge pull request #1250 from nxglabs/sync-to-public_repo-17512737859
Merge pull request
2025-09-08 04:57:25 +00:00
prafull-opensignlabsandGitHub 85bc2fbbb0 Merge pull request #1902 from OpenSignLabs/updates-16958097538
fix: save sign, initials not working
2025-08-14 12:27:31 +05:30
prafull-opensignlabs e857724502 Merge pull request #1137 from nxglabs/signers_completed_main
fix: not able update public username and save sign, initials not working
2025-08-14 06:53:43 +00:00
prafull-opensignlabsandGitHub 086d8ee5be Merge pull request #1889 from OpenSignLabs/updates-16770567266
fix: managesign report alignment and loader issue
2025-08-06 13:06:40 +05:30
prafull-opensignlabs 04543e826a Merge pull request #1105 from nxglabs/raktima-main-patch-2
fix: managesign report alignment and loader issue
2025-08-06 07:35:09 +00:00
prafull-opensignlabsandGitHub 04f6e38778 Merge pull request #1881 from OpenSignLabs/updates-16679049560
refactor: remove form-name from form title
2025-08-01 21:03:59 +05:30
prafull-opensignlabs 1b5c85fd5f Merge pull request #1085 from nxglabs/signers_completed_main
refactor: remove form-name from form title
2025-08-01 15:33:06 +00:00
prafull-opensignlabsandGitHub 123425d7d1 Merge pull request #1880 from OpenSignLabs/updates-16678047946
feat: allow preferences menu for all user
2025-08-01 20:18:07 +05:30
prafull-opensignlabs a5b9d8178c Merge pull request #1084 from nxglabs/signers_completed_main
feat: allow preferences menu for all user and add smtp setting in it
2025-08-01 14:46:44 +00:00
prafull-opensignlabsandGitHub aefd2ff933 Merge pull request #1868 from OpenSignLabs/updates-16625254839
refactor: correct grammar and normalise font size of signer email in report
2025-07-30 19:54:36 +05:30
nxglabs fe93be8be7 Merge pull request #1070 from nxglabs/signers_completed_main 2025-07-30 14:22:07 +00:00
prafull-opensignlabsandGitHub 369e3660fc Merge pull request #1865 from OpenSignLabs/updates-16598173522
fix: add dynamic truncate for email in list of signers
2025-07-29 19:29:57 +05:30
nxglabs 378c0f057b Merge pull request #1066 from nxglabs/signers_completed_main 2025-07-29 13:54:44 +00:00
prafull-opensignlabsandGitHub 4f3c034cec Merge pull request #1864 from OpenSignLabs/updates-16597572999
Fix: show signers list in completed documents
2025-07-29 19:04:08 +05:30
nxglabs e7d3453744 Merge pull request #1064 from nxglabs/signers_completed_main 2025-07-29 13:30:45 +00:00
prafull-opensignlabsandGitHub 47cc0ef580 Merge pull request #1858 from OpenSignLabs/updates-16525889391
remove checkbox from sign agreement screen
2025-07-25 21:18:33 +05:30
prafull-opensignlabs 04f71be6a2 Merge pull request #1044 from nxglabs/raktima-patch-main-2
fix: remove agreement checkbox and remove agreement checkbox tour
2025-07-25 15:38:38 +00:00
prafull-opensignlabsandGitHub 07603f4bc2 Merge pull request #1856 from OpenSignLabs/imgbot
[ImgBot] Optimize images
2025-07-24 14:40:19 +05:30
ImgBotApp ea16180551 [ImgBot] Optimize images
*Total -- 487.98kb -> 238.96kb (51.03%)

/apps/OpenSign/public/logo512.png -- 10.72kb -> 2.72kb (74.62%)
/apps/OpenSignServer/logo.png -- 86.48kb -> 24.83kb (71.28%)
/apps/OpenSign/src/assets/images/logo.png -- 86.48kb -> 24.83kb (71.28%)
/apps/OpenSign/public/static/js/assets/images/logo.2a7bff0c1189183fafe71d7d5b94e0cd.png -- 86.48kb -> 24.83kb (71.28%)
/apps/OpenSign/public/logo192.png -- 2.13kb -> 0.65kb (69.69%)
/apps/OpenSign/src/assets/images/dp.png -- 12.83kb -> 4.25kb (66.9%)
/apps/OpenSign/src/assets/images/folder.png -- 4.39kb -> 1.93kb (56.05%)
/apps/OpenSign/public/static/js/assets/images/logo-dark.png -- 95.21kb -> 57.21kb (39.91%)
/apps/OpenSignServer/public/assets/images/parse-logo.png -- 5.16kb -> 3.81kb (26.21%)
/apps/OpenSign/src/assets/images/pdf3.png -- 11.66kb -> 9.77kb (16.21%)
/apps/OpenSign/src/assets/images/login_img.svg -- 40.75kb -> 39.10kb (4.04%)
/apps/OpenSign/src/assets/images/recreatedoc.png -- 40.69kb -> 40.08kb (1.48%)
/apps/OpenSign/src/assets/images/pad.svg -- 5.00kb -> 4.94kb (1.07%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2025-07-24 08:28:02 +00:00
prafull-opensignlabsandGitHub 75ad566d54 Merge pull request #1841 from OpenSignLabs/updates-16144605887
v2.26.1
2025-07-08 19:01:19 +05:30
nxglabs fda9fb534b Merge pull request #963 from nxglabs/staging
v2.26.1
2025-07-08 13:27:37 +00:00
prafull-opensignlabsandGitHub cb3e31ebbf Merge pull request #1838 from OpenSignLabs/updates-16124490027
dark-theme logo
2025-07-07 23:43:31 +05:30
nxglabs 400525debb Merge pull request #960 from nxglabs/fix_customlogo
fix: custom applogo not visible in header
2025-07-07 18:07:24 +00:00
196 changed files with 20636 additions and 29649 deletions
+3 -3
View File
@@ -3,20 +3,20 @@
<head> <head>
<meta charset="utf-8" /> <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="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" /> <meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
<!-- <link rel="apple-touch-icon" href="/logo192.png" /> --> <!-- <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" <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" /> integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" /> <link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" />
<title>...Loading</title> <title>...Loading</title>
</head> </head>
<body> <body>
<div id="root" style="touch-action:pan-x pan-y;"></div> <div id="root" style="touch-action:pan-x pan-y;"></div>
<script type="module" src="/src/index.jsx"></script> <script type="module" src="/src/index.jsx"></script>
</body> </body>
</html>
</html>
-17820
View File
File diff suppressed because it is too large Load Diff
+28 -30
View File
@@ -1,18 +1,18 @@
{ {
"name": "open_sign", "name": "open_sign",
"version": "2.21.1", "version": "2.30.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.2", "@formkit/auto-animate": "^0.9.0",
"@imgly/background-removal": "^1.6.0", "@imgly/background-removal": "^1.7.0",
"@lottiefiles/dotlottie-react": "^0.14.2", "@lottiefiles/dotlottie-react": "^0.16.2",
"@pdf-lib/fontkit": "^1.1.1", "@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.2.1", "@radix-ui/themes": "^3.2.1",
"@reduxjs/toolkit": "^2.8.2", "@reduxjs/toolkit": "^2.8.2",
"axios": "^1.10.0", "axios": "^1.12.2",
"date-fns-tz": "^3.2.0", "date-fns-tz": "^3.2.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18next": "^25.3.0", "i18next": "^25.4.0",
"i18next-browser-languagedetector": "^8.2.0", "i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2", "i18next-http-backend": "^3.0.2",
"jszip": "^3.10.1", "jszip": "^3.10.1",
@@ -24,11 +24,11 @@
"print-js": "^1.6.0", "print-js": "^1.6.0",
"prismjs": "^1.30.0", "prismjs": "^1.30.0",
"quill-html-edit-button": "^3.0.0", "quill-html-edit-button": "^3.0.0",
"radix-ui": "^1.4.2", "radix-ui": "^1.4.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-bootstrap": "^2.10.10", "react-bootstrap": "^2.10.10",
"react-confetti": "^6.4.0", "react-confetti": "^6.4.0",
"react-datepicker": "^8.4.0", "react-datepicker": "^8.7.0",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1", "react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^9.0.0", "react-dnd-multi-backend": "^9.0.0",
@@ -36,24 +36,24 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-gtm-module": "^2.0.11", "react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-i18next": "^15.5.3", "react-i18next": "^15.7.4",
"react-konva": "^18.2.10", "react-konva": "^18.2.12",
"react-pdf": "^9.2.1", "react-pdf": "^9.2.1",
"react-quill-new": "^3.4.6", "react-quill-new": "^3.6.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-rnd": "^10.5.2", "react-rnd": "^10.5.2",
"react-router": "^7.6.3", "react-router": "^7.9.3",
"react-scrollbars-custom": "^4.1.1", "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-signature-canvas": "^1.1.0-alpha.2",
"react-timezone-select": "^3.2.8", "react-timezone-select": "^3.2.8",
"react-tooltip": "^5.29.1", "react-tooltip": "^5.29.1",
"reactour": "^1.19.4", "reactour": "^1.19.4",
"redux": "^5.0.1", "redux": "^5.0.1",
"regex-parser": "^2.3.1", "regex-parser": "^2.3.1",
"serve": "^14.2.4", "serve": "^14.2.5",
"styled-components": "^5.3.11", "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" "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
}, },
"scripts": { "scripts": {
@@ -94,36 +94,34 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.27.7", "@babel/core": "^7.28.3",
"@babel/preset-env": "^7.27.2", "@babel/preset-env": "^7.28.3",
"@babel/preset-react": "^7.27.1", "@babel/preset-react": "^7.27.1",
"@babel/runtime-corejs2": "^7.27.6", "@babel/runtime-corejs2": "^7.28.3",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/react": "^18.3.23", "@types/react": "^19.1.13",
"@vitejs/plugin-react": "^4.6.0", "@vitejs/plugin-react": "^4.7.0",
"@vitejs/plugin-react-swc": "^3.10.2",
"@vitest/ui": "^3.2.4", "@vitest/ui": "^3.2.4",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"babel-loader": "^10.0.0", "babel-loader": "^10.0.0",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"concurrently": "^9.2.0", "concurrently": "^9.2.1",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"daisyui": "^4.12.24", "daisyui": "^4.12.24",
"dotenv": "^16.6.1", "eslint": "^9.37.0",
"eslint": "^9.30.0", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-prettier": "^5.5.1",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"jsdom": "^26.1.0", "jsdom": "^27.0.0",
"lint-staged": "^16.1.2", "lint-staged": "^16.1.6",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"pretty-quick": "^4.2.2", "pretty-quick": "^4.2.2",
"rollup-plugin-node-polyfills": "^0.2.1", "rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"vite": "^6.3.5", "vite": "^7.1.4",
"vite-plugin-svgr": "^4.3.0", "vite-plugin-svgr": "^4.5.0",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4" "vitest": "^3.2.4"
}, },
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

+248 -33
View File
@@ -1,4 +1,4 @@
{ {
"header-news": "Neue Funktion: Benutzer des Teams-Plans können jetzt ihre eigenen AWS S3-Buckets für die Dateispeicherung integrieren", "header-news": "Neue Funktion: Benutzer des Teams-Plans können jetzt ihre eigenen AWS S3-Buckets für die Dateispeicherung integrieren",
"header-news-btn": "Jetzt einrichten", "header-news-btn": "Jetzt einrichten",
"sandbox-news": "Dies ist eine Sandbox-Umgebung. Bitte nicht für produktive Zwecke verwenden.", "sandbox-news": "Dies ist eine Sandbox-Umgebung. Bitte nicht für produktive Zwecke verwenden.",
@@ -8,7 +8,7 @@
"dark-mode": "Dunkelmodus", "dark-mode": "Dunkelmodus",
"name": "Name", "name": "Name",
"phone": "Telefon", "phone": "Telefon",
"phone-optional": "optional", "phone-optional": "Optional",
"email": "E-Mail", "email": "E-Mail",
"company": "Unternehmen", "company": "Unternehmen",
"job-title": "Berufsbezeichnung", "job-title": "Berufsbezeichnung",
@@ -26,6 +26,8 @@
"Name": "Name", "Name": "Name",
"Date": "Datum" "Date": "Datum"
}, },
"folder": "Ordner",
"pdf": "Pdf",
"context-menu": { "context-menu": {
"Download": "Herunterladen", "Download": "Herunterladen",
"Rename": "Umbenennen", "Rename": "Umbenennen",
@@ -44,6 +46,10 @@
"contact-now": "Jetzt kontaktieren", "contact-now": "Jetzt kontaktieren",
"upgrade-to": "Upgrade zu", "upgrade-to": "Upgrade zu",
"plan": "Plan", "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.", "subscription-renew-warning": "Ihr Abonnement läuft in {{remainingDays}} Tagen ab. Bitte verlängern Sie Ihr Abonnement.",
"subscribe-card-teamplan": "Entfesseln Sie die volle Kraft der Zusammenarbeit! Erstellen Sie unbegrenzt Organisationen, Teams und Hierarchien. Teilen Sie Vorlagen nahtlos zwischen Teams und weisen Sie benutzerdefinierte Benutzerrollen zu. Optimieren Sie Ihren Workflow noch heute!", "subscribe-card-teamplan": "Entfesseln Sie die volle Kraft der Zusammenarbeit! Erstellen Sie unbegrenzt Organisationen, Teams und Hierarchien. Teilen Sie Vorlagen nahtlos zwischen Teams und weisen Sie benutzerdefinierte Benutzerrollen zu. Optimieren Sie Ihren Workflow noch heute!",
"subscribe-card-plan": "Entsperren Sie Premium-Funktionen ab nur {{premiumPrice}}/Monat. Genießen Sie eine verbesserte Leistung und zahlen Sie nur {{addonPrice}} pro zusätzlichem Credit nach den enthaltenen Premium-Credits.", "subscribe-card-plan": "Entsperren Sie Premium-Funktionen ab nur {{premiumPrice}}/Monat. Genießen Sie eine verbesserte Leistung und zahlen Sie nur {{addonPrice}} pro zusätzlichem Credit nach den enthaltenen Premium-Credits.",
@@ -58,7 +64,7 @@
"welcome": "Willkommen zurück!", "welcome": "Willkommen zurück!",
"Login-to-your-account": "Melden Sie sich bei Ihrem Konto an", "Login-to-your-account": "Melden Sie sich bei Ihrem Konto an",
"password": "Passwort", "password": "Passwort",
"forgot-password": "Passwort vergessen?", "forgot-password": "Passwort vergessen",
"loading": "Wird geladen...", "loading": "Wird geladen...",
"of": "von", "of": "von",
"sign-SSO": "Mit SSO anmelden", "sign-SSO": "Mit SSO anmelden",
@@ -159,7 +165,8 @@
"daily-mail-quota": "Tägliches E-Mail-Kontingent", "daily-mail-quota": "Tägliches E-Mail-Kontingent",
"Save as template": "Als Vorlage speichern", "Save as template": "Als Vorlage speichern",
"Fix & resend": "Korrigieren und erneut senden", "Fix & resend": "Korrigieren und erneut senden",
"Kiosk Mode": "Kiosk Modus" "Kiosk Mode": "Kiosk Modus",
"Reset password": "Passwort zurücksetzen"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nr.", "Sr.No": "Nr.",
@@ -181,7 +188,20 @@
"created-date": "Erstellungsdatum", "created-date": "Erstellungsdatum",
"Type": "Type", "Type": "Type",
"Logs": "Protokolle", "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": { "report-help": {
"Draft Documents": "Dies sind Dokumente, die Sie begonnen, aber noch nicht zum Versenden fertiggestellt haben.", "Draft Documents": "Dies sind Dokumente, die Sie begonnen, aber noch nicht zum Versenden fertiggestellt haben.",
@@ -193,16 +213,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.", "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." "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",
"Sign Yourself": "Selbst unterschreiben", "Request Signatures": "Signaturen anfordern",
"Request Signatures": "Signaturen anfordern", "New Template": "Neue Vorlage",
"New Template": "Neue Vorlage"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "Datei ausgewählt", "file-selected": "Datei ausgewählt",
"template-title": "Vorlagentitel", "template-title": "Vorlagentitel",
"document-title": "Dokumenttitel", "document-title": "Dokumenttitel",
"title": "Titel",
"description": "Beschreibung", "description": "Beschreibung",
"time-to-complete": "Bearbeitungszeit (Tage)", "time-to-complete": "Bearbeitungszeit (Tage)",
"send-in-order": "In der Reihenfolge senden", "send-in-order": "In der Reihenfolge senden",
@@ -280,7 +299,7 @@
"deactivate": "deaktivieren", "deactivate": "deaktivieren",
"this-user": "diesen Benutzer", "this-user": "diesen Benutzer",
"delete-user": "Benutzer löschen", "delete-user": "Benutzer löschen",
"delete": "löschen", "delete": "Löschen",
"add-user": "Benutzer hinzufügen", "add-user": "Benutzer hinzufügen",
"password-generateed": "Das Passwort wird nur einmal generiert; stellen Sie sicher, dass Sie es kopieren.", "password-generateed": "Das Passwort wird nur einmal generiert; stellen Sie sicher, dass Sie es kopieren.",
"Team status": "Teamstatus", "Team status": "Teamstatus",
@@ -367,7 +386,8 @@
"dropdown": "Dropdown", "dropdown": "Dropdown",
"radio button": "Radiobutton", "radio button": "Radiobutton",
"image": "Bild", "image": "Bild",
"email": "E-Mail" "email": "E-Mail",
"number": "Zahl"
}, },
"fields": "Felder", "fields": "Felder",
"recipients": "Empfänger", "recipients": "Empfänger",
@@ -379,6 +399,7 @@
"certificate": "Zertifikat", "certificate": "Zertifikat",
"decline": "Ablehnen", "decline": "Ablehnen",
"finish": "Fertigstellen", "finish": "Fertigstellen",
"done": "Fertig",
"mail": "E-Mail", "mail": "E-Mail",
"sign-now": "Jetzt unterzeichnen", "sign-now": "Jetzt unterzeichnen",
"successfully-signed": "Erfolgreich unterzeichnet!", "successfully-signed": "Erfolgreich unterzeichnet!",
@@ -388,6 +409,10 @@
"Email-verified-alert-1": "E-Mail wurde verifiziert.", "Email-verified-alert-1": "E-Mail wurde verifiziert.",
"Email-verified-alert-2": "E-Mail wurde bereits verifiziert.", "Email-verified-alert-2": "E-Mail wurde bereits verifiziert.",
"upload-stamp-image": "Stempelbild hochladen", "upload-stamp-image": "Stempelbild hochladen",
"draw-signature": "Unterschrift zeichnen",
"draw-initials": "Initialen zeichnen",
"enter-text": "Text eingeben",
"enter-widgettype": "{{widgetType}} eingeben",
"draw": "Zeichnen", "draw": "Zeichnen",
"type": "Schreiben", "type": "Schreiben",
"color-type": { "color-type": {
@@ -400,7 +425,7 @@
"upload": "Hochladen", "upload": "Hochladen",
"initial-teb": "Initialen", "initial-teb": "Initialen",
"signature-tab": "Unterschrift", "signature-tab": "Unterschrift",
"initial-alert": "Meine Initiale nicht gefunden", "initial-alert": "Meine Initiale wurde nicht gefunden",
"copy-title": "Widget kopieren zu", "copy-title": "Widget kopieren zu",
"contact-delete-alert": "Sind Sie sicher, dass Sie diesen Kontakt löschen möchten?", "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.", "reset-password-alert-1": "Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail-Adresse gesendet.",
@@ -419,6 +444,19 @@
"maximum-check": "Maximale Anzahl", "maximum-check": "Maximale Anzahl",
"cell-count": "Zellzahl", "cell-count": "Zellzahl",
"default-value": "Standardwert", "default-value": "Standardwert",
"formula": "Formel",
"formula-placeholder": "Erstellen Sie eine Formel mit Zahlen-Widgets",
"formula-no-widgets": "Für diesen Unterzeichner sind noch keine weiteren Zahlen-Widgets vorhanden.",
"supported-operators": "Unterstützte Operatoren:",
"formula-helper": "Klicken Sie auf einen Zahlen-Widget-Chip, um ihn einzufügen (z. B. {{number-1}} + {{number-2}}).",
"formula-error-letter": "Widget-Namen müssen in doppelte geschweifte Klammern gesetzt werden (z. B. {{number-1}}).",
"formula-error-characters": "Außerhalb der Klammern sind nur Zahlen sowie +, -, *, /, (, ) erlaubt.",
"formula-error-braces": "Jede öffnende {{ muss eine passende }} haben.",
"formula-error-operators": "Verwenden Sie bei Operatoren oder Klammern mindestens zwei Werte.",
"formula-error-leading-operator": "Formeln dürfen nicht mit Operatoren beginnen.",
"decimal-places": "Dezimalstellen",
"number-of-decimal-places": "Anzahl der Dezimalstellen",
"decimal-places-helper": "Auf 0 setzen für ganze Zahlen",
"select": "Auswählen", "select": "Auswählen",
"read-only": "Ist schreibgeschützt", "read-only": "Ist schreibgeschützt",
"hide-labels": "Labels ausblenden", "hide-labels": "Labels ausblenden",
@@ -465,7 +503,7 @@
"add-recipients": "Empfänger hinzufügen", "add-recipients": "Empfänger hinzufügen",
"loading-mssg": "Das könnte etwas Zeit in Anspruch nehmen", "loading-mssg": "Das könnte etwas Zeit in Anspruch nehmen",
"send-mail": "E-Mail senden", "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-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-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?", "placeholder-alert-3": "Sind Sie sicher, dass Sie dieses Dokument zur Unterzeichnung senden möchten?",
@@ -600,7 +638,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "Sie haben sich erfolgreich angemeldet! Werfen wir einen Blick darauf.", "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-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.", "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-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.", "opensign-drive-2": "Klicken Sie auf die Schaltfläche Hinzufügen, um einen neuen Ordner oder ein neues Dokument zu erstellen.",
@@ -610,28 +649,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-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.", "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-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-2": "Klicken Sie auf eines der Felder im Dokument, um zu beginnen. Danach können Sie die erforderlichen Informationen eingeben.",
"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-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-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-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.", "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-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-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-4": "Ziehen oder klicken Sie auf ein Widget, um es dem Dokument 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-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.", "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.", "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", "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 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.", "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 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.", "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.", "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-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-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": "Ziehen Sie ein Feld in das Dokument oder klicken Sie darauf, um es hinzuzufügen.", "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.",
"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.",
"webhook-1": "Jetzt upgraden, um einen Webhook einzurichten", "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.", "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.", "Out for signatures": "Das Klicken auf diese Karte führt Sie zu einer Liste der Dokumente, die auf eine Signatur warten.",
@@ -728,7 +765,7 @@
"public-tour-message": "Die Vorlage muss öffentlich sein, bevor Sie einen teilbaren Link generieren können.", "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.", "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}}", "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.", "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", "allowed-signature-types": "Erlaubte Signaturtypen",
"at-least-one-signature-type": "Mindestens ein Signaturtyp sollte aktiviert sein.", "at-least-one-signature-type": "Mindestens ein Signaturtyp sollte aktiviert sein.",
@@ -776,7 +813,7 @@
"agree-p1": "Ich bestätige, dass ich die ", "agree-p1": "Ich bestätige, dass ich die ",
"agree-p2": "Offenlegung elektronischer Aufzeichnungen und Signaturen", "agree-p2": "Offenlegung elektronischer Aufzeichnungen und Signaturen",
"agree-p3": "gelesen und verstanden habe und der Verwendung elektronischer Aufzeichnungen und Signaturen zustimme.", "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-title": "Allgemeine Geschäftsbedingungen",
"term-cond-h": "OFFENLEGUNG ELEKTRONISCHER AUFZEICHNUNGEN UND SIGNATUREN", "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.", "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 +916,10 @@
"thanks-for-feedback": "Danke für Ihr Feedback 🙏", "thanks-for-feedback": "Danke für Ihr Feedback 🙏",
"share-your-feedback": "Teilen Sie Ihr Feedback", "share-your-feedback": "Teilen Sie Ihr Feedback",
"share-your-review": "Teilen Sie Ihre Bewertung", "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", "date-format": "Datumsformat",
"document-deleted": "Das Dokument wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.", "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?", "save-as-template-?": "Sind Sie sicher, dass Sie dieses Dokument als Vorlage speichern möchten?",
@@ -1032,14 +1073,188 @@
"not-calculated": "Nicht berechnet", "not-calculated": "Nicht berechnet",
"not-found-in-signature": "Nicht in Signatur gefunden", "not-found-in-signature": "Nicht in Signatur gefunden",
"readonly-error": "Das schreibgeschützte {{widgetName}}-Widget muss einen Standardwert haben oder kann optional gemacht werden.", "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-templates": "Vorlagen durchsuchen…",
"search-documents": "Dokumente suchen…", "search-documents": "Dokumente suchen…",
"search-contacts": "Kontakte durchsuchen…", "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}}", "invalid-email-found": "Ungültige E-Mail gefunden: {{email}}",
"duplicate-email-found": "Doppelte E-Mail gefunden: {{email}}", "duplicate-email-found": "Doppelte E-Mail gefunden: {{email}}",
"vertical": "Vertikal", "vertical": "Vertikal",
"horizontal": "Horizontal", "horizontal": "Horizontal",
"billing": "Abrechnung", "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"
}
+256 -40
View File
@@ -8,7 +8,7 @@
"dark-mode": "Dark mode", "dark-mode": "Dark mode",
"name": "Name", "name": "Name",
"phone": "Phone", "phone": "Phone",
"phone-optional": "optional", "phone-optional": "Optional",
"email": "Email", "email": "Email",
"company": "Company", "company": "Company",
"job-title": "Job title", "job-title": "Job title",
@@ -26,6 +26,8 @@
"Name": "Name", "Name": "Name",
"Date": "Date" "Date": "Date"
}, },
"folder": "Folder",
"pdf": "Pdf",
"context-menu": { "context-menu": {
"Download": "Download", "Download": "Download",
"Rename": "Rename", "Rename": "Rename",
@@ -44,6 +46,10 @@
"contact-now": "Contact now", "contact-now": "Contact now",
"upgrade-to": "Upgrade to", "upgrade-to": "Upgrade to",
"plan": "Plan", "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.", "subscription-renew-warning": "Your subscription will expire in {{remainingDays}} days. Please renew your subscription.",
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!", "subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.", "subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
@@ -58,7 +64,7 @@
"welcome": "Welcome back!", "welcome": "Welcome back!",
"Login-to-your-account": "Login to your account", "Login-to-your-account": "Login to your account",
"password": "Password", "password": "Password",
"forgot-password": "Forgot password?", "forgot-password": "Forgot password",
"loading": "Loading...", "loading": "Loading...",
"of": "of", "of": "of",
"sign-SSO": "Sign in with SSO", "sign-SSO": "Sign in with SSO",
@@ -159,7 +165,8 @@
"daily-mail-quota": "Daily Email Quota", "daily-mail-quota": "Daily Email Quota",
"Save as template": "Save as template", "Save as template": "Save as template",
"Fix & resend": "Fix & Resend", "Fix & resend": "Fix & Resend",
"Kiosk Mode": "Kiosk Mode" "Kiosk Mode": "Kiosk Mode",
"Reset password": "Reset password"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Sr.No", "Sr.No": "Sr.No",
@@ -181,7 +188,20 @@
"created-date": "Created date", "created-date": "Created date",
"Type": "Type", "Type": "Type",
"Logs": "Logs", "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": { "report-help": {
"Draft Documents": "These are documents you have started but have not finalized for sending.", "Draft Documents": "These are documents you have started but have not finalized for sending.",
@@ -193,16 +213,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.", "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." "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",
"Sign Yourself": "Sign yourself", "Request Signatures": "Request signatures",
"Request Signatures": "Request signatures", "New Template": "New template",
"New Template": "New template"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "file(s) selected", "file-selected": "file(s) selected",
"template-title": "Template title", "template-title": "Template title",
"document-title": "Document title", "document-title": "Document title",
"title": "Title",
"description": "Description", "description": "Description",
"time-to-complete": "Time to complete (Days)", "time-to-complete": "Time to complete (Days)",
"send-in-order": "Send in order", "send-in-order": "Send in order",
@@ -280,7 +299,7 @@
"deactivate": "deactivate", "deactivate": "deactivate",
"this-user": " this user", "this-user": " this user",
"delete-user": "Delete user", "delete-user": "Delete user",
"delete": "delete", "delete": "Delete",
"add-user": "Add user", "add-user": "Add user",
"password-generateed": "Password will only be generated once; make sure to copy it.", "password-generateed": "Password will only be generated once; make sure to copy it.",
"Team status": "Team status", "Team status": "Team status",
@@ -367,7 +386,8 @@
"dropdown": "dropdown", "dropdown": "dropdown",
"radio button": "radio button", "radio button": "radio button",
"image": "image", "image": "image",
"email": "email" "email": "email",
"number": "number"
}, },
"fields": "Fields", "fields": "Fields",
"recipients": "Recipients", "recipients": "Recipients",
@@ -379,6 +399,7 @@
"certificate": "Certificate", "certificate": "Certificate",
"decline": "Decline", "decline": "Decline",
"finish": "Finish", "finish": "Finish",
"done": "Done",
"mail": "Mail", "mail": "Mail",
"sign-now": "Sign now", "sign-now": "Sign now",
"successfully-signed": "Successfully signed!", "successfully-signed": "Successfully signed!",
@@ -388,6 +409,10 @@
"Email-verified-alert-1": "Email is verified.", "Email-verified-alert-1": "Email is verified.",
"Email-verified-alert-2": "Email is already verified.", "Email-verified-alert-2": "Email is already verified.",
"upload-stamp-image": "Upload stamp image", "upload-stamp-image": "Upload stamp image",
"draw-signature": "Draw signature",
"draw-initials": "Draw initials",
"enter-text": "Enter text",
"enter-widgettype": "Enter {{widgetType}}",
"draw": "Draw", "draw": "Draw",
"type": "Type", "type": "Type",
"color-type": { "color-type": {
@@ -408,6 +433,7 @@
"reset-password-alert-3": "Reset Your Password", "reset-password-alert-3": "Reset Your Password",
"faild-animation": "Failed to load animation", "faild-animation": "Failed to load animation",
"apply": "Apply", "apply": "Apply",
"select-columns": "Select columns",
"copy-type": { "copy-type": {
"All pages": "All pages", "All pages": "All pages",
"All pages but last": "All pages but last", "All pages but last": "All pages but last",
@@ -419,6 +445,19 @@
"maximum-check": "Maximum check", "maximum-check": "Maximum check",
"cell-count": "Cell count", "cell-count": "Cell count",
"default-value": "Default value", "default-value": "Default value",
"formula": "Formula",
"formula-placeholder": "Build a formula using number widgets",
"formula-no-widgets": "No other number widgets are assigned to this signer yet.",
"supported-operators": "Supported operators:",
"formula-helper": "Click a number widget chip to insert it (e.g. {{number-1}} + {{number-2}}).",
"formula-error-letter": "Wrap widget names in double curly braces (e.g. {{number-1}}).",
"formula-error-characters": "Only numbers and +, -, *, /, (, ) are allowed outside of braces.",
"formula-error-braces": "Each opening {{ must have a matching }}.",
"formula-error-operators": "Add at least two values when using operators or parentheses.",
"formula-error-leading-operator": "Formulas cannot start with operators.",
"decimal-places": "Decimal Places",
"number-of-decimal-places": "Number of decimal places",
"decimal-places-helper": "Set to 0 for whole numbers",
"select": "Select", "select": "Select",
"read-only": "read only", "read-only": "read only",
"hide-labels": "Hide labels", "hide-labels": "Hide labels",
@@ -465,13 +504,13 @@
"add-recipients": "Add recipients", "add-recipients": "Add recipients",
"loading-mssg": "This might take some time", "loading-mssg": "This might take some time",
"send-mail": "Send Mail", "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-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-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-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-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 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-5": "Do you want to sign the document right now?",
"placeholder-alert-6": "Please setup mail adapter to send mail!", "placeholder-alert-6": "Please setup mail adapter to send mail!",
"placeholder-alert-7": "Please select signer for add placeholder!", "placeholder-alert-7": "Please select signer for add placeholder!",
@@ -600,7 +639,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "You have logged in successfully! Let's take a look.", "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-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.", "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-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.", "opensign-drive-2": "Click the add button to create a new folder or document.",
@@ -609,35 +649,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-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-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.", "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-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-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": "Click Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .", "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-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-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.", "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-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-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-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 placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.", "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.", "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. ", "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.", "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.", "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. 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. ", "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 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.", "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.", "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-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-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": "Drag or click on a field to add it to the document.", "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.",
"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.",
"webhook-1": "Upgrade now to set webhook", "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.", "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.", "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.", "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.", "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.", "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." "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 +766,7 @@
"public-tour-message": "The template needs to be public before you can generate a shareable link.", "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.", "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}}", "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.", "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", "allowed-signature-types": "Allowed signature types",
"at-least-one-signature-type": "At least one signature type should be enabled.", "at-least-one-signature-type": "At least one signature type should be enabled.",
@@ -776,7 +814,7 @@
"agree-p1": "I confirm that I have read and understood the ", "agree-p1": "I confirm that I have read and understood the ",
"agree-p2": "Electronic Record and Signature Disclosure", "agree-p2": "Electronic Record and Signature Disclosure",
"agree-p3": "and consent to use electronic records and signatures.", "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-title": "Terms and conditions",
"term-cond-h": "ELECTRONIC RECORD AND SIGNATURE DISCLOSURE", "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.", "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 +917,10 @@
"thanks-for-feedback": "Thanks for your feedback 🙏", "thanks-for-feedback": "Thanks for your feedback 🙏",
"share-your-feedback": "Share your feedback", "share-your-feedback": "Share your feedback",
"share-your-review": "Share your review", "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", "date-format": "Date format",
"document-deleted": "The document has been deleted or you don't have access. Please contact the sender.", "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?", "save-as-template-?": "Are you sure you want to save this document as template?",
@@ -976,20 +1018,20 @@
"verify-identity": "Verify identity", "verify-identity": "Verify identity",
"passkey-verification-failed": "Passkey verification failed. Please try again or use OTP.", "passkey-verification-failed": "Passkey verification failed. Please try again or use OTP.",
"security-auth-help": { "security-auth-help": {
"p1":"Manage your account's security settings to keep your data safe. OpenSign supports advanced authentication methods to enhance account protection.", "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.", "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." "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", "signer-already-present": "Signer already present",
"kiosk-sign": "Kiosk Sign", "kiosk-sign": "Kiosk Sign",
"dont-have-access-to-template": "The template has been deleted or you don't have access. Please contact the sender.", "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. ", "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", "learn-more": "Learn more",
"finish-mssg":" Are you sure you want to finish the document ?", "finish-mssg": " Are you sure you want to finish the document ?",
"review":"Review", "review": "Review",
"next-field":"Next Field", "next-field": "Next Field",
"required-mssg":"{{leftRequiredWidget}} of {{totalWidget}} fields left", "required-mssg": "{{leftRequiredWidget}} of {{totalWidget}} fields left",
"verify-document": "Verify document", "verify-document": "Verify document",
"verify-document-signature": "Verify Document Signature", "verify-document-signature": "Verify Document Signature",
"select-pdf-document": "Select PDF Document", "select-pdf-document": "Select PDF Document",
"selected-file": "Selected file", "selected-file": "Selected file",
@@ -1032,14 +1074,188 @@
"not-calculated": "Not calculated", "not-calculated": "Not calculated",
"not-found-in-signature": "Not found in signature", "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.", "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-templates": "Search templates…",
"search-documents": "Search documents…", "search-documents": "Search documents…",
"search-contacts": "Search contacts…", "search-contacts": "Search contacts…",
"add-role-alert": "Please add at least one role",
"edit-draft": "Edit draft",
"invalid-email-found": "Invalid email found: {{email}}", "invalid-email-found": "Invalid email found: {{email}}",
"duplicate-email-found": "Duplicate email found: {{email}}", "duplicate-email-found": "Duplicate email found: {{email}}",
"vertical": "Vertical", "vertical": "Vertical",
"horizontal": "Horizontal", "horizontal": "Horizontal",
"billing": "Billing", "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"
} }
+249 -33
View File
@@ -8,7 +8,7 @@
"dark-mode": "Modo oscuro", "dark-mode": "Modo oscuro",
"name": "Nombre", "name": "Nombre",
"phone": "Teléfono", "phone": "Teléfono",
"phone-optional": "opcional", "phone-optional": "Opcional",
"email": "Correo", "email": "Correo",
"company": "Compañía", "company": "Compañía",
"job-title": "Título del puesto", "job-title": "Título del puesto",
@@ -26,6 +26,8 @@
"Name": "Nombre", "Name": "Nombre",
"Date": "Fecha" "Date": "Fecha"
}, },
"folder": "Carpeta",
"pdf": "Pdf",
"context-menu": { "context-menu": {
"Download": "Descargar", "Download": "Descargar",
"Rename": "Renombrar", "Rename": "Renombrar",
@@ -44,6 +46,10 @@
"contact-now": "Contactar ahora", "contact-now": "Contactar ahora",
"upgrade-to": "Mejorar a", "upgrade-to": "Mejorar a",
"plan": "Plan", "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.", "subscription-renew-warning": "Su suscripción vencerá en {{remainingDays}} días. Por favor, renueve su suscripción.",
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!", "subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.", "subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
@@ -58,7 +64,7 @@
"welcome": "¡Bienvenido de nuevo!", "welcome": "¡Bienvenido de nuevo!",
"Login-to-your-account": "Ingresa a tu cuenta", "Login-to-your-account": "Ingresa a tu cuenta",
"password": "Contraseña", "password": "Contraseña",
"forgot-password": "¿Olvidaste la contraseña?", "forgot-password": "¿Olvidaste la contraseña",
"loading": "Cargando...", "loading": "Cargando...",
"of": "de", "of": "de",
"sign-SSO": "Ingresar con SSO", "sign-SSO": "Ingresar con SSO",
@@ -159,7 +165,8 @@
"daily-mail-quota": "Cuota diaria de correos electrónicos", "daily-mail-quota": "Cuota diaria de correos electrónicos",
"Save as template": "Guardar como plantilla", "Save as template": "Guardar como plantilla",
"Fix & resend": "Corregir y reenviar", "Fix & resend": "Corregir y reenviar",
"Kiosk Mode": "Modo Kiosco" "Kiosk Mode": "Modo Kiosco",
"Reset password": "Restablecer contraseña"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nº", "Sr.No": "Nº",
@@ -181,7 +188,20 @@
"created-date": "Fecha de creación", "created-date": "Fecha de creación",
"Type": "Tipo", "Type": "Tipo",
"Logs": "Registros", "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": { "report-help": {
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.", "Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
@@ -193,16 +213,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.", "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." "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",
"Sign Yourself": "Firmar", "Request Signatures": "Solicitar firmas",
"Request Signatures": "Solicitar firmas", "New Template": "Nueva plantilla",
"New Template": "Nueva plantilla"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "archivo seleccionado", "file-selected": "archivo seleccionado",
"template-title": "Título de la plantilla", "template-title": "Título de la plantilla",
"document-title": "Título del documento", "document-title": "Título del documento",
"title": "Título",
"description": "Descripción", "description": "Descripción",
"time-to-complete": "Plazo de finalización (días)", "time-to-complete": "Plazo de finalización (días)",
"send-in-order": "Enviar en orden", "send-in-order": "Enviar en orden",
@@ -242,7 +261,6 @@
"submit": "Enviar", "submit": "Enviar",
"reset": "Volver a empezar", "reset": "Volver a empezar",
"my-signature": "Mi firma", "my-signature": "Mi firma",
"initial-alert": "Mi inicial no encontrada",
"signature": "Firma", "signature": "Firma",
"upload-image": " Subir imagen", "upload-image": " Subir imagen",
"clear": "Limpiar", "clear": "Limpiar",
@@ -281,7 +299,7 @@
"deactivate": "desactivar", "deactivate": "desactivar",
"this-user": " este usuario", "this-user": " este usuario",
"delete-user": "Eliminar usuario", "delete-user": "Eliminar usuario",
"delete": "eliminar", "delete": "Eliminar",
"add-user": "Agregar usuario", "add-user": "Agregar usuario",
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.", "password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
"Team status": "Estado del equipo", "Team status": "Estado del equipo",
@@ -368,7 +386,8 @@
"dropdown": "desplegable", "dropdown": "desplegable",
"radio button": "botón de radio", "radio button": "botón de radio",
"image": "imagen", "image": "imagen",
"email": "correo" "email": "correo",
"number": "número"
}, },
"fields": "Campos", "fields": "Campos",
"recipients": "Destinatarios", "recipients": "Destinatarios",
@@ -380,6 +399,7 @@
"certificate": "Certificado", "certificate": "Certificado",
"decline": "Rechazar", "decline": "Rechazar",
"finish": "Finalizar", "finish": "Finalizar",
"done": "Hecho",
"mail": "Correo", "mail": "Correo",
"sign-now": "Firmar ahora", "sign-now": "Firmar ahora",
"successfully-signed": "¡Firmado exitosamente!", "successfully-signed": "¡Firmado exitosamente!",
@@ -389,6 +409,10 @@
"Email-verified-alert-1": "El correo está verificado.", "Email-verified-alert-1": "El correo está verificado.",
"Email-verified-alert-2": "El correo ya ha sido verificado.", "Email-verified-alert-2": "El correo ya ha sido verificado.",
"upload-stamp-image": "Subir imagen del sello", "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", "draw": "Dibujar",
"type": "Escribir", "type": "Escribir",
"color-type": { "color-type": {
@@ -401,7 +425,7 @@
"upload": "Subir", "upload": "Subir",
"initial-teb": "Iniciales", "initial-teb": "Iniciales",
"signature-tab": "Firma", "signature-tab": "Firma",
"your-signature": "Tu firma", "initial-alert": "Mi inicial no encontrada",
"copy-title": "Copiar widget a", "copy-title": "Copiar widget a",
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?", "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", "reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
@@ -409,6 +433,7 @@
"reset-password-alert-3": "Restablece tu contraseña", "reset-password-alert-3": "Restablece tu contraseña",
"faild-animation": "Error al cargar la animación", "faild-animation": "Error al cargar la animación",
"apply": "Aplicar", "apply": "Aplicar",
"select-columns": "Seleccionar columnas",
"copy-type": { "copy-type": {
"All pages": "Todas las páginas", "All pages": "Todas las páginas",
"All pages but last": "Todas las páginas menos la última", "All pages but last": "Todas las páginas menos la última",
@@ -420,6 +445,19 @@
"maximum-check": "Chequeo máximo", "maximum-check": "Chequeo máximo",
"cell-count": "recuento de células", "cell-count": "recuento de células",
"default-value": "Valor por defecto", "default-value": "Valor por defecto",
"formula": "Fórmula",
"formula-placeholder": "Crea una fórmula utilizando widgets numéricos",
"formula-no-widgets": "Todavía no hay otros widgets numéricos asignados a este firmante.",
"supported-operators": "Operadores compatibles:",
"formula-helper": "Haz clic en una ficha de widget numérico para insertarla (p. ej. {{number-1}} + {{number-2}}).",
"formula-error-letter": "Envuelve los nombres de los widgets entre dobles llaves (p. ej. {{number-1}}).",
"formula-error-characters": "Fuera de las llaves solo se permiten números y +, -, *, /, (, ).",
"formula-error-braces": "Cada {{ de apertura debe tener una }} correspondiente.",
"formula-error-operators": "Cuando uses operadores o paréntesis, agrega al menos dos valores.",
"formula-error-leading-operator": "Las fórmulas no pueden comenzar con operadores.",
"decimal-places": "Decimales",
"number-of-decimal-places": "Número de decimales",
"decimal-places-helper": "Establecer en 0 para números enteros",
"select": "Seleccionar", "select": "Seleccionar",
"read-only": "Es de solo lectura", "read-only": "Es de solo lectura",
"hide-labels": "Esconder etiquetas", "hide-labels": "Esconder etiquetas",
@@ -466,12 +504,13 @@
"add-recipients": "Agregar destinatarios", "add-recipients": "Agregar destinatarios",
"loading-mssg": "Esto podría llevar algún tiempo", "loading-mssg": "Esto podría llevar algún tiempo",
"send-mail": "Enviar correo", "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-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-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-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!", "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": "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-5": "¿Quieres firmar documentos ahora mismo?",
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!", "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!", "placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
@@ -600,7 +639,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.", "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-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.", "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-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.", "opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
@@ -610,34 +650,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-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.", "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-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-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": "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-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-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-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.", "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-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-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-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 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-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.", "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.", "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.", "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'.", "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ú 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.", "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": "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.", "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.", "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-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-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": "Arrastra o haz clic en un capo para agregarlo al documento.", "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.",
"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.",
"webhook-1": "Mejora ahora para configurar un webhook", "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.", "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.", "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.", "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.", "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.", "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" "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 +766,7 @@
}, },
"form-title-1": "Configuración del flujo de documentos", "form-title-1": "Configuración del flujo de documentos",
"form-title-2": "Configuración de seguridad", "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.", "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", "allowed-signature-types": "Tipos de firma permitidos",
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.", "at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
@@ -776,7 +814,7 @@
"agree-p1": "Confirmo que he leído y comprendido el ", "agree-p1": "Confirmo que he leído y comprendido el ",
"agree-p2": "Registro Electrónico y Divulgación de Firma", "agree-p2": "Registro Electrónico y Divulgación de Firma",
"agree-p3": "y consentimiento para utilizar registros y firmas electrónicas.", "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-title": "Términos y condiciones",
"term-cond-h": "DIVULGACIÓN DE REGISTRO ELECTRÓNICO Y FIRMA", "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.", "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 +917,10 @@
"thanks-for-feedback": "Gracias por su comentario 🙏", "thanks-for-feedback": "Gracias por su comentario 🙏",
"share-your-feedback": "Comparta sus comentarios", "share-your-feedback": "Comparta sus comentarios",
"share-your-review": "Comparta su reseña", "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", "date-format": "Formato de fecha",
"document-deleted": "El documento ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.", "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?", "save-as-template-?": "¿Está seguro de que desea guardar este documento como plantilla?",
@@ -1032,14 +1074,188 @@
"not-calculated": "No calculado", "not-calculated": "No calculado",
"not-found-in-signature": "No encontrado en la firma", "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.", "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-templates": "Buscar plantillas…",
"search-documents": "Buscar documentos…", "search-documents": "Buscar documentos…",
"search-contacts": "Buscar contactos…", "search-contacts": "Buscar contactos…",
"invalid-email-found": "Correo electrónico no válido encontrado: {{email}}", "invalid-email-found": "Correo electrónico no válido encontrado: {{email}}",
"duplicate-email-found": "Correo electrónico duplicado encontrado: {{email}}", "duplicate-email-found": "Correo electrónico duplicado encontrado: {{email}}",
"vertical": "Vertical", "vertical": "Vertical",
"add-role-alert": "Por favor, agregue al menos un rol",
"edit-draft": "Editar borrador",
"horizontal": "Horizontal", "horizontal": "Horizontal",
"billing": "Facturación", "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"
}
+253 -38
View File
@@ -8,7 +8,7 @@
"dark-mode": "Mode sombre", "dark-mode": "Mode sombre",
"name": "Nom et Prénom", "name": "Nom et Prénom",
"phone": "Téléphone", "phone": "Téléphone",
"phone-optional": "(facultatif)", "phone-optional": "Optionnel",
"email": "E-mail", "email": "E-mail",
"company": "Organisation", "company": "Organisation",
"job-title": "Votre Fonction", "job-title": "Votre Fonction",
@@ -26,6 +26,8 @@
"Name": "Nom et Prénom", "Name": "Nom et Prénom",
"Date": "Date" "Date": "Date"
}, },
"folder": "Dossier",
"pdf": "Pdf",
"context-menu": { "context-menu": {
"Download": "Télécharger", "Download": "Télécharger",
"Rename": "Renommer", "Rename": "Renommer",
@@ -43,13 +45,17 @@
"upgrade-now": "Mettre à jour maintenant", "upgrade-now": "Mettre à jour maintenant",
"contact-now": "Contacter maintenant", "contact-now": "Contacter maintenant",
"upgrade-to": "Mettre à niveau vers", "upgrade-to": "Mettre à niveau vers",
"pro": "PRO",
"plan": "Offre", "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.", "subscription-renew-warning": "Votre abonnement expirera dans {{remainingDays}} jours. Veuillez renouveler votre abonnement.",
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !", "subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.", "subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner", "user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
"tour-content": "Ne plus afficher", "tour-content": "Ne plus afficher",
"pro": "PRO",
"team": "Équipe", "team": "Équipe",
"docs": "Documents", "docs": "Documents",
"session-expired": "Votre session a expiré.", "session-expired": "Votre session a expiré.",
@@ -58,7 +64,7 @@
"welcome": "Content de te revoir!", "welcome": "Content de te revoir!",
"Login-to-your-account": "Se connecter à son compte", "Login-to-your-account": "Se connecter à son compte",
"password": "Mot de passe", "password": "Mot de passe",
"forgot-password": "Mot de passe oublié?", "forgot-password": "Mot de passe oublié",
"loading": "Chargement...", "loading": "Chargement...",
"of": "de", "of": "de",
"sign-SSO": "Connectez-vous avec SSO", "sign-SSO": "Connectez-vous avec SSO",
@@ -155,10 +161,24 @@
"created-date": "Date de création", "created-date": "Date de création",
"Type": "Saisir", "Type": "Saisir",
"Logs": "Journaux", "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": { "btnLabel": {
"sign": "Signer", "sign": "Signer",
"Sign": "SIGNER",
"Resend": "Relancer", "Resend": "Relancer",
"Rename": "Renommer", "Rename": "Renommer",
"Revoke": "Révoquer", "Revoke": "Révoquer",
@@ -180,7 +200,8 @@
"daily-mail-quota": "Quota d'e-mails quotidien", "daily-mail-quota": "Quota d'e-mails quotidien",
"Save as template": "Enregistrer comme modèle", "Save as template": "Enregistrer comme modèle",
"Fix & resend": "Corriger et renvoyer", "Fix & resend": "Corriger et renvoyer",
"Kiosk Mode": "Mode Kiosque" "Kiosk Mode": "Mode Kiosque",
"Reset password": "Réinitialiser le mot de passe"
}, },
"report-help": { "report-help": {
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.", "Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
@@ -192,16 +213,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.", "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." "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",
"Sign Yourself": "signez vous-même", "Request Signatures": "Demander des signatures",
"Request Signatures": "Demander des signatures", "New Template": "Nouveau modèle",
"New Template": "Nouveau modèle"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "fichier sélectionné", "file-selected": "fichier sélectionné",
"template-title": "Titre du modèle", "template-title": "Titre du modèle",
"document-title": "Titre du document", "document-title": "Titre du document",
"title": "Titre",
"description": "Description", "description": "Description",
"time-to-complete": "Temps de réalisation (jours)", "time-to-complete": "Temps de réalisation (jours)",
"send-in-order": "Envoyer dans l'ordre ?", "send-in-order": "Envoyer dans l'ordre ?",
@@ -241,7 +261,6 @@
"submit": "Soumettre", "submit": "Soumettre",
"reset": "Réinitialiser", "reset": "Réinitialiser",
"my-signature": "Ma signature", "my-signature": "Ma signature",
"initial-alert": "Mon initiale introuvable",
"signature": "Signature", "signature": "Signature",
"upload-image": "Télécharger une image", "upload-image": "Télécharger une image",
"clear": "Vider", "clear": "Vider",
@@ -280,7 +299,7 @@
"deactivate": "désactiver", "deactivate": "désactiver",
"this-user": "cet utilisateur ", "this-user": "cet utilisateur ",
"delete-user": "Supprimer l'utilisateur", "delete-user": "Supprimer l'utilisateur",
"delete": "supprimer", "delete": "Supprimer",
"add-user": "Ajouter un utilisateur", "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.", "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", "Team status": "Statut de l'équipe",
@@ -367,7 +386,8 @@
"dropdown": "dérouler", "dropdown": "dérouler",
"radio button": "bouton radio", "radio button": "bouton radio",
"image": "image", "image": "image",
"email": "e-mail" "email": "e-mail",
"number": "nombre"
}, },
"fields": "Des champs", "fields": "Des champs",
"recipients": "Destinataires", "recipients": "Destinataires",
@@ -379,6 +399,7 @@
"certificate": "Certificat", "certificate": "Certificat",
"decline": "refusé", "decline": "refusé",
"finish": "terminé", "finish": "terminé",
"done": "Terminé",
"mail": "Mail", "mail": "Mail",
"sign-now": "Signez maintenant", "sign-now": "Signez maintenant",
"successfully-signed": "Signé avec succès !", "successfully-signed": "Signé avec succès !",
@@ -388,6 +409,10 @@
"Email-verified-alert-1": "L'e-mail est vérifié.", "Email-verified-alert-1": "L'e-mail est vérifié.",
"Email-verified-alert-2": "L'e-mail est déjà vérifié.", "Email-verified-alert-2": "L'e-mail est déjà vérifié.",
"upload-stamp-image": "Télécharger l'image du tampon", "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", "draw": "Dessiner",
"type": "Taper", "type": "Taper",
"color-type": { "color-type": {
@@ -400,10 +425,10 @@
"upload": "Télécharger", "upload": "Télécharger",
"initial-teb": "Initiales", "initial-teb": "Initiales",
"signature-tab": "Signature", "signature-tab": "Signature",
"your-signature": "Votre signature", "initial-alert": "Mon initiale est introuvable",
"copy-title": "Copier le widget vers", "copy-title": "Copier le widget vers",
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?", "contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
"reset-password": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie", "reset-password-alert-1": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie", "reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
"reset-password-alert-3": "Réinitialisez votre mot de passe", "reset-password-alert-3": "Réinitialisez votre mot de passe",
"faild-animation": "Échec du chargement de l'animation", "faild-animation": "Échec du chargement de l'animation",
@@ -419,6 +444,19 @@
"maximum-check": "Contrôle maximum", "maximum-check": "Contrôle maximum",
"cell-count": "numération cellulaire", "cell-count": "numération cellulaire",
"default-value": "Valeur par défaut", "default-value": "Valeur par défaut",
"formula": "Formule",
"formula-placeholder": "Créez une formule à l'aide des widgets numériques",
"formula-no-widgets": "Aucun autre widget numérique n'est encore attribué à ce signataire.",
"supported-operators": "Opérateurs pris en charge :",
"formula-helper": "Cliquez sur une puce de widget numérique pour l'insérer (ex. {{number-1}} + {{number-2}}).",
"formula-error-letter": "Encadrez les noms de widget avec des doubles accolades (ex. {{number-1}}).",
"formula-error-characters": "En dehors des accolades, seuls les chiffres et +, -, *, /, (, ) sont autorisés.",
"formula-error-braces": "Chaque {{ ouvrante doit avoir une }} correspondante.",
"formula-error-operators": "Lorsque vous utilisez des opérateurs ou des parenthèses, ajoutez au moins deux valeurs.",
"formula-error-leading-operator": "Les formules ne peuvent pas commencer par des opérateurs.",
"decimal-places": "Décimales",
"number-of-decimal-places": "Nombre de décimales",
"decimal-places-helper": "Définir sur 0 pour les nombres entiers",
"select": "Sélectionner", "select": "Sélectionner",
"read-only": "Est en lecture seule", "read-only": "Est en lecture seule",
"hide-labels": "Masquer les étiquettes", "hide-labels": "Masquer les étiquettes",
@@ -465,13 +503,13 @@
"add-recipients": "Ajouter des destinataires", "add-recipients": "Ajouter des destinataires",
"loading-mssg": "Cela pourrait prendre du temps", "loading-mssg": "Cela pourrait prendre du temps",
"send-mail": "Envoyer un mail", "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 navez 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-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-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-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-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": "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 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-5": "Voulez-vous signer des documents maintenant ?",
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !", "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é !", "placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
@@ -600,7 +638,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.", "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-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.", "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-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.", "opensign-drive-2": "Cliquez sur le bouton Ajouter pour créer un nouveau dossier ou document.",
@@ -610,34 +649,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-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.", "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-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-2": "Cliquez sur l'un des champs figurant dans le document pour commencer. Vous pourrez ensuite remplir les informations requises.",
"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-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-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-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.", "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-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-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-4": "Faites glisser ou cliquez sur un widget 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-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.", "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", "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.", "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'.", "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 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 ", "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 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.", "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.", "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-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-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": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.", "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.",
"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.",
"webhook-1": "Mettez à niveau maintenant pour définir le webhook", "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.", "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.", "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.", "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.", "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.", "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." "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 +733,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-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-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": "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", "quota-mail-head": "Quota atteint",
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.", "unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
"sent-this-month": "envoyé ce mois-ci", "sent-this-month": "envoyé ce mois-ci",
@@ -728,7 +765,7 @@
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.", "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. ", "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}}", "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.", "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", "allowed-signature-types": "Types de signature autorisés",
"at-least-one-signature-type": "Au moins un type de signature doit être activé.", "at-least-one-signature-type": "Au moins un type de signature doit être activé.",
@@ -776,7 +813,7 @@
"agree-p1": "Je confirme avoir lu et compris les ", "agree-p1": "Je confirme avoir lu et compris les ",
"agree-p2": "Divulgation des enregistrements électroniques et des signatures", "agree-p2": "Divulgation des enregistrements électroniques et des signatures",
"agree-p3": "et consentez à l'utilisation d'enregistrements et de signatures électroniques.", "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-title": "Termes et conditions",
"term-cond-h": "DIVULGATION D'ENREGISTREMENT ÉLECTRONIQUE ET DE SIGNATURE", "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.", "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 +916,10 @@
"thanks-for-feedback": "Merci pour votre retour 🙏", "thanks-for-feedback": "Merci pour votre retour 🙏",
"share-your-feedback": "Partagez votre avis", "share-your-feedback": "Partagez votre avis",
"share-your-review": "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", "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.", "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 ?", "save-as-template-?": "Êtes-vous sûr de vouloir enregistrer ce document comme modèle ?",
@@ -988,7 +1029,7 @@
"finish-mssg": "Êtes-vous sûr de vouloir terminer le document ?", "finish-mssg": "Êtes-vous sûr de vouloir terminer le document ?",
"review": "Revoir", "review": "Revoir",
"next-field": "Champ suivant", "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": "Vérifier le document",
"verify-document-signature": "Vérifier la signature du document", "verify-document-signature": "Vérifier la signature du document",
"select-pdf-document": "Sélectionner le document PDF", "select-pdf-document": "Sélectionner le document PDF",
@@ -1032,14 +1073,188 @@
"not-calculated": "Non calculé", "not-calculated": "Non calculé",
"not-found-in-signature": "Introuvable dans la signature", "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.", "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-templates": "Rechercher des modèles…",
"search-documents": "Rechercher des documents…", "search-documents": "Rechercher des documents…",
"search-contacts": "Rechercher des contacts…", "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}}", "invalid-email-found": "Adresse e-mail invalide trouvée : {{email}}",
"duplicate-email-found": "Adresse e-mail en double trouvée : {{email}}", "duplicate-email-found": "Adresse e-mail en double trouvée : {{email}}",
"vertical": "Vertical", "vertical": "Vertical",
"horizontal": "Horizontal", "horizontal": "Horizontal",
"billing": "Facturation", "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 lune 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 dutiliser 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 dachè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 dempêcher les spammeurs dabuser 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"
}
+245 -30
View File
@@ -26,6 +26,8 @@
"Name": "नाम", "Name": "नाम",
"Date": "दिनांक" "Date": "दिनांक"
}, },
"folder": "फ़ोल्डर",
"pdf": "पीडीएफ़",
"context-menu": { "context-menu": {
"Download": "डाउनलोड करें", "Download": "डाउनलोड करें",
"Rename": "नाम बदलें", "Rename": "नाम बदलें",
@@ -44,6 +46,10 @@
"contact-now": "अभी संपर्क करें", "contact-now": "अभी संपर्क करें",
"upgrade-to": "इसमें अपग्रेड करें", "upgrade-to": "इसमें अपग्रेड करें",
"plan": "योजना", "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}} दिनों में समाप्त हो जाएगी। कृपया अपनी सदस्यता नवीनीकृत करें।", "subscription-renew-warning": "आपकी सदस्यता {{remainingDays}} दिनों में समाप्त हो जाएगी। कृपया अपनी सदस्यता नवीनीकृत करें।",
"subscribe-card-teamplan": "सहयोग की पूरी शक्ति को अनलॉक करें! असीमित संगठन, टीम और पदानुक्रम बनाएं। टीमों में टेम्पलेट को निर्बाध रूप से साझा करें और कस्टम उपयोगकर्ता भूमिकाएँ असाइन करें। आज ही अपने वर्कफ़्लो को उन्नत करें!", "subscribe-card-teamplan": "सहयोग की पूरी शक्ति को अनलॉक करें! असीमित संगठन, टीम और पदानुक्रम बनाएं। टीमों में टेम्पलेट को निर्बाध रूप से साझा करें और कस्टम उपयोगकर्ता भूमिकाएँ असाइन करें। आज ही अपने वर्कफ़्लो को उन्नत करें!",
"subscribe-card-plan": "केवल {{premiumPrice}}/माह से शुरू होने वाली प्रीमियम सुविधाओं को अनलॉक करें। अपने शामिल प्रीमियम क्रेडिट के बाद प्रति अतिरिक्त क्रेडिट केवल {{addonPrice}} पर बढ़ी हुई प्रदर्शन का आनंद लें।", "subscribe-card-plan": "केवल {{premiumPrice}}/माह से शुरू होने वाली प्रीमियम सुविधाओं को अनलॉक करें। अपने शामिल प्रीमियम क्रेडिट के बाद प्रति अतिरिक्त क्रेडिट केवल {{addonPrice}} पर बढ़ी हुई प्रदर्शन का आनंद लें।",
@@ -58,7 +64,7 @@
"welcome": "वापसी पर स्वागत है!", "welcome": "वापसी पर स्वागत है!",
"Login-to-your-account": "अपने खाते में प्रवेश करे", "Login-to-your-account": "अपने खाते में प्रवेश करे",
"password": "पासवर्ड", "password": "पासवर्ड",
"forgot-password": "पासवर्ड भूल गए?", "forgot-password": "पासवर्ड भूल गए",
"loading": "लोड हो रहा है...", "loading": "लोड हो रहा है...",
"of": "का", "of": "का",
"sign-SSO": "SSO के साथ साइन इन करें", "sign-SSO": "SSO के साथ साइन इन करें",
@@ -159,7 +165,8 @@
"daily-mail-quota": "दैनिक ईमेल कोटा", "daily-mail-quota": "दैनिक ईमेल कोटा",
"Save as template": "टेम्पलेट के रूप में सहेजें", "Save as template": "टेम्पलेट के रूप में सहेजें",
"Fix & resend": "ठीक करें और पुनः भेजें", "Fix & resend": "ठीक करें और पुनः भेजें",
"Kiosk Mode": "कियोस्क मोड" "Kiosk Mode": "कियोस्क मोड",
"Reset password": "पासवर्ड रीसेट करें"
}, },
"report-heading": { "report-heading": {
"Sr.No": "क्रमांक", "Sr.No": "क्रमांक",
@@ -181,7 +188,20 @@
"created-date": "निर्माण तिथि", "created-date": "निर्माण तिथि",
"Type": "प्रकार", "Type": "प्रकार",
"Logs": "लॉग", "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": { "report-help": {
"Draft Documents": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।", "Draft Documents": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
@@ -193,16 +213,15 @@
"Contactbook": "यह आपके द्वारा जोड़े गए संपर्कों/हस्ताक्षरकर्ताओं की सूची है। जब आप किसी नए दस्तावेज़ में हस्ताक्षरकर्ताओं को जोड़ने का प्रयास करेंगे तो ये सुझावों के रूप में दिखाई देंगे।", "Contactbook": "यह आपके द्वारा जोड़े गए संपर्कों/हस्ताक्षरकर्ताओं की सूची है। जब आप किसी नए दस्तावेज़ में हस्ताक्षरकर्ताओं को जोड़ने का प्रयास करेंगे तो ये सुझावों के रूप में दिखाई देंगे।",
"Templates": "यह उन टेम्पलेट्स की सूची है जो दस्तावेज़ बनाने के लिए आपके लिए उपलब्ध हैं। आप टेम्पलेट का उपयोग करके एक नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक कर सकते हैं, दस्तावेज़ को संशोधित कर सकते हैं और अगले चरण में हस्ताक्षरकर्ताओं को जोड़ सकते हैं।" "Templates": "यह उन टेम्पलेट्स की सूची है जो दस्तावेज़ बनाने के लिए आपके लिए उपलब्ध हैं। आप टेम्पलेट का उपयोग करके एक नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक कर सकते हैं, दस्तावेज़ को संशोधित कर सकते हैं और अगले चरण में हस्ताक्षरकर्ताओं को जोड़ सकते हैं।"
}, },
"form-name": { "Sign Yourself": "स्वयं हस्ताक्षर करें",
"Sign Yourself": "स्वयं हस्ताक्षर करें", "Request Signatures": "हस्ताक्षर का अनुरोध करें",
"Request Signatures": "हस्ताक्षर का अनुरोध करें", "New Template": "नया टेम्पलेट",
"New Template": "नया टेम्पलेट"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "फ़ाइल चयनित", "file-selected": "फ़ाइल चयनित",
"template-title": "टेम्पलेट शीर्षक", "template-title": "टेम्पलेट शीर्षक",
"document-title": "दस्तावेज़ शीर्षक", "document-title": "दस्तावेज़ शीर्षक",
"title": "शीर्षक",
"description": "विवरण", "description": "विवरण",
"time-to-complete": "पूरा करने का समय (दिन)", "time-to-complete": "पूरा करने का समय (दिन)",
"send-in-order": "क्रम से भेजें", "send-in-order": "क्रम से भेजें",
@@ -367,7 +386,8 @@
"dropdown": "ड्रॉपडाउन", "dropdown": "ड्रॉपडाउन",
"radio button": "रेडियो बटन", "radio button": "रेडियो बटन",
"image": "छवि", "image": "छवि",
"email": "ईमेल" "email": "ईमेल",
"number": "संख्या"
}, },
"fields": "क्षेत्र", "fields": "क्षेत्र",
"recipients": "प्राप्तकर्ता", "recipients": "प्राप्तकर्ता",
@@ -379,6 +399,7 @@
"certificate": "प्रमाण पत्र", "certificate": "प्रमाण पत्र",
"decline": "अस्वीकार करें", "decline": "अस्वीकार करें",
"finish": "समाप्त करें", "finish": "समाप्त करें",
"done": "हो गया",
"mail": "मेल", "mail": "मेल",
"sign-now": "अभी हस्ताक्षर करें", "sign-now": "अभी हस्ताक्षर करें",
"successfully-signed": "सफलतापूर्वक हस्ताक्षर किए गए!", "successfully-signed": "सफलतापूर्वक हस्ताक्षर किए गए!",
@@ -388,6 +409,10 @@
"Email-verified-alert-1": "ईमेल सत्यापित है।", "Email-verified-alert-1": "ईमेल सत्यापित है।",
"Email-verified-alert-2": "ईमेल पहले से ही सत्यापित है।", "Email-verified-alert-2": "ईमेल पहले से ही सत्यापित है।",
"upload-stamp-image": "मोहर छवि अपलोड करें", "upload-stamp-image": "मोहर छवि अपलोड करें",
"draw-signature": "हस्ताक्षर बनाएं",
"draw-initials": "इनिशियल्स बनाएं",
"enter-text": "पाठ दर्ज करें",
"enter-widgettype": "{{widgetType}} दर्ज करें",
"draw": "आरेखित करें", "draw": "आरेखित करें",
"type": "टाइप", "type": "टाइप",
"color-type": { "color-type": {
@@ -419,6 +444,19 @@
"maximum-check": "अधिकतम जाँच", "maximum-check": "अधिकतम जाँच",
"cell-count": "सेल्स संख्या", "cell-count": "सेल्स संख्या",
"default-value": "डिफ़ॉल्ट मान", "default-value": "डिफ़ॉल्ट मान",
"formula": "सूत्र",
"formula-placeholder": "संख्या विजेट्स का उपयोग करके एक सूत्र बनाएं",
"formula-no-widgets": "इस हस्ताक्षरकर्ता को अभी तक कोई अन्य संख्या विजेट असाइन नहीं किया गया है।",
"supported-operators": "समर्थित ऑपरेटर:",
"formula-helper": "सूची में से संख्या विजेट चिप पर क्लिक करके उसे जोड़ें (उदा. {{number-1}} + {{number-2}})।",
"formula-error-letter": "विजेट नामों को दोहरी घुंघराली ब्रैकेट में रखें (उदा. {{number-1}})।",
"formula-error-characters": "ब्रैकेट के बाहर केवल संख्याएँ और +, -, *, /, (, ) की अनुमति है।",
"formula-error-braces": "हर खुलने वाली {{ के लिए एक मिलान करने वाली }} होना चाहिए।",
"formula-error-operators": "ऑपरेटर या कोष्ठक का उपयोग करते समय कम से कम दो मान जोड़ें।",
"formula-error-leading-operator": "सूत्र ऑपरेटर से शुरू नहीं हो सकते।",
"decimal-places": "दशमलव स्थान",
"number-of-decimal-places": "दशमलव स्थानों की संख्या",
"decimal-places-helper": "पूर्णांक के लिए 0 पर सेट करें",
"select": "चुनें", "select": "चुनें",
"read-only": "सिर्फ पढ़ने के लिए है", "read-only": "सिर्फ पढ़ने के लिए है",
"hide-labels": "लेबल छिपाएँ", "hide-labels": "लेबल छिपाएँ",
@@ -465,13 +503,13 @@
"add-recipients": "प्राप्तकर्ता जोड़ें", "add-recipients": "प्राप्तकर्ता जोड़ें",
"loading-mssg": "इसमें कुछ समय लग सकता है", "loading-mssg": "इसमें कुछ समय लग सकता है",
"send-mail": "मेल भेजें", "send-mail": "मेल भेजें",
"signature-field-widget": "प्रत्येक उपयोगकर्ता के लिए कम से कम एक हस्ताक्षर फ़ील्ड जोड़ा जाना चाहिए। आपने {{signersName}} के लिए हस्ताक्षर फ़ील्ड नहीं जोड़ है", "signature-field-widget": "प्रत्येक उपयोगकर्ता के पास कम से कम एक हस्ताक्षर विजेट होना चाहिए। आपने {{signersName}} के लिए हस्ताक्षर विजेट नहीं जोड़ है",
"placeholder-alert-1": "कृपया सुनिश्चित करें कि सभी प्राप्तकर्ताओं के लिए कम से कम एक हस्ताक्षर विजेट जोड़ा गया है।", "placeholder-alert-1": "कृपया सुनिश्चित करें कि सभी प्राप्तकर्ताओं के लिए कम से कम एक हस्ताक्षर विजेट जोड़ा गया है।",
"placeholder-alert-2": "कृपया पुष्टि करें कि आपने टेक्स्ट फ़ील्ड भर दिया है।", "placeholder-alert-2": "कृपया पुष्टि करें कि आपने टेक्स्ट फ़ील्ड भर दिया है।",
"placeholder-alert-3": " क्या आप वाकई इस दस्तावेज़ को हस्ताक्षर के लिए भेजना चाहते हैं?", "placeholder-alert-3": " क्या आप वाकई इस दस्तावेज़ को हस्ताक्षर के लिए भेजना चाहते हैं?",
"placeholder-alert-4": "आपने सभी प्राप्तकर्ताओं को सफलतापूर्वक मेल भेज दिए हैं!", "placeholder-alert-4": "आपने सभी प्राप्तकर्ताओं को सफलतापूर्वक मेल भेज दिए हैं!",
"placeholder-mail-alert": "आपने {{name}} को सफलतापूर्वक ईमेल भेज दिया है। {{name}} द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे", "placeholder-mail-alert": "आपने {{name}} को सफलतापूर्वक ईमेल भेज दिया है। {{name}} द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे",
"placeholder-mail-alert-you": "आपके द्वारा दस्तावेज़ पर हस्ताक्षर करने के बाद बाद के हस्ताक्षरकर्ताओं को ईमेल प्राप्त होंगे।", "placeholder-mail-alert-you": "आपके दस्तावेज़ पर हस्ताक्षर करने के बाद अगले हस्ताक्षरकर्ता को ईमेल प्राप्त होगा।",
"placeholder-alert-5": "क्या आप अभी दस्तावेज़ पर हस्ताक्षर करना चाहते हैं?", "placeholder-alert-5": "क्या आप अभी दस्तावेज़ पर हस्ताक्षर करना चाहते हैं?",
"placeholder-alert-6": "मेल भेजने के लिए कृपया मेल एडॉप्टर सेटअप करें!", "placeholder-alert-6": "मेल भेजने के लिए कृपया मेल एडॉप्टर सेटअप करें!",
"placeholder-alert-7": "प्लेसहोल्डर जोड़ने के लिए कृपया हस्ताक्षरकर्ता चुनें!", "placeholder-alert-7": "प्लेसहोल्डर जोड़ने के लिए कृपया हस्ताक्षरकर्ता चुनें!",
@@ -600,7 +638,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "आप सफलतापूर्वक लॉग इन हो गए हैं! आइए एक नज़र डालते हैं।", "home-layout-1": "आप सफलतापूर्वक लॉग इन हो गए हैं! आइए एक नज़र डालते हैं।",
"home-layout-2": "स्व-हस्ताक्षर के लिए दस्तावेज़ अपलोड करने या दूसरों के हस्ताक्षर का अनुरोध करने के लिए, बस संबंधित बटन चुनें।", "home-layout-2": "स्व-हस्ताक्षर के लिए दस्तावेज़ अपलोड करने या दूसरों के हस्ताक्षर का अनुरोध करने के लिए, बस संबंधित बटन चुनें।",
"home-layout-3": "आप {{appName}} का उपयोग शुरू करने के लिए तैयार हैं! यदि आपको सहायता की आवश्यकता है तो हमसे संपर्क करने में संकोच न करें।", "home-layout-3": "आप {{appName}} का उपयोग शुरू करने के लिए तैयार हैं!",
"home-layout-4": "हमें स्टार दें",
"generate-token": "उत्पादन एपीआई टोकन उत्पन्न करने के लिए अभी अपग्रेड करें।", "generate-token": "उत्पादन एपीआई टोकन उत्पन्न करने के लिए अभी अपग्रेड करें।",
"opensign-drive-1": "फ़ोल्डर पदानुक्रम के माध्यम से आसानी से नेविगेट करने और प्रत्येक फ़ोल्डर के भीतर दस्तावेज़ देखने के लिए ब्रेडक्रंब लिंक पर क्लिक करें।", "opensign-drive-1": "फ़ोल्डर पदानुक्रम के माध्यम से आसानी से नेविगेट करने और प्रत्येक फ़ोल्डर के भीतर दस्तावेज़ देखने के लिए ब्रेडक्रंब लिंक पर क्लिक करें।",
"opensign-drive-2": "नया फ़ोल्डर या दस्तावेज़ बनाने के लिए जोड़ें बटन पर क्लिक करें।", "opensign-drive-2": "नया फ़ोल्डर या दस्तावेज़ बनाने के लिए जोड़ें बटन पर क्लिक करें।",
@@ -610,34 +649,32 @@
"opensign-drive-6": "डाउनलोड, नाम बदलें, स्थानांतरित करें और हटाएं जैसे विकल्प देखने के लिए किसी दस्तावेज़ पर राइट-क्लिक करें। इसे खोलने के लिए दस्तावेज़ पर क्लिक करें।", "opensign-drive-6": "डाउनलोड, नाम बदलें, स्थानांतरित करें और हटाएं जैसे विकल्प देखने के लिए किसी दस्तावेज़ पर राइट-क्लिक करें। इसे खोलने के लिए दस्तावेज़ पर क्लिक करें।",
"opensign-drive-7": "विकल्प देखने के लिए किसी भी फ़ोल्डर पर राइट-क्लिक करें। फ़ोल्डर का नाम बदलने के लिए 'नाम बदलें' चुनें या इसकी सामग्री के माध्यम से नेविगेट करने के लिए फ़ोल्डर पर क्लिक करें।", "opensign-drive-7": "विकल्प देखने के लिए किसी भी फ़ोल्डर पर राइट-क्लिक करें। फ़ोल्डर का नाम बदलने के लिए 'नाम बदलें' चुनें या इसकी सामग्री के माध्यम से नेविगेट करने के लिए फ़ोल्डर पर क्लिक करें।",
"pdf-request-file-1": "उन हस्ताक्षरकर्ताओं की सूची जिन्हें अभी भी दस्तावेज़ पर हस्ताक्षर करने की आवश्यकता है।", "pdf-request-file-1": "उन हस्ताक्षरकर्ताओं की सूची जिन्हें अभी भी दस्तावेज़ पर हस्ताक्षर करने की आवश्यकता है।",
"pdf-request-file-2": "हस्ताक्षर करने के लिए दस्तावेज़ पर दिखाई देने वाले किसी भी प्लेसहोल्ड पर क्लिक करें। फिर आपको अपना हस्ताक्षर खींचने, टाइप करने या एक छवि अपलोड करने के विकल्प दिखाई देंगे।", "pdf-request-file-2": "शुरू करने के लिए दस्तावेज़ में दिखाई देने वाले किसी भी फ़ील्ड पर क्लिक करें। उसके बाद आप आवश्यक जानकारी भर सकेंगे।",
"pdf-request-file-3": "अपने दस्तावेज़ को नेविगेट करने के लिए अस्वीकार करें, या समाप्त करें बटन पर क्लिक करें। डाउनलोड बटन सहित अतिरिक्त विकल्पों के लिए दीर्घवृत्त मेनू का उपयोग करें।", "pdf-request-file-3": "एक बार जब आप सभी आवश्यक फ़ील्ड भर लें, तो 'समाप्त करें' पर क्लिक करें। उसके बाद आप हस्ताक्षरित दस्तावेज़ डाउनलोड कर सकेंगे। प्रेषक की सेटिंग्स के आधार पर, जब सभी प्राप्तकर्ता हस्ताक्षर कर लेंगे, तो आपको पूर्ण दस्तावेज़ की एक प्रति और पूर्णता प्रमाणपत्र प्राप्त हो सकता है।",
"pdf-request-file-4": "उन हस्ताक्षरकर्ताओं की सूची जिन्होंने पहले ही दस्तावेज़ पर हस्ताक्षर कर दिए हैं।", "pdf-request-file-4": "उन हस्ताक्षरकर्ताओं की सूची जिन्होंने पहले ही दस्तावेज़ पर हस्ताक्षर कर दिए हैं।",
"pdf-request-file-5": "आप उन सभी स्थानों पर स्वचालित रूप से हस्ताक्षर करने के लिए 'सभी पर स्वतः हस्ताक्षर करें' पर क्लिक कर सकते हैं, जिन पर आपके द्वारा हस्ताक्षर किए जाने हैं। सुनिश्चित करें कि आप इस बटन पर क्लिक करने से पहले दस्तावेज़ की ठीक से समीक्षा कर लें।", "pdf-request-file-5": "आप उन सभी स्थानों पर स्वचालित रूप से हस्ताक्षर करने के लिए 'सभी पर स्वतः हस्ताक्षर करें' पर क्लिक कर सकते हैं, जिन पर आपके द्वारा हस्ताक्षर किए जाने हैं। सुनिश्चित करें कि आप इस बटन पर क्लिक करने से पहले दस्तावेज़ की ठीक से समीक्षा कर लें।",
"pdf-request-file-6": "कृपया पृष्ठ संख्या {{pagenumbers}} पर फ़ील्ड पूरी करें, सभी आसान पहचान के लिए एक ही रंग में हाइलाइट किए गए हैं।", "pdf-request-file-6": "कृपया पृष्ठ संख्या {{pagenumbers}} पर फ़ील्ड पूरी करें, सभी आसान पहचान के लिए एक ही रंग में हाइलाइट किए गए हैं।",
"placeholder-sign-1": "एक प्लेस-होल्डर जोड़ने के लिए इस सूची से एक प्राप्तकर्ता का चयन करें जहां उसे हस्ताक्षर करना है। प्लेसहोल्डर दस्तावेज़ पर छोड़ने के बाद प्राप्तकर्ता के नाम के समान रंग में दिखाई देगा।", "placeholder-sign-1": "इस सूची से एक प्राप्तकर्ता चुनें ताकि विजेट जोड़ सकें। दस्तावेज़ पर रखने के बाद विजेट उसी रंग में दिखाई देगा जो प्राप्तकर्ता के नाम का है।",
"placeholder-sign-2": "'प्राप्तकर्ता जोड़ें' बटन पर क्लिक करने से आप अधिक हस्ताक्षरकर्ताओं को जोड़ सकेंगे।", "placeholder-sign-2": "'प्राप्तकर्ता जोड़ें' बटन पर क्लिक करने से आप अधिक हस्ताक्षरकर्ताओं को जोड़ सकेंगे।",
"placeholder-sign-3": "दस्तावेज़ में अधिक प्राप्तकर्ताओं/हस्ताक्षरकर्ताओं को जोड़ने के लिए इस बटन पर क्लिक करें।", "placeholder-sign-3": "दस्तावेज़ में अधिक प्राप्तकर्ताओं/हस्ताक्षरकर्ताओं को जोड़ने के लिए इस बटन पर क्लिक करें।",
"placeholder-sign-4": "इसे दस्तावेज़ में जोड़ने के लिए किसी फ़ील्ड पर खींचें या क्लिक करें।", "placeholder-sign-4": "दस्तावेज़ में जोड़ने के लिए विजेट को खींचें या उस पर क्लिक करें।",
"placeholder-sign-5": "पीडीएफ सामग्री क्षेत्र पहले से ही टेम्पलेट के मौजूदा प्लेसहोल्डर प्रदर्शित करता है। आपकी सुविधा के लिए, ये प्लेसहोल्डर प्राप्तकर्ता के नाम के रंग से मेल खाएगे, जिससे उन्हें आसानी से पहचान जा सकेगा।", "placeholder-sign-5": "PDF सामग्री क्षेत्र पहले से ही टेम्पलेट के मौजूदा विजेट दिखाता है। आपकी सुविधा के लिए, ये विजेट प्राप्तकर्ता के नाम के रंग से मेल खाएगे ताकि वे आसानी से पहचान जा सके।",
"placeholder-sign-6": "'अगला' पर क्लिक करने से दस्तावेज़ सहेजा जाएगा। अगले चरण में आप प्राप्तकर्ताओं को भेजे जाने वाले ईमेल को अनुकूलित कर सकते हैं या हस्ताक्षर लिंक कॉपी कर सकते हैं और उन्हें स्वयं प्राप्तकर्ताओं के साथ साझा कर सकते हैं।", "placeholder-sign-6": "'अगला' पर क्लिक करने से दस्तावेज़ सहेजा जाएगा। अगले चरण में आप प्राप्तकर्ताओं को भेजे जाने वाले ईमेल को अनुकूलित कर सकते हैं या हस्ताक्षर लिंक कॉपी कर सकते हैं और उन्हें स्वयं प्राप्तकर्ताओं के साथ साझा कर सकते हैं।",
"report-1": "नया टेम्पलेट बनाने के लिए 'जोड़ें' बटन पर क्लिक करें। टेम्पलेट पुन: प्रयोज्य दस्तावेज़ हैं जिन्हें समान संरचना और अलग-अलग हस्ताक्षरकर्ताओं के साथ नए दस्तावेज़ों को शीघ्रता से उत्पन्न करने के लिए डिज़ाइन किया गया है। उदाहरण के लिए, ऑनबोर्डिंग के लिए एक मानव संसाधन टेम्पलेट में 'मानव संसाधन प्रबंधक' और 'नया कर्मचारी' जैसी पूर्वनिर्धारित भूमिकाएँ हो सकती हैं। हर बार जब आप टेम्पलेट का उपयोग करते हैं, तो आप विभिन्न आने वाले कर्मचारियों को 'नया कर्मचारी' भूमिका सौंप सकते हैं, जबकि 'मानव संसाधन प्रबंधक' भूमिका स्थिर रहती है, जिससे प्रत्येक भर्ती के लिए एक सहज ऑनबोर्डिंग प्रक्रिया सुविधाजनक होती है। ", "report-1": "नया टेम्पलेट बनाने के लिए 'जोड़ें' बटन पर क्लिक करें। टेम्पलेट पुन: प्रयोज्य दस्तावेज़ हैं जिन्हें समान संरचना और अलग-अलग हस्ताक्षरकर्ताओं के साथ नए दस्तावेज़ों को शीघ्रता से उत्पन्न करने के लिए डिज़ाइन किया गया है। उदाहरण के लिए, ऑनबोर्डिंग के लिए एक मानव संसाधन टेम्पलेट में 'मानव संसाधन प्रबंधक' और 'नया कर्मचारी' जैसी पूर्वनिर्धारित भूमिकाएँ हो सकती हैं। हर बार जब आप टेम्पलेट का उपयोग करते हैं, तो आप विभिन्न आने वाले कर्मचारियों को 'नया कर्मचारी' भूमिका सौंप सकते हैं, जबकि 'मानव संसाधन प्रबंधक' भूमिका स्थिर रहती है, जिससे प्रत्येक भर्ती के लिए एक सहज ऑनबोर्डिंग प्रक्रिया सुविधाजनक होती है। ",
"redirect": "किसी मौजूदा टेम्पलेट से नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक करें।", "redirect": "किसी मौजूदा टेम्पलेट से नया दस्तावेज़ बनाने के लिए 'उपयोग करें' बटन पर क्लिक करें।",
"bulksend": "केवल प्राप्तकर्ता ईमेल पते बनाकर किसी मौजूदा टेम्पलेट का उपयोग करके कई दस्तावेज़ों को शीघ्रता से भेजने के लिए, 'थोक भेजें' बटन पर क्लिक करें।", "bulksend": "किसी मौजूदा टेम्पलेट का उपयोग करके कई दस्तावेज़ जल्दी भेजने के लिए, केवल प्राप्तकर्ताओं के ईमेल पते दर्ज करें और 'बल्क सेंड' बटन पर क्लिक करें। आप अधिकतम 50 प्राप्तकर्ताओं को भेज सकते हैं।",
"option": "यह मेनू संपादन और हटाएं जैसे अधिक विकल्प प्रकट करता है। हस्ताक्षरकर्ता भूमिकाएं जोड़ने, फ़ील्ड संशोधित करने और अपने टेम्पलेट को अपडेट करने के लिए 'संपादित करें' बटन का उपयोग करें। परिवर्तन इस टेम्पलेट से बनाए गए सभी भविष्य के दस्तावेज़ों पर लागू होंगे लेकिन मौजूदा दस्तावेज़ों को प्रभावित नहीं करेंगे। हटाएं बटन का उपयोग करके आप टेम्पलेट हटा सकते हैं। ", "option": "इस मेनू में और विकल्प दिखाई देंगे जैसे संपादित करें, हटाएं, नाम बदलें, डुप्लिकेट करें, साझा करें आदि। <1>यहां क्लिक करें</1> सभी उपलब्ध विकल्पों के बारे में और पढ़ने के लिए। <3>नोट: किसी मौजूदा टेम्पलेट में किए गए बदलाव उस टेम्पलेट से बनाए गए सभी भविष्य के दस्तावेज़ों पर लागू होंगे, लेकिन पहले से भेजे गए दस्तावेज़ों पर प्रभाव नहीं डालेंगे।</3>",
"signyour-self-1": "हस्ताक्षर करने से पहले अपने दस्तावेज़ को अनुकूलित करने के लिए अपन पसंदीदा विजेट चुनें और उन्हें पीडीएफ पर खींचें। अपनी आवश्यकताओं के अनुसार दस्तावेज़ को अनुकूलित करने के लिए प्रत्येक संशोधन के लिए सही स्थान चुनें।", "signyour-self-1": "हस्ताक्षर करने से पहले अपने दस्तावेज़ को अनुकूलित करने के लिए अपन पसंद के विजेट चुनें या उन्हें PDF पर खींचें। अपनी आवश्यकता के अनुसार प्रत्येक विजेट को सही स्थान पर रखें।",
"signyour-self-2": "इस क्षेत्र में कहीं भी खींचें और छोड़ें। आप इसे बाद में आकार बदल सकते हैं और स्थानांतरित कर सकते हैं।", "signyour-self-2": "इस क्षेत्र में कहीं भी खींचें और छोड़ें। आप इसे बाद में आकार बदल सकते हैं और स्थानांतरित कर सकते हैं।",
"template-placeholder-1": "'भूमिका जोड़ें' बटन पर क्लिक करने से आप विभिन्न हस्ताक्षरकर्ता भूमिकाएँ जोड़ सकेंगे। आप बाद के चरणों में प्रत्येक भूमिका में उपयोगकर्ताओं को संलग्न कर सकते हैं।", "template-placeholder-1": "'भूमिका जोड़ें' बटन पर क्लिक करने से आप विभिन्न हस्ताक्षरकर्ता भूमिकाएँ जोड़ सकेंगे। आप बाद के चरणों में प्रत्येक भूमिका में उपयोगकर्ताओं को संलग्न कर सकते हैं।",
"template-placeholder-2": "भूमिकाएँ जोड़ने के बाद, सूची से एक भूमिका चुनें ताकि एक प्लेस-होल्डर जोड़ा जा सके जहाँ उसे हस्ताक्षर करना है। प्लेसहोल्डर दस्तावेज़ पर छोड़ने के बाद भूमिका के नाम के समान रंग में दिखाई देगा।", "template-placeholder-2": "भूमिकाएँ जोड़ने के बाद, सूची में से एक चुनें ताकि उस प्राप्तकर्ता के लिए विजेट रखा जा सके। आप विजेट पर क्लिक कर सकते हैं या उसे दस्तावेज़ पर खींच सकते हैं। एक बार रखे जाने के बाद, विजेट चयनित भूमिका के समान रंग में दिखाई देगा।",
"template-placeholder-3": "इसे दस्तावेज़ में जोड़ने के लिए किसी फ़ील्ड पर खींचें या क्लिक करें।", "template-placeholder-3": "'अगला' पर क्लिक करने से वर्तमान टेम्पलेट संग्रहीत हो जाएगा। सहेजने के बाद, यदि आप चाहें तो इस टेम्पलेट से एक नया दस्तावेज़ बनाने के लिए आपको संकेत दिया जाएगा।",
"template-placeholder-4": "किसी भूमिका के लिए प्लेसहोल्डर को दस्तावेज़ पर कहीं भी खींचें। याद रखें, यह आसान संदर्भ के लिए प्राप्तकर्ता के नाम के समान रंग में दिखाई देगा।",
"template-placeholder-5": "'अगला' पर क्लिक करने से वर्तमान टेम्पलेट संग्रहीत हो जाएगा। सहेजने के बाद, यदि आप चाहें तो इस टेम्पलेट से एक नया दस्तावेज़ बनाने के लिए आपको संकेत दिया जाएगा।",
"webhook-1": "वेबहूक सेट करने के लिए अभी अपग्रेड करें", "webhook-1": "वेबहूक सेट करने के लिए अभी अपग्रेड करें",
"Need your Signature": "इस कार्ड पर क्लिक करने से आप अपनी समीक्षा की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।", "Need your Signature": "इस कार्ड पर क्लिक करने से आप अपनी समीक्षा की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।",
"Out for signatures": "इस कार्ड पर क्लिक करने से आप हस्ताक्षर की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।", "Out for signatures": "इस कार्ड पर क्लिक करने से आप हस्ताक्षर की प्रतीक्षा कर रहे दस्तावेज़ों की सूची पर पहुंच जाएंगे।",
"Recent signature requests": "यह उन दस्तावेज़ों की सूची है जो आपके हस्ताक्षर की प्रतीक्षा कर रहे हैं।", "Recent signature requests": "यह उन दस्तावेज़ों की सूची है जो आपके हस्ताक्षर की प्रतीक्षा कर रहे हैं।",
"Recently sent for signatures": "यह उन दस्तावेज़ों की सूची है जिन्हें आपने हस्ताक्षर के लिए अन्य पक्षों को भेजा है।", "Recently sent for signatures": "यह उन दस्तावेज़ों की सूची है जिन्हें आपने हस्ताक्षर के लिए अन्य पक्षों को भेजा है।",
"Drafts": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू तो किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।", "Drafts": "ये वे दस्तावेज़ हैं जिन्हें आपने शुरू किया है लेकिन भेजने के लिए अंतिम रूप नहीं दिया है।",
"public-template": "यह वीडियो दर्शाता है कि आप अपना व्यक्तिगत सार्वजनिक प्रोफ़ाइल कैसे सेट अप कर सकते हैं, जैसे 'https://opensign.me/your-username'। आप यह भी सीखेंगे कि अपनी टैगलाइन को कैसे अनुकूलित करें और अपने टेम्पलेट्स को सार्वजनिक हस्ताक्षर के लिए उपलब्ध कराएं।", "public-template": "यह वीडियो दर्शाता है कि आप अपना व्यक्तिगत सार्वजनिक प्रोफ़ाइल कैसे सेट अप कर सकते हैं, जैसे 'https://opensign.me/your-username'। आप यह भी सीखेंगे कि अपनी टैगलाइन को कैसे अनुकूलित करें और अपने टेम्पलेट्स को सार्वजनिक हस्ताक्षर के लिए उपलब्ध कराएं।",
"allowModify-widgets": "आप इन फ़ील्ड में से किसी को भी दस्तावेज़ पर खींच और छोड़ सकते हैं, इसके अतिरिक्त दस्तावेज़ निर्माता द्वारा आपके लिए पहले से निर्दिष्ट फ़ील्ड के। " "allowModify-widgets": "आप इन फ़ील्ड में से किसी को भी दस्तावेज़ पर खींच और छोड़ सकते हैं, इसके अतिरिक्त दस्तावेज़ निर्माता द्वारा आपके लिए पहले से निर्दिष्ट फ़ील्ड के। "
}, },
@@ -728,7 +765,7 @@
"public-tour-message": "साझा करने योग्य लिंक उत्पन्न करने से पहले टेम्पलेट को सार्वजनिक होना चाहिए।", "public-tour-message": "साझा करने योग्य लिंक उत्पन्न करने से पहले टेम्पलेट को सार्वजनिक होना चाहिए।",
"add-user-template": "इसके लिए फ़ील्ड जोड़ने से पहले आपको एक भूमिका जोड़ने की आवश्यकता है।", "add-user-template": "इसके लिए फ़ील्ड जोड़ने से पहले आपको एक भूमिका जोड़ने की आवश्यकता है।",
"pdf-uncompatible": "यह पीडीएफ संगत नहीं है, कृपया {{appName}} से संपर्क करें", "pdf-uncompatible": "यह पीडीएफ संगत नहीं है, कृपया {{appName}} से संपर्क करें",
"text-field-tour": "दस्तावेज़ भेजे जाने से पहले 'टेक्स्ट' प्रकार के फ़ील्ड पहले से भरे जाने चाहिए। यदि आपको हस्ताक्षरकर्ताओं से इनपुट प्रदान करने की आवश्यकता है, तो इसके बजाय 'टेक्स्ट इनपुट' फ़ील्ड का उपयोग करें।", "text-field-tour": "दस्तावेज़ सबमिट करने से पहले 'प्रीफ़िल' फ़ील्ड को पहले से भरना होगा। यदि आपको हस्ताक्षरकर्ताओं से इनपुट की आवश्यकता है, तो इसके बजाय हस्ताक्षरकर्ता फ़ील्ड का उपयोग करें।",
"attach-signer-tour": "आपको प्रत्येक भूमिका में एक हस्ताक्षरकर्ता संलग्न करने की आवश्यकता है। आप इस आइकन पर क्लिक करके ऐसा कर सकते हैं। एक बार जब आप एक हस्ताक्षरकर्ता का चयन कर लेते हैं तो यह उस भूमिका से जुड़े सभी फ़ील्ड से जुड़ जाएगा जो एक ही रंग में दिखाई देते हैं।", "attach-signer-tour": "आपको प्रत्येक भूमिका में एक हस्ताक्षरकर्ता संलग्न करने की आवश्यकता है। आप इस आइकन पर क्लिक करके ऐसा कर सकते हैं। एक बार जब आप एक हस्ताक्षरकर्ता का चयन कर लेते हैं तो यह उस भूमिका से जुड़े सभी फ़ील्ड से जुड़ जाएगा जो एक ही रंग में दिखाई देते हैं।",
"allowed-signature-types": "अनुमत हस्ताक्षर प्रकार", "allowed-signature-types": "अनुमत हस्ताक्षर प्रकार",
"at-least-one-signature-type": "कम से कम एक हस्ताक्षर प्रकार सक्षम होना चाहिए।", "at-least-one-signature-type": "कम से कम एक हस्ताक्षर प्रकार सक्षम होना चाहिए।",
@@ -776,7 +813,7 @@
"agree-p1": "मैं पुष्टि करता हूं कि मैंने पढ़ लिया है और समझ लिया है ", "agree-p1": "मैं पुष्टि करता हूं कि मैंने पढ़ लिया है और समझ लिया है ",
"agree-p2": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण", "agree-p2": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण",
"agree-p3": "और इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर का उपयोग करने के लिए सहमति।", "agree-p3": "और इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर का उपयोग करने के लिए सहमति।",
"agrre-button": " सहमत हूँ और जारी रखें", "agrre-button": "मैं पुष्टि करता हूँ और आगे बढ़ने के लिए सहमत हूँ",
"term-cond-title": "नियम और शर्तें", "term-cond-title": "नियम और शर्तें",
"term-cond-h": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण", "term-cond-h": "इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण",
"term-cond-p1": "यह इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण ('प्रकटीकरण') दस्तावेज़ निर्माता ('प्रेषक') और हस्ताक्षरकर्ता ('आप') के बीच एक समझौता है, जिसे {{appName}} प्लेटफ़ॉर्म ('प्लेटफ़ॉर्म') के माध्यम से सुगम बनाया गया है। {{appName}} के माध्यम से दस्तावेज़ों पर हस्ताक्षर करके, आप इस प्रकटीकरण में उल्लिखित शर्तों से सहमत होते हैं। कृपया आगे बढ़ने से पहले इसे ध्यान से पढ़ें।", "term-cond-p1": "यह इलेक्ट्रॉनिक रिकॉर्ड और हस्ताक्षर प्रकटीकरण ('प्रकटीकरण') दस्तावेज़ निर्माता ('प्रेषक') और हस्ताक्षरकर्ता ('आप') के बीच एक समझौता है, जिसे {{appName}} प्लेटफ़ॉर्म ('प्लेटफ़ॉर्म') के माध्यम से सुगम बनाया गया है। {{appName}} के माध्यम से दस्तावेज़ों पर हस्ताक्षर करके, आप इस प्रकटीकरण में उल्लिखित शर्तों से सहमत होते हैं। कृपया आगे बढ़ने से पहले इसे ध्यान से पढ़ें।",
@@ -879,6 +916,10 @@
"thanks-for-feedback": "आपकी प्रतिक्रिया के लिए धन्यवाद 🙏", "thanks-for-feedback": "आपकी प्रतिक्रिया के लिए धन्यवाद 🙏",
"share-your-feedback": "अपनी प्रतिक्रिया साझा करें", "share-your-feedback": "अपनी प्रतिक्रिया साझा करें",
"share-your-review": "अपनी समीक्षा साझा करें", "share-your-review": "अपनी समीक्षा साझा करें",
"please-select-rating": "कृपया एक रेटिंग चुनें",
"feedback-optional": "प्रतिक्रिया (वैकल्पिक)",
"feedback-saved": "प्रतिक्रिया सहेज ली गई है।",
"feedback-save-error": "प्रतिक्रिया सहेजी नहीं जा सकी, कृपया पुनः प्रयास करें।",
"date-format": "दिनांक प्रारूप", "date-format": "दिनांक प्रारूप",
"document-deleted": "दस्तावेज़ हटा दिया गया है या आपके पास पहुंच नहीं है। कृपया प्रेषक से संपर्क करें।", "document-deleted": "दस्तावेज़ हटा दिया गया है या आपके पास पहुंच नहीं है। कृपया प्रेषक से संपर्क करें।",
"save-as-template-?": "क्या आप वाकई इस दस्तावेज़ को टेम्पलेट के रूप में सहेजना चाहते हैं?", "save-as-template-?": "क्या आप वाकई इस दस्तावेज़ को टेम्पलेट के रूप में सहेजना चाहते हैं?",
@@ -1032,14 +1073,188 @@
"not-calculated": "गणना नहीं की गई", "not-calculated": "गणना नहीं की गई",
"not-found-in-signature": "हस्ताक्षर में नहीं मिला", "not-found-in-signature": "हस्ताक्षर में नहीं मिला",
"readonly-error": "रीड-ओनली {{widgetName}} विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", "readonly-error": "रीड-ओनली {{widgetName}} विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।",
"choose-one":"एक का चयन", "choose-one": "एक का चयन",
"search-templates": "टेम्पलेट खोजें…", "search-templates": "टेम्पलेट खोजें…",
"search-documents": "दस्तावेज़ खोजें…", "search-documents": "दस्तावेज़ खोजें…",
"search-contacts": "संपर्क खोजें…", "search-contacts": "संपर्क खोजें…",
"add-role-alert": "कृपया कम से कम एक भूमिका जोड़ें",
"edit-draft": "ड्राफ्ट संपादित करें",
"invalid-email-found": "अमान्य ईमेल पाया गया: {{email}}", "invalid-email-found": "अमान्य ईमेल पाया गया: {{email}}",
"duplicate-email-found": "डुप्लिकेट ईमेल पाया गया: {{email}}", "duplicate-email-found": "डुप्लिकेट ईमेल पाया गया: {{email}}",
"vertical": "वर्टिकल", "vertical": "वर्टिकल",
"horizontal": "हॉरिज़ॉन्टल", "horizontal": "हॉरिज़ॉन्टल",
"billing": "बिलिंग", "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": "स्वतः उत्पन्न करें"
}
+252 -37
View File
@@ -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": "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", "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", "create-account": "Crea Account",
"login": "Accedi", "login": "Accedi",
"language": "Lingua", "language": "Lingua",
"dark-mode": "Modalità scura", "dark-mode": "Modalità scura",
"name": "Nome", "name": "Nome",
"phone": "Telefono", "phone": "Telefono",
"phone-optional": "facoltativo", "phone-optional": "Opzionale",
"email": "Email", "email": "Email",
"company": "Azienda", "company": "Azienda",
"job-title": "Titolo professionale", "job-title": "Titolo professionale",
@@ -26,6 +26,8 @@
"Name": "Nome", "Name": "Nome",
"Date": "Data" "Date": "Data"
}, },
"folder": "Cartella",
"pdf": "Pdf",
"context-menu": { "context-menu": {
"Download": "Scarica", "Download": "Scarica",
"Rename": "Rinomina", "Rename": "Rinomina",
@@ -44,6 +46,10 @@
"contact-now": "Contatta ora", "contact-now": "Contatta ora",
"upgrade-to": "Aggiorna a", "upgrade-to": "Aggiorna a",
"plan": "Piano", "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.", "subscription-renew-warning": "Il tuo abbonamento scadrà tra {{remainingDays}} giorni. Ti preghiamo di rinnovarlo.",
"subscribe-card-teamplan": "Sblocca tutto il potenziale della collaborazione! Crea organizzazioni, team e gerarchie illimitati. Condividi modelli senza problemi tra i team e assegna ruoli personalizzati agli utenti. Migliora il tuo flusso di lavoro oggi stesso!", "subscribe-card-teamplan": "Sblocca tutto il potenziale della collaborazione! Crea organizzazioni, team e gerarchie illimitati. Condividi modelli senza problemi tra i team e assegna ruoli personalizzati agli utenti. Migliora il tuo flusso di lavoro oggi stesso!",
"subscribe-card-plan": "Sblocca le funzionalità premium a partire da soli {{premiumPrice}}/mese. Approfitta di prestazioni migliorate e paga solo {{addonPrice}} per ogni credito aggiuntivo dopo quelli inclusi.", "subscribe-card-plan": "Sblocca le funzionalità premium a partire da soli {{premiumPrice}}/mese. Approfitta di prestazioni migliorate e paga solo {{addonPrice}} per ogni credito aggiuntivo dopo quelli inclusi.",
@@ -58,7 +64,7 @@
"welcome": "Bentornato!", "welcome": "Bentornato!",
"Login-to-your-account": "Accedi al tuo account", "Login-to-your-account": "Accedi al tuo account",
"password": "Password", "password": "Password",
"forgot-password": "Password dimenticata?", "forgot-password": "Password dimenticata",
"loading": "Caricamento...", "loading": "Caricamento...",
"of": "di", "of": "di",
"sign-SSO": "Accedi con SSO", "sign-SSO": "Accedi con SSO",
@@ -159,7 +165,8 @@
"daily-mail-quota": "Quota e-mail giornaliera", "daily-mail-quota": "Quota e-mail giornaliera",
"Save as template": "Salva come modello", "Save as template": "Salva come modello",
"Fix & resend": "Correggi e reinvia", "Fix & resend": "Correggi e reinvia",
"Kiosk Mode": "Modalità Kiosk" "Kiosk Mode": "Modalità Kiosk",
"Reset password": "Reimposta password"
}, },
"report-heading": { "report-heading": {
"Sr.No": "Nr.", "Sr.No": "Nr.",
@@ -181,7 +188,20 @@
"created-date": "Data di creazione", "created-date": "Data di creazione",
"Type": "Tipo", "Type": "Tipo",
"Logs": "Log", "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": { "report-help": {
"Draft Documents": "Questi sono documenti che hai iniziato ma non hai ancora finalizzato per l'invio.", "Draft Documents": "Questi sono documenti che hai iniziato ma non hai ancora finalizzato per l'invio.",
@@ -193,16 +213,15 @@
"Contactbook": "Questa è una lista di contatti/firmatari aggiunti da te. Appariranno come suggerimenti quando provi ad aggiungere firmatari a un nuovo documento.", "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." "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",
"Sign Yourself": "Firma tu stesso", "Request Signatures": "Richiedi firme",
"Request Signatures": "Richiedi firme", "New Template": "Nuovo modello",
"New Template": "Nuovo modello"
},
"file-type": "pdf, png, jpg, jpeg", "file-type": "pdf, png, jpg, jpeg",
"docx": "docx", "docx": "docx",
"file-selected": "file selezionato", "file-selected": "file selezionato",
"template-title": "Titolo del modello", "template-title": "Titolo del modello",
"document-title": "Titolo del documento", "document-title": "Titolo del documento",
"title": "Titolo",
"description": "Descrizione", "description": "Descrizione",
"time-to-complete": "Tempo per completare (giorni)", "time-to-complete": "Tempo per completare (giorni)",
"send-in-order": "Invia in ordine", "send-in-order": "Invia in ordine",
@@ -280,7 +299,7 @@
"deactivate": "disattiva", "deactivate": "disattiva",
"this-user": "questo utente", "this-user": "questo utente",
"delete-user": "Elimina utente", "delete-user": "Elimina utente",
"delete": "elimina", "delete": "Elimina",
"add-user": "Aggiungi utente", "add-user": "Aggiungi utente",
"password-generateed": "La password verrà generata solo una volta; assicurati di copiarla.", "password-generateed": "La password verrà generata solo una volta; assicurati di copiarla.",
"Team status": "Stato del Team", "Team status": "Stato del Team",
@@ -367,7 +386,8 @@
"dropdown": "menu a tendina", "dropdown": "menu a tendina",
"radio button": "pulsante di opzione", "radio button": "pulsante di opzione",
"image": "immagine", "image": "immagine",
"email": "email" "email": "email",
"number": "numero"
}, },
"fields": "Campi", "fields": "Campi",
"recipients": "Destinatari", "recipients": "Destinatari",
@@ -379,6 +399,7 @@
"certificate": "Certificato", "certificate": "Certificato",
"decline": "Rifiuta", "decline": "Rifiuta",
"finish": "Completa", "finish": "Completa",
"done": "Fatto",
"mail": "Email", "mail": "Email",
"sign-now": "Firma ora", "sign-now": "Firma ora",
"successfully-signed": "Firmato con successo!", "successfully-signed": "Firmato con successo!",
@@ -388,6 +409,10 @@
"Email-verified-alert-1": "Email verificata.", "Email-verified-alert-1": "Email verificata.",
"Email-verified-alert-2": "Email già verificata.", "Email-verified-alert-2": "Email già verificata.",
"upload-stamp-image": "Carica immagine timbro", "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", "draw": "Disegno",
"type": "Digitata", "type": "Digitata",
"color-type": { "color-type": {
@@ -400,7 +425,7 @@
"upload": "Caricamento", "upload": "Caricamento",
"initial-teb": "Iniziali", "initial-teb": "Iniziali",
"signature-tab": "Firma", "signature-tab": "Firma",
"your-signature": "La tua firma", "initial-alert": "La mia iniziale non trovata",
"copy-title": "Copia widget in", "copy-title": "Copia widget in",
"contact-delete-alert": "Sei sicuro di voler eliminare questo contatto?", "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", "reset-password-alert-1": "Il link per reimpostare la password è stato inviato al tuo indirizzo email",
@@ -419,6 +444,19 @@
"maximum-check": "Controllo massimo", "maximum-check": "Controllo massimo",
"cell-count": "conteggio delle cellule", "cell-count": "conteggio delle cellule",
"default-value": "Valore predefinita", "default-value": "Valore predefinita",
"formula": "Formula",
"formula-placeholder": "Crea una formula utilizzando i widget numerici",
"formula-no-widgets": "Non ci sono ancora altri widget numerici assegnati a questo firmatario.",
"supported-operators": "Operatori supportati:",
"formula-helper": "Fai clic su una chip di widget numerico per inserirla (es. {{number-1}} + {{number-2}}).",
"formula-error-letter": "Racchiudi i nomi dei widget tra doppie parentesi graffe (es. {{number-1}}).",
"formula-error-characters": "Al di fuori delle parentesi sono consentiti solo numeri e +, -, *, /, (, ).",
"formula-error-braces": "Ogni {{ di apertura deve avere una }} corrispondente.",
"formula-error-operators": "Quando usi operatori o parentesi, aggiungi almeno due valori.",
"formula-error-leading-operator": "Le formule non possono iniziare con operatori.",
"decimal-places": "Cifre decimali",
"number-of-decimal-places": "Numero di cifre decimali",
"decimal-places-helper": "Imposta su 0 per numeri interi",
"select": "Seleziona", "select": "Seleziona",
"read-only": "È di sola lettura", "read-only": "È di sola lettura",
"hide-labels": "Nascondi etichette", "hide-labels": "Nascondi etichette",
@@ -465,13 +503,13 @@
"add-recipients": "Aggiungi destinatari", "add-recipients": "Aggiungi destinatari",
"loading-mssg": "Questo potrebbe richiedere del tempo", "loading-mssg": "Questo potrebbe richiedere del tempo",
"send-mail": "Invia Mail", "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-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-2": "Conferma di aver compilato il campo di testo.",
"placeholder-alert-3": "Sei sicuro di voler inviare questo documento per le firme?", "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-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": "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-5": "Vuoi firmare i documenti ora?",
"placeholder-alert-6": "Configura l'adattatore email per inviare mail!", "placeholder-alert-6": "Configura l'adattatore email per inviare mail!",
"placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!", "placeholder-alert-7": "Seleziona il firmatario per aggiungere un segnaposto!",
@@ -600,7 +638,8 @@
"tour-mssg": { "tour-mssg": {
"home-layout-1": "Accesso effettuato con successo! Diamo un'occhiata.", "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-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.", "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-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.", "opensign-drive-2": "Fai clic sul pulsante aggiungi per creare una nuova cartella o documento.",
@@ -610,34 +649,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-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.", "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-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-2": "Fai clic su uno qualsiasi dei campi presenti nel documento per iniziare. Potrai quindi compilare le informazioni richieste.",
"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-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-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-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.", "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-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-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-4": "Trascina o fai clic su un widget 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-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.", "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.", "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.", "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'.", "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 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.", "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 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.", "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.", "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-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-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": "Trascina o fai clic su un campo per aggiungerlo al documento.", "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.",
"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.",
"webhook-1": "Esegui l'upgrade ora per configurare il webhook.", "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.", "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.", "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.", "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.", "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.", "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." "allowModify-widgets": "È possibile trascinare e rilasciare uno qualsiasi di questi campi nel documento, oltre ai campi già designati dal creatore del documento."
}, },
@@ -728,11 +765,11 @@
"public-tour-message": "Il modello deve essere pubblico prima di poter generare un link condivisibile.", "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.", "add-user-template": "Devi aggiungere un ruolo prima di poter aggiungere i campi per esso.",
"pdf-uncompatible": "Questo PDF non è compatibile, contatta {{appName}}", "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.", "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", "allowed-signature-types": "Tipi di firma consentiti",
"at-least-one-signature-type": "Almeno un tipo di firma deve essere abilitato.", "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", "add-signer-note": "Nota - Questo contatto verrà aggiunto alla tua lista di contatti",
"allowed-signature-types-help": { "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.", "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 +813,7 @@
"agree-p1": "Confermo di aver letto e compreso il", "agree-p1": "Confermo di aver letto e compreso il",
"agree-p2": "Divulgazione sulle registrazioni e firme elettroniche", "agree-p2": "Divulgazione sulle registrazioni e firme elettroniche",
"agree-p3": "e acconsento all'uso di 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-title": "Termini e Condizioni",
"term-cond-h": "DIVULGAZIONE SULLE REGISTRAZIONI E FIRME ELETTRONICHE", "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.", "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 +916,10 @@
"thanks-for-feedback": "Grazie per il tuo feedback 🙏", "thanks-for-feedback": "Grazie per il tuo feedback 🙏",
"share-your-feedback": "Condividi il tuo feedback", "share-your-feedback": "Condividi il tuo feedback",
"share-your-review": "Condividi la tua recensione", "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", "date-format": "Formato data",
"document-deleted": "Il documento è stato eliminato o non hai accesso. Si prega di contattare il mittente.", "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?", "save-as-template-?": "Sei sicuro di voler salvare questo documento come modello?",
@@ -898,7 +939,7 @@
"note-length-alert": "La nota può contenere al massimo 200 caratteri.", "note-length-alert": "La nota può contenere al massimo 200 caratteri.",
"description-length-alert": " La descrizione può contenere al massimo 500 caratteri.", "description-length-alert": " La descrizione può contenere al massimo 500 caratteri.",
"fix-&-resend-document": "Correggi e reinvia il documento", "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", "start-editing": "Inizia a modificare",
"unsaved-changes-discard-them?": "Hai modifiche non salvate. Vuoi scartarle?", "unsaved-changes-discard-them?": "Hai modifiche non salvate. Vuoi scartarle?",
"yes-discard": "Sì, scarta", "yes-discard": "Sì, scarta",
@@ -988,7 +1029,7 @@
"finish-mssg": "Sei sicuro di voler completare il documento?", "finish-mssg": "Sei sicuro di voler completare il documento?",
"review": "Rivedere", "review": "Rivedere",
"next-field": "Campo successivo", "next-field": "Campo successivo",
"required-mssg":"{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti", "required-mssg": "{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti",
"verify-document": "Verifica documento", "verify-document": "Verifica documento",
"verify-document-signature": "Verifica firma documento", "verify-document-signature": "Verifica firma documento",
"select-pdf-document": "Seleziona documento PDF", "select-pdf-document": "Seleziona documento PDF",
@@ -1032,14 +1073,188 @@
"not-calculated": "Non calcolato", "not-calculated": "Non calcolato",
"not-found-in-signature": "Non trovato nella firma", "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.", "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-templates": "Cerca modelli…",
"search-documents": "Cerca documenti…", "search-documents": "Cerca documenti…",
"search-contacts": "Cerca contatti…", "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}}", "invalid-email-found": "Email non valida trovata: {{email}}",
"duplicate-email-found": "Email duplicata trovata: {{email}}", "duplicate-email-found": "Email duplicata trovata: {{email}}",
"vertical": "Verticale", "vertical": "Verticale",
"horizontal": "Orizzontale", "horizontal": "Orizzontale",
"billing": "Fatturazione", "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"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

+4 -4
View File
@@ -1,4 +1,4 @@
Name,Email,Phone Name,Email,Phone,Company,JobTitle
John Doe,john@example.com,1234567890 John Doe,john@example.com,1234567890,abc firm,dev
Jane Smith,jane@example.com,9876543210 Jane Smith,jane@example.com,9876543210,xyz firm,manager
Foo Bar,foo@example.com,5555555555 Foo Bar,foo@example.com,5555555555,xyz firm,director
1 Name Email Phone Company JobTitle
2 John Doe john@example.com 1234567890 abc firm dev
3 Jane Smith jane@example.com 9876543210 xyz firm manager
4 Foo Bar foo@example.com 5555555555 xyz firm director
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.
+57 -96
View File
@@ -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 { Routes, Route, BrowserRouter } from "react-router";
import { pdfjs } from "react-pdf"; import { pdfjs } from "react-pdf";
import Form from "./pages/Form"; import Form from "./pages/Form";
@@ -13,24 +14,27 @@ import SignYourSelf from "./pages/SignyourselfPdf";
import DraftDocument from "./components/pdf/DraftDocument"; import DraftDocument from "./components/pdf/DraftDocument";
import PlaceHolderSign from "./pages/PlaceHolderSign"; import PlaceHolderSign from "./pages/PlaceHolderSign";
import PdfRequestFiles from "./pages/PdfRequestFiles"; import PdfRequestFiles from "./pages/PdfRequestFiles";
import LazyPage from "./primitives/LazyPage"; import Lazy from "./primitives/LazyPage";
import Loader from "./primitives/Loader"; import Loader from "./primitives/Loader";
import UserList from "./pages/UserList"; import UserList from "./pages/UserList";
import { serverUrl_fn } from "./constant/appinfo"; import { serverUrl_fn } from "./constant/appinfo";
import DocSuccessPage from "./pages/DocSuccessPage"; import DocSuccessPage from "./pages/DocSuccessPage";
import ValidateSession from "./primitives/ValidateSession"; import DragProvider from "./components/DragProivder";
const DebugPdf = lazy(() => import("./pages/DebugPdf")); import Title from "./components/Title";
const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const DebugPdf = lazyWithRetry(() => import("./pages/DebugPdf"));
const GuestLogin = lazy(() => import("./pages/GuestLogin")); const ForgetPassword = lazyWithRetry(() => import("./pages/ForgetPassword"));
const ChangePassword = lazy(() => import("./pages/ChangePassword")); const GuestLogin = lazyWithRetry(() => import("./pages/GuestLogin"));
const UserProfile = lazy(() => import("./pages/UserProfile")); const ChangePassword = lazyWithRetry(() => import("./pages/ChangePassword"));
const Opensigndrive = lazy(() => import("./pages/Opensigndrive")); const UserProfile = lazyWithRetry(() => import("./pages/UserProfile"));
const ManageSign = lazy(() => import("./pages/Managesign")); const Opensigndrive = lazyWithRetry(() => import("./pages/Opensigndrive"));
const AddAdmin = lazy(() => import("./pages/AddAdmin")); const ManageSign = lazyWithRetry(() => import("./pages/Managesign"));
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin")); const AddAdmin = lazyWithRetry(() => import("./pages/AddAdmin"));
const Preferences = lazy(() => import("./pages/Preferences")); const UpdateExistUserAdmin = lazyWithRetry(
const Login = lazy(() => import("./pages/Login")); () => import("./pages/UpdateExistUserAdmin")
const VerifyDocument = lazy(() => import("./pages/VerifyDocument")); );
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`; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => { const AppLoader = () => {
return ( return (
@@ -42,137 +46,94 @@ const AppLoader = () => {
function App() { function App() {
const [isloading, setIsLoading] = useState(true); const [isloading, setIsLoading] = useState(true);
useEffect(() => { 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 ( return (
<div className="bg-base-200"> <div className="bg-base-200">
{isloading ? ( {isloading ? (
<AppLoader /> <AppLoader />
) : ( ) : (
<BrowserRouter> <BrowserRouter>
<Title />
<Routes> <Routes>
<Route element={<ValidateRoute />}> <Route element={<ValidateRoute />}>
<Route exact path="/" element={<LazyPage Page={Login} />} /> <Route exact path="/" element={<Lazy Page={Login} />} />
<Route <Route path="/addadmin" element={<Lazy Page={AddAdmin} />} />
path="/addadmin"
element={<LazyPage Page={AddAdmin} />}
/>
<Route <Route
path="/upgrade-2.1" path="/upgrade-2.1"
element={<LazyPage Page={UpdateExistUserAdmin} />} element={<Lazy Page={UpdateExistUserAdmin} />}
/> />
</Route> </Route>
<Route element={<Validate />}> <Route element={<Validate />}>
<Route
path="/load/template/:templateId"
element={<TemplatePlaceholder />}
/>
<Route
exact
path="/load/placeholdersign/:docId"
element={<PlaceHolderSign />}
/>
<Route <Route
exact exact
path="/load/recipientSignPdf/:docId/:contactBookId" path="/load/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />} element={<DragProvider Page={PdfRequestFiles} />}
/> />
</Route> </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 <Route
path="/login/:base64url" 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 <Route
path="/forgetpassword" path="/forgetpassword"
element={<LazyPage Page={ForgetPassword} />} element={<Lazy Page={ForgetPassword} />}
/> />
<Route <Route element={<HomeLayout />}>
element={ <Route path="/users" element={<UserList />} />
<ValidateSession> <Route
<HomeLayout /> path="/changepassword"
</ValidateSession> element={<Lazy Page={ChangePassword} />}
} />
>
<Route
path="/changepassword"
element={<LazyPage Page={ChangePassword} />}
/>
<Route path="/form/:id" element={<Form />} /> <Route path="/form/:id" element={<Form />} />
<Route path="/report/:id" element={<Report />} /> <Route path="/report/:id" element={<Report />} />
<Route path="/dashboard/:id" element={<Dashboard />} /> <Route path="/dashboard/:id" element={<Dashboard />} />
<Route <Route path="/profile" element={<Lazy Page={UserProfile} />} />
path="/profile" <Route path="/drive" element={<Lazy Page={Opensigndrive} />} />
element={<LazyPage Page={UserProfile} />} <Route path="/managesign" element={<Lazy Page={ManageSign} />} />
/>
<Route
path="/drive"
element={<LazyPage Page={Opensigndrive} />}
/>
<Route
path="/managesign"
element={<LazyPage Page={ManageSign} />}
/>
<Route <Route
path="/template/:templateId" path="/template/:templateId"
element={<TemplatePlaceholder />} element={<DragProvider Page={TemplatePlaceholder} />}
/> />
{/* signyouself route with no rowlevel data using docId from url */} {/* 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 */} {/* 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*/} {/* recipient placeholder set route with no rowlevel data using docId from url*/}
<Route <Route
path="/placeHolderSign/:docId" path="/placeHolderSign/:docId"
element={<PlaceHolderSign />} element={<DragProvider Page={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 />}
/> />
{/* recipient signature route with no rowlevel data using docId from url */} {/* recipient signature route with no rowlevel data using docId from url */}
<Route <Route
path="/recipientSignPdf/:docId/:contactBookId" path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />} element={<DragProvider Page={PdfRequestFiles} />}
/> />
<Route <Route
path="/recipientSignPdf/:docId" path="/recipientSignPdf/:docId"
element={<PdfRequestFiles />} element={<DragProvider Page={PdfRequestFiles} />}
/> />
<Route path="/users" element={<UserList />} />
<Route <Route
path="/verify-document" path="/verify-document"
element={<LazyPage Page={VerifyDocument} />} element={<Lazy Page={VerifyDocument} />}
/> />
<Route <Route
path="/preferences" path="/preferences"
element={<LazyPage Page={Preferences} />} element={<Lazy Page={Preferences} />}
/> />
</Route> </Route>
<Route path="/success" element={<DocSuccessPage />} /> <Route path="/success" element={<DocSuccessPage />} />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+3 -1
View File
@@ -49,7 +49,7 @@ const AddSigner = (props) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (!emailRegex.test(email)) { if (!emailRegex.test(email)) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
} else { } else {
setIsLoader(true); setIsLoader(true);
if (localStorage.getItem("TenantId")) { if (localStorage.getItem("TenantId")) {
@@ -244,6 +244,7 @@ const AddSigner = (props) => {
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))} onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder={t("enter-name")}
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
@@ -263,6 +264,7 @@ const AddSigner = (props) => {
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
disabled={addYourself} disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder={t("enter-email")}
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
+9 -10
View File
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { import {
copytoData, copytoData,
@@ -72,7 +71,7 @@ const AddUser = (props) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (!emailRegex.test(formdata.email)) { if (!emailRegex.test(formdata.email)) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
} else { } else {
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsFormLoader(true); setIsFormLoader(true);
@@ -100,7 +99,6 @@ const AddUser = (props) => {
}; };
const res = await Parse.Cloud.run("adduser", params); const res = await Parse.Cloud.run("adduser", params);
const parseData = JSON.parse(JSON.stringify(res)); const parseData = JSON.parse(JSON.stringify(res));
console.log("parseData ", parseData);
if (props.closePopup) { if (props.closePopup) {
props.closePopup(); props.closePopup();
} }
@@ -155,7 +153,6 @@ const AddUser = (props) => {
}; };
return ( return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative"> <div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
<Title title={t("add-user")} />
{isFormLoader && ( {isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50"> <div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader /> <Loader />
@@ -166,7 +163,7 @@ const AddUser = (props) => {
<div className="mb-3"> <div className="mb-3">
<label <label
htmlFor="name" htmlFor="name"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("name")} {t("name")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
@@ -182,12 +179,13 @@ const AddUser = (props) => {
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
required required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder={t("enter-name")}
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label <label
htmlFor="email" htmlFor="email"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("email")} {t("email")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
@@ -203,10 +201,11 @@ const AddUser = (props) => {
} }
onInput={(e) => e.target.setCustomValidity("")} 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" 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>
<div className="mb-3"> <div className="mb-3">
<label className="block text-xs text-gray-700 font-semibold"> <label className="block text-xs font-semibold">
{t("password")} {t("password")}
</label> </label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]"> <div className="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"> <div className="mb-3">
<label <label
htmlFor="phone" htmlFor="phone"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("phone")} {t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */} {/* <span className="text-[red] text-[13px]"> *</span> */}
@@ -240,7 +239,7 @@ const AddUser = (props) => {
<div className="mb-3"> <div className="mb-3">
<label <label
htmlFor="phone" htmlFor="phone"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("Role")} {t("Role")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
+12 -2
View File
@@ -17,6 +17,7 @@ const BulkSendUi = (props) => {
const [isLoader, setIsLoader] = useState(false); const [isLoader, setIsLoader] = useState(false);
const [signers, setSigners] = useState([]); const [signers, setSigners] = useState([]);
const [emails, setEmails] = useState([]); const [emails, setEmails] = useState([]);
const [isPrefillExist, setIsPrefillExist] = useState(false);
useEffect(() => { useEffect(() => {
signatureExist(); signatureExist();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -24,9 +25,14 @@ const BulkSendUi = (props) => {
//function to check at least one signature field exist //function to check at least one signature field exist
const signatureExist = async () => { const signatureExist = async () => {
const isPrefill = props?.Placeholders.some((x) => x?.Role === "prefill");
if (isPrefill) {
setIsPrefillExist(isPrefill);
}
setIsDisableBulkSend(false); setIsDisableBulkSend(false);
const getPlaceholder = props?.Placeholders; 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) => placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature") holder?.pos?.some((posItem) => posItem?.type === "signature")
) )
@@ -216,7 +222,11 @@ const BulkSendUi = (props) => {
<Loader /> <Loader />
</div> </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 ? ( {props.Placeholders?.length > 0 ? (
isSignatureExist ? ( 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"
/>
<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,51 @@
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 "./pdf/DragElement";
import LazyPage from "../primitives/LazyPage";
import { GuidelinesProvider } from "../context/GuidelinesContext";
const HTML5toTouch = {
backends: [
{
id: "html5",
backend: HTML5Backend,
transition: MouseTransition
},
{
id: "touch",
backend: TouchBackend,
options: { enableMouseEvents: true },
preview: true,
transition: TouchTransition
}
]
};
/* This handling in only for devices which support touch */
const generatePreview = (props) => {
const { item, style } = props;
const newStyle = { ...style };
return (
<div style={newStyle}>
<DragElement {...item} />
</div>
);
};
export default function DragProvider({ Page, lazy = false }) {
return (
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<GuidelinesProvider>
{lazy ? <LazyPage Page={Page} /> : <Page />}
</GuidelinesProvider>
</DndProvider>
);
}
+41 -22
View File
@@ -12,27 +12,44 @@ import {
} from "../constant/Utils"; } from "../constant/Utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux";
import { toggleSidebar } from "../redux/reducers/sidebarReducer.js";
import { sessionStatus } from "../redux/reducers/userReducer.js";
const Header = ({ showSidebar, setIsMenu, isConsole }) => { const Header = ({ isConsole, setIsLoggingOut }) => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const { width } = useWindowSize(); const { width } = useWindowSize();
const dispatch = useDispatch();
const username = localStorage.getItem("username") || ""; const username = localStorage.getItem("username") || "";
const image = localStorage.getItem("profileImg") || dp; const image = localStorage.getItem("profileImg") || dp;
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [applogo, setAppLogo] = useState(""); const [applogo, setAppLogo] = useState("");
const [isDarkTheme, setIsDarkTheme] = useState();
const toggleDropdown = () => { const toggleDropdown = () => {
setIsOpen(!isOpen); setIsOpen(!isOpen);
if (width <= 768) { closeSidebar();
setIsMenu(false); };
const closeSidebar = () => {
if (width && width <= 768) {
dispatch(toggleSidebar(false));
} }
}; };
useEffect(() => { useEffect(() => {
initializeHead(); initializeHead();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => {
closeSidebar();
}, [width]);
const showSidebar = () => {
dispatch(toggleSidebar());
};
async function initializeHead() { async function initializeHead() {
const applogo = await getAppLogo(); const applogo = await getAppLogo();
@@ -44,12 +61,15 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
} }
} }
const closeDropdown = async () => { const handleLogout = async () => {
setIsOpen(false); setIsOpen(false);
setIsLoggingOut(true);
try { try {
await Parse.User.logOut(); await Parse.User.logOut();
} catch (err) { } catch (err) {
console.log("Err while logging out", err); console.log("Err while logging out", err);
} finally {
dispatch(sessionStatus(true));
} }
let appdata = localStorage.getItem("userSettings"); let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo"); let applogo = localStorage.getItem("appLogo");
@@ -57,6 +77,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
let PageLanding = localStorage.getItem("PageLanding"); let PageLanding = localStorage.getItem("PageLanding");
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
let favicon = localStorage.getItem("favicon");
localStorage.clear(); localStorage.clear();
saveLanguageInLocal(i18n); saveLanguageInLocal(i18n);
@@ -66,7 +87,8 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
localStorage.setItem("userSettings", appdata); localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
localStorage.setItem("favicon", favicon);
setIsLoggingOut(false);
navigate("/"); navigate("/");
}; };
@@ -88,22 +110,15 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
useEffect(() => { useEffect(() => {
const updateLogoForTheme = () => { const updateThemeStatus = () => {
const isDarkMode = const isDarkTheme =
document.documentElement.getAttribute("data-theme") === "opensigndark"; document.documentElement.getAttribute("data-theme") === "opensigndark";
const logo = isDarkMode setIsDarkTheme(isDarkTheme);
? "/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);
}
}; };
updateThemeStatus();
// Set the logo immediately based on the current theme
updateLogoForTheme();
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
updateLogoForTheme(); updateThemeStatus();
}); });
observer.observe(document.documentElement, { observer.observe(document.documentElement, {
@@ -112,10 +127,10 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
}); });
return () => observer.disconnect(); return () => observer.disconnect();
}, [applogo]); }, []);
return ( return (
<div> <>
<div className="op-navbar bg-base-100 shadow touch-none"> <div className="op-navbar bg-base-100 shadow touch-none">
<div className="flex-none"> <div className="flex-none">
<button <button
@@ -130,7 +145,11 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
{applogo && ( {applogo && (
<img <img
className="object-contain h-full w-auto" className="object-contain h-full w-auto"
src={applogo} src={
isDarkTheme
? "/static/js/assets/images/logo-dark.png"
: applogo
}
alt="logo" alt="logo"
/> />
)} )}
@@ -235,7 +254,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
</li> </li>
</> </>
)} )}
<li onClick={closeDropdown}> <li onClick={handleLogout}>
<span> <span>
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "} <i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
{t("log-out")} {t("log-out")}
@@ -245,7 +264,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
</div> </div>
</div> </div>
</div> </div>
</div> </>
); );
}; };
+1 -1
View File
@@ -26,7 +26,7 @@ function RotateAlert(props) {
props.setShowRotateAlert({ status: false, degree: 0 }) props.setShowRotateAlert({ status: false, degree: 0 })
} }
type="button" type="button"
className="op-btn op-btn-ghost shadow-md" className="op-btn op-btn-ghost text-base-content shadow-md"
> >
{t("no")} {t("no")}
</button> </button>
+73 -11
View File
@@ -1,20 +1,82 @@
import { Helmet } from "react-helmet"; 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 = const appName =
"OpenSign™"; "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 ( return (
<Helmet> <Helmet>
<title>{drive ? title : `${title} - ${appName}`}</title> <title>{title}</title>
<meta name="description" content={`${title} - ${appName}`} /> <meta name="description" content={title} />
<link {logo && <link rel="icon" type="image/png" href={logo} />}
rel="icon" <link rel="manifest" href={manifestUrl} />
type="image/png"
href={localStorage.getItem("fev_Icon")}
sizes="40x40"
/>
</Helmet> </Helmet>
); );
} }
export default Title;
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import axios from "axios"; import axios from "axios";
import Parse from "parse"; import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery"; import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
@@ -168,25 +168,21 @@ const DashboardCard = (props) => {
} }
} }
setresponse(arr.length); setresponse(arr.length);
setLoading(false);
}); });
} else { } else {
await axios.get(url, { headers: headers }).then((res) => { await axios.get(url, { headers: headers }).then((res) => {
if (res.data.results.length > 0) { if (res?.data?.[props.Data.key]) {
setLoading(false); setresponse(parseInt(res.data[props.Data.key]));
if (props.Data.key !== "count") { } else if (res?.data?.results?.length > 0) {
setresponse(res.data.results[0][props.Data.key]); setresponse(res.data.results.length);
} else {
setresponse(res.data[props.Data.key]);
}
} else { } else {
setresponse(0); setresponse(0);
setLoading(false);
} }
}); });
} }
} catch (e) { } catch (e) {
console.error("Problem", e); console.error("Problem", e);
} finally {
setLoading(false); setLoading(false);
} }
} }
@@ -1,6 +1,6 @@
import React, { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import Parse from "parse"; import Parse from "parse";
import ReportTable from "../../primitives/GetReportDisplay"; import DocumentsReport from "../../reports/document/DocumentsReport";
import reportJson from "../../json/ReportJson"; import reportJson from "../../json/ReportJson";
import axios from "axios"; import axios from "axios";
import Loader from "../../primitives/Loader"; import Loader from "../../primitives/Loader";
@@ -198,7 +198,7 @@ function DashboardReport(props) {
) : ( ) : (
<> <>
{reportName ? ( {reportName ? (
<ReportTable <DocumentsReport
ReportName={reportName} ReportName={reportName}
List={List} List={List}
setList={setList} setList={setList}
@@ -215,9 +215,9 @@ function DashboardReport(props) {
isSearchResult={isSearchResult} isSearchResult={isSearchResult}
/> />
) : ( ) : (
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded"> <div className="flex items-center justify-center h-[100px] w-full bg-white rounded-box">
<div className="text-center"> <div className="text-center text-xl text-base-content">
<p className="text-xl text-base-content">{t("report-not-found")}</p> {t("report-not-found")}
</div> </div>
</div> </div>
)} )}
@@ -1,8 +1,9 @@
import React, { Suspense, lazy } from "react"; import React, { Suspense } from "react";
import { lazyWithRetry } from "../../utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const DashboardButton = lazy(() => import("./DashboardButton")); const DashboardButton = lazyWithRetry(() => import("./DashboardButton"));
const DashboardCard = lazy(() => import("./DashboardCard")); const DashboardCard = lazyWithRetry(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport")); const DashboardReport = lazyWithRetry(() => import("./DashboardReport"));
const buttonList = [ const buttonList = [
{ {
label: "Sign yourself", label: "Sign yourself",
@@ -71,11 +71,9 @@ function DriveBody(props) {
}); });
props.setPdfData(updatedData); props.setPdfData(updatedData);
props.sortingData(null, null, updatedData); props.sortingData(null, null, updatedData);
await axios try {
.put( await axios.put(
`${localStorage.getItem( `${localStorage.getItem("baseUrl")}classes/contracts_Document/${docId}`,
"baseUrl"
)}classes/contracts_Document/${docId}`,
updateName, updateName,
{ {
headers: { headers: {
@@ -84,18 +82,14 @@ function DriveBody(props) {
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
} }
} }
) );
.then(() => { } catch (err) {
// const res = result.data; console.log("Err ", err);
// console.log("res", res); props.setIsAlert({
}) isShow: true,
.catch((err) => { alertMessage: t("something-went-wrong-mssg")
console.log("Err ", err);
props.setIsAlert({
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}); });
}
} }
}; };
@@ -245,7 +239,6 @@ function DriveBody(props) {
) )
.then((Listdata) => { .then((Listdata) => {
// console.log("Listdata ", Listdata);
const res = Listdata.data; const res = Listdata.data;
if (res) { if (res) {
const updatedData = props.pdfData.filter( const updatedData = props.pdfData.filter(
@@ -366,14 +359,14 @@ function DriveBody(props) {
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" 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" /> <path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg> </svg>
<span className="text-[12px] font-medium">{data.Name}</span> <span className="text-[12px] font-medium">{data.Name}</span>
</td> </td>
<td>_</td> <td>_</td>
<td>Folder</td> <td>{t("folder")}</td>
<td>_</td> <td>_</td>
<td>_</td> <td>_</td>
</tr> </tr>
@@ -390,7 +383,7 @@ function DriveBody(props) {
<span className="text-[12px] font-medium">{data.Name}</span> <span className="text-[12px] font-medium">{data.Name}</span>
</td> </td>
<td>{createddate}</td> <td>{createddate}</td>
<td>Pdf</td> <td>{t("pdf")}</td>
<td>{t(`drive-document-status.${status}`)}</td> <td>{t(`drive-document-status.${status}`)}</td>
<td> <td>
<i <i
@@ -421,7 +414,7 @@ function DriveBody(props) {
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" 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" /> <path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg> </svg>
@@ -458,14 +451,14 @@ function DriveBody(props) {
className="ContextMenuItem" className="ContextMenuItem"
> >
<i className="fa-light fa-font mr-[8px]"></i> <i className="fa-light fa-font mr-[8px]"></i>
<span>Rename</span> <span>{t(`context-menu.Rename`)}</span>
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
onClick={() => handleMenuItemClick("Delete", data, data.Type)} onClick={() => handleMenuItemClick("Delete", data, data.Type)}
className="ContextMenuItem" className="ContextMenuItem"
> >
<i className="fa-light fa-trash mr-[8px]"></i> <i className="fa-light fa-trash mr-[8px]"></i>
<span>Delete</span> <span>{t(`context-menu.Delete`)}</span>
</ContextMenu.Item> </ContextMenu.Item>
</ContextMenu.Content> </ContextMenu.Content>
</ContextMenu.Portal> </ContextMenu.Portal>
@@ -550,7 +543,6 @@ function DriveBody(props) {
) )
)} )}
</div> </div>
<ContextMenu.Portal> <ContextMenu.Portal>
<ContextMenu.Content <ContextMenu.Content
className="ContextMenuContent" className="ContextMenuContent"
@@ -603,7 +595,6 @@ function DriveBody(props) {
{signersName()} {signersName()}
</> </>
)} )}
<HoverCard.Arrow className="HoverCardArrow" /> <HoverCard.Arrow className="HoverCardArrow" />
</HoverCard.Content> </HoverCard.Content>
</HoverCard.Portal> </HoverCard.Portal>
@@ -627,21 +618,21 @@ function DriveBody(props) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{props?.pdfData?.map((data, ind) => { {props?.pdfData?.map((data, ind) => (
return ( <React.Fragment key={ind}>
<React.Fragment key={ind}> {handleFolderData(data, ind, "table")}
{handleFolderData(data, ind, "table")} </React.Fragment>
</React.Fragment> ))}
);
})}
</tbody> </tbody>
</Table> </Table>
</div> </div>
) : ( ) : (
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]"> <div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
{props.pdfData.map((data, ind) => { {props?.pdfData?.map((data, ind) => (
return <React.Fragment key={ind}>{handleFolderData(data, ind, "list")}</React.Fragment>; <React.Fragment key={ind}>
})} {handleFolderData(data, ind, "list")}
</React.Fragment>
))}
</div> </div>
)} )}
@@ -33,7 +33,7 @@ const AddRoleModal = (props) => {
<button <button
onClick={props.handleCloseRoleModal} onClick={props.handleCloseRoleModal}
type="button" type="button"
className="op-btn op-btn-ghost ml-2" className="op-btn op-btn-ghost text-base-content ml-2"
> >
{t("close")} {t("close")}
</button> </button>
@@ -9,7 +9,6 @@ function AgreementContent(props) {
const h2Style = "text-base-content font-medium text-lg"; const h2Style = "text-base-content font-medium text-lg";
const ulStyle = "list-disc px-4 py-3"; const ulStyle = "list-disc px-4 py-3";
const handleOnclick = () => { const handleOnclick = () => {
props.setIsAgreeTour(false);
props.setIsAgree(true); props.setIsAgree(true);
props.setIsShowAgreeTerms(false); props.setIsShowAgreeTerms(false);
props.showFirstWidget(); props.showFirstWidget();
@@ -119,7 +118,7 @@ function AgreementContent(props) {
{t("agrre-button")} {t("agrre-button")}
</button> </button>
<button <button
className="op-btn op-btn-ghost" className="op-btn op-btn-ghost text-base-content"
onClick={() => props.setIsShowAgreeTerms(false)} onClick={() => props.setIsShowAgreeTerms(false)}
> >
{t("close")} {t("close")}
@@ -4,7 +4,6 @@ import AgreementContent from "./AgreementContent";
function AgreementSign(props) { function AgreementSign(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const [isChecked, setIsChecked] = useState(false);
const [isShowAgreeTerms, setIsShowAgreeTerms] = useState(false); const [isShowAgreeTerms, setIsShowAgreeTerms] = useState(false);
return ( return (
@@ -12,39 +11,12 @@ function AgreementSign(props) {
<div className="op-modal op-modal-open absolute z-[448]"> <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="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"> <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"> <div className="text-[11px] md:text-base text-base-content">
<span>{t("agree-p1")}</span> <span>{t("agree-p1")}</span>
<span <span
className="font-bold text-blue-600 cursor-pointer" className="font-bold text-blue-600 cursor-pointer"
onClick={() => { onClick={() => {
setIsShowAgreeTerms(true); setIsShowAgreeTerms(true);
props.setIsAgreeTour(false);
}} }}
> >
{t("agree-p2")} {t("agree-p2")}
@@ -52,15 +24,11 @@ function AgreementSign(props) {
<span> {t("agree-p3")}</span> <span> {t("agree-p3")}</span>
</div> </div>
</div> </div>
<div className="flex ml-[35px] mt-3"> <div className="flex mt-3">
<button <button
onClick={() => { onClick={() => {
if (isChecked) { props.setIsAgree(true);
props.setIsAgreeTour(false); props.showFirstWidget();
props.setIsAgree(true);
} else {
props.setIsAgreeTour(true);
}
}} }}
className="op-btn op-btn-primary op-btn-sm w-full md:w-auto" className="op-btn op-btn-primary op-btn-sm w-full md:w-auto"
> >
@@ -74,7 +42,6 @@ function AgreementSign(props) {
</div> </div>
{isShowAgreeTerms && ( {isShowAgreeTerms && (
<AgreementContent <AgreementContent
setIsAgreeTour={props.setIsAgreeTour}
setIsAgree={props.setIsAgree} setIsAgree={props.setIsAgree}
setIsShowAgreeTerms={setIsShowAgreeTerms} setIsShowAgreeTerms={setIsShowAgreeTerms}
showFirstWidget={props.showFirstWidget} showFirstWidget={props.showFirstWidget}
@@ -0,0 +1,155 @@
import { useRef, useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import { EmailBody } from "./EmailBody";
import {
contractDocument,
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(props?.defaultMail);
}}
>
<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"; import { useSelector } from "react-redux";
function DefaultSignature(props) { function DefaultSignature(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg); const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
const myInitial = useSelector((state) => state.widget.myInitial) const myInitial = useSelector((state) => state.widget.myInitial);
const tabName = ["my-signature", "my-initials"]; const tabName = ["my-signature", "my-initials"];
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const confirmToaddDefaultSign = (type) => { const confirmToaddDefaultSign = (type) => {
if (!props.isAgree) { if (props?.xyPosition.length > 0) {
props.setIsAgreeTour(true); //check signature or initial widgets exist or not for auto signing
} else { const getCurrentSignerXY = props?.xyPosition.filter(
if (props?.xyPosition.length > 0) { (data) => data.Id === props.uniqueId
//check signature or initial widgets exist or not for auto signing );
const getCurrentSignerXY = props?.xyPosition.filter( const checkIsSignInitialExist = getCurrentSignerXY?.every(
(data) => data.Id === props.uniqueId (placeholderObj) =>
); placeholderObj?.placeHolder?.some((placeholder) =>
const checkIsSignInitialExist = getCurrentSignerXY?.every( placeholder?.pos?.some((posItem) => posItem?.type === type)
(placeholderObj) => )
placeholderObj?.placeHolder?.some((placeholder) => );
placeholder?.pos?.some((posItem) => posItem?.type === type) if (checkIsSignInitialExist) {
) props?.setDefaultSignAlert({
); isShow: true,
if (checkIsSignInitialExist) { alertMessage: t("default-sign-alert", { widgetsType: type }),
props?.setDefaultSignAlert({ type: type
isShow: true, });
alertMessage: t("default-sign-alert", { widgetsType: type }),
type: type
});
} else {
props?.setDefaultSignAlert({
isShow: true,
alertMessage: t("defaultSign-alert", { widgetsType: type })
});
}
} else { } else {
props?.setDefaultSignAlert({ props?.setDefaultSignAlert({
isShow: true, 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 { widgets } from "../../constant/Utils";
import { getWidgetType, widgets } from "../../constant/Utils"; import getWidgetType from "./getWidgetType";
function DragElement(item) { function DragElement(item) {
const getWidgets = widgets; const getWidgets = widgets;
@@ -21,6 +21,7 @@ function DropdownWidgetOption(props) {
const [layout, setLayout] = useState("vertical"); const [layout, setLayout] = useState("vertical");
const statusArr = ["required", "optional"]; const statusArr = ["required", "optional"];
const layoutArr = ["vertical", "horizontal"]; const layoutArr = ["vertical", "horizontal"];
const isPrefillExist = props?.roleName === "prefill";
const resetState = () => { const resetState = () => {
setDropdownOptionList(["Option-1", "Option-2"]); setDropdownOptionList(["Option-1", "Option-2"]);
@@ -87,7 +88,7 @@ function DropdownWidgetOption(props) {
const deleteOption = true; const deleteOption = true;
const addOption = false; const addOption = false;
const getUpdatedOptions = dropdownOptionList.filter( const getUpdatedOptions = dropdownOptionList.filter(
(data, index) => index !== ind (_, index) => index !== ind
); );
setDropdownOptionList(getUpdatedOptions); setDropdownOptionList(getUpdatedOptions);
props.handleSaveWidgetsOptions( props.handleSaveWidgetsOptions(
@@ -102,6 +103,14 @@ function DropdownWidgetOption(props) {
}; };
const handleSaveOption = () => { 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 = const defaultData =
defaultCheckbox && defaultCheckbox.length > 0 defaultCheckbox && defaultCheckbox.length > 0
? defaultCheckbox ? defaultCheckbox
@@ -228,37 +237,40 @@ function DropdownWidgetOption(props) {
></i> ></i>
</div> </div>
))} ))}
<i <div>
onClick={handleAddInput} <i
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer" className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
></i> aria-label="Add option"
onClick={handleAddInput}
></i>
</div>
</div> </div>
{["dropdown", radioButtonWidget].includes(props.type) && ( {["dropdown", radioButtonWidget].includes(props.type) &&
<> !isPrefillExist && (
<label className="text-[13px] font-semibold mt-[5px]"> <>
{t("default-value")} <label className="text-[13px] font-semibold mt-[5px]">
</label> {t("default-value")}
<select </label>
onChange={(e) => setDefaultValue(e.target.value)} <select
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs" value={defaultValue}
name="defaultvalue" onChange={(e) => setDefaultValue(e.target.value)}
value={defaultValue} className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder="select default value" name="defaultvalue"
> >
<option value="" disabled hidden className="text-[13px]"> <option value="" disabled hidden className="text-[13px]">
{t("select")}... {t("select")}...
</option> </option>
{dropdownOptionList.map((data, ind) => { {dropdownOptionList.map((data, ind) => {
return ( return (
<option className="text-[13px]" key={ind} value={data}> <option className="text-[13px]" key={ind} value={data}>
{data} {data}
</option> </option>
); );
})} })}
</select> </select>
</> </>
)} )}
{props.type !== "checkbox" && ( {props.type !== "checkbox" && !isPrefillExist && (
<div className="flex flex-row gap-[10px] mt-[0.5rem]"> <div className="flex flex-row gap-[10px] mt-[0.5rem]">
{statusArr.map((data, ind) => ( {statusArr.map((data, ind) => (
<div <div
@@ -332,7 +344,7 @@ function DropdownWidgetOption(props) {
props.type props.type
) && ( ) && (
<div className="flex flex-row gap-5 my-2 items-center text-center"> <div className="flex flex-row gap-5 my-2 items-center text-center">
{props.isShowAdvanceFeature && ( {props.isShowAdvanceFeature && !isPrefillExist && (
<div className="flex items-center"> <div className="flex items-center">
<input <input
id="isreadonly" id="isreadonly"
@@ -416,7 +428,7 @@ function DropdownWidgetOption(props) {
{props.currWidgetsDetails?.options?.values?.length > 0 && ( {props.currWidgetsDetails?.options?.values?.length > 0 && (
<button <button
type="submit" type="submit"
className="op-btn op-btn-ghost ml-1" className="op-btn op-btn-ghost text-base-content ml-1"
onClick={() => { onClick={() => {
props.handleClose && props.handleClose(); props.handleClose && props.handleClose();
resetState(); resetState();
@@ -28,6 +28,7 @@ const EditTemplate = ({
onSuccess, onSuccess,
setPdfArrayBuffer, setPdfArrayBuffer,
setPdfBase64Url, setPdfBase64Url,
isAddYourSelfCheckbox,
}) => { }) => {
const appName = const appName =
"OpenSign™"; "OpenSign™";
@@ -51,7 +52,7 @@ const EditTemplate = ({
Bcc: template?.Bcc, Bcc: template?.Bcc,
RedirectUrl: template?.RedirectUrl || "", RedirectUrl: template?.RedirectUrl || "",
AllowModifications: template?.AllowModifications || false, AllowModifications: template?.AllowModifications || false,
TimeToCompleteDays: template?.TimeToCompleteDays || 15 TimeToCompleteDays: template?.TimeToCompleteDays || 15,
}); });
const [isUpdate, setIsUpdate] = useState(false); const [isUpdate, setIsUpdate] = useState(false);
const [showConfirm, setShowConfirm] = useState(false); const [showConfirm, setShowConfirm] = useState(false);
@@ -71,7 +72,7 @@ const EditTemplate = ({
handleReplaceFileValdition(file); handleReplaceFileValdition(file);
// You can handle the file here // You can handle the file here
} else { } else {
alert("Only pdf files are allowed."); alert(t("only-pdf-allowed"));
if (inputFileRef.current) inputFileRef.current.value = ""; if (inputFileRef.current) inputFileRef.current.value = "";
} }
}; };
@@ -181,7 +182,8 @@ const EditTemplate = ({
localStorage.getItem("TenantId") || localStorage.getItem("TenantId") ||
template?.ExtUserPtr?.TenantId?.objectId; template?.ExtUserPtr?.TenantId?.objectId;
const buffer = atob(uploadPdf.base64); 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 isChecked = formData.SendinOrder === "true" ? true : false;
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true; const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
@@ -242,6 +244,7 @@ const EditTemplate = ({
setShowConfirm(false); setShowConfirm(false);
handleClose(); handleClose();
}; };
return ( return (
<ModalUi <ModalUi
isOpen isOpen
@@ -286,7 +289,7 @@ const EditTemplate = ({
htmlFor="fileUpload" htmlFor="fileUpload"
className="cursor-pointer text-center mb-0" 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> </label>
</div> </div>
<input <input
@@ -490,10 +493,11 @@ const EditTemplate = ({
helptextZindex={50} helptextZindex={50}
helpText={t("bcc-help")} helpText={t("bcc-help")}
isCaptureAllData isCaptureAllData
isAddYourSelfCheckbox={isAddYourSelfCheckbox}
/> />
</div> </div>
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block">Redirect Url</label> <label className="block">{t("redirect-url")}</label>
<input <input
name="RedirectUrl" name="RedirectUrl"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -18,7 +18,13 @@ export function EmailBody(props) {
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))} onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
value={props.requestSubject} 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}"' 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" 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); setIsLoading(true);
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList }; const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
const sendmail = await Parse.Cloud.run("forwarddoc", params); const sendmail = await Parse.Cloud.run("forwarddoc", params);
console.log("sendmail ", sendmail);
if (sendmail?.status === "success") { if (sendmail?.status === "success") {
setSuccessEmail(true); setSuccessEmail(true);
setIsEmail(false); setIsEmail(false);
@@ -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> </span>
<div className="flex gap-1"> <div className="flex gap-1">
<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 === 0} disabled={i === 0}
onClick={() => move(i, -1)} onClick={() => move(i, -1)}
> >
<i className="fa-light fa-arrow-up"></i> <i className="fa-light fa-arrow-up"></i>
</button> </button>
<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} disabled={i === order.length - 1}
onClick={() => move(i, 1)} onClick={() => move(i, 1)}
> >
@@ -84,7 +84,7 @@ export default function PageReorderModal({
<button <button
onClick={handleClose} onClick={handleClose}
type="button" type="button"
className="op-btn op-btn-ghost ml-1" className="op-btn op-btn-ghost text-base-content ml-1"
> >
{t("close")} {t("close")}
</button> </button>
+116 -143
View File
@@ -23,9 +23,6 @@ import { maxFileSize } from "../../constant/const";
function Header(props) { function Header(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const filterPrefill =
props?.signerPos &&
props?.signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const [isDownloading, setIsDownloading] = useState(""); const [isDownloading, setIsDownloading] = useState("");
const [isDeletePage, setIsDeletePage] = useState(false); const [isDeletePage, setIsDeletePage] = useState(false);
@@ -71,17 +68,19 @@ function Header(props) {
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) { if (!file) {
alert("Please upload a valid PDF file."); alert(t("please-select-pdf"));
return; return;
} }
if (!file.type.includes("pdf")) { if (!file.type.includes("pdf")) {
alert("Only PDF files are allowed."); alert(t("only-pdf-allowed"));
return; return;
} }
const fileSize =
const mb = Math.round(file?.size / Math.pow(1024, 2)); maxFileSize;
if (mb > maxFileSize) { const pdfsize = file?.size;
alert(`${t("file-alert-1")} ${maxFileSize} MB`); const fileSizeBytes = fileSize * 1024 * 1024;
if (pdfsize > fileSizeBytes) {
alert(`${t("file-alert-1")} ${fileSize} MB`);
removeFile(e); removeFile(e);
return; return;
} }
@@ -108,18 +107,18 @@ function Header(props) {
// Upload the file to Parse Server // Upload the file to Parse Server
} catch (err) { } catch (err) {
console.error("Incorrect password or decryption failed", err); console.error("Incorrect password or decryption failed", err);
alert("Incorrect password or decryption failed."); alert(t("incorrect-password-or-decryption-failed"));
} }
} else { } else {
alert("Please provided Password."); alert(t("provide-password"));
} }
} else { } else {
console.log("Err ", err); console.log("Err ", err);
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
} else { } else {
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -138,6 +137,13 @@ function Header(props) {
useObjectStreams: false useObjectStreams: false
}); });
const pdfBuffer = base64ToArrayBuffer(pdfBase64); 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.setPdfArrayBuffer(pdfBuffer);
props.setPdfBase64Url(pdfBase64); props.setPdfBase64Url(pdfBase64);
props.setIsUploadPdf && props.setIsUploadPdf(true); props.setIsUploadPdf && props.setIsUploadPdf(true);
@@ -162,6 +168,23 @@ function Header(props) {
} }
setIsReorderModal(false); 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 ( return (
<div className="flex py-[5px]"> <div className="flex py-[5px]">
{isMobile && props?.isShowHeader ? ( {isMobile && props?.isShowHeader ? (
@@ -201,17 +224,7 @@ function Header(props) {
> >
<DropdownMenu.Item <DropdownMenu.Item
className="DropdownMenuItem" className="DropdownMenuItem"
onClick={() => { onClick={() => handleDownloadBtn()}
if (props?.isCompleted) {
props?.setIsDownloadModal(true);
} else {
handleDownloadPdf(
props?.pdfDetails,
setIsDownloading,
props.pdfBase64
);
}
}}
> >
<div className="flex flex-row"> <div className="flex flex-row">
<i <i
@@ -221,25 +234,27 @@ function Header(props) {
{t("download")} {t("download")}
</div> </div>
</DropdownMenu.Item> </DropdownMenu.Item>
{props?.isCompleted && ( {
<DropdownMenu.Item props?.isCompleted && (
className="DropdownMenuItem" <DropdownMenu.Item
onClick={() => className="DropdownMenuItem"
handleDownloadCertificate( onClick={() =>
props?.pdfDetails, handleDownloadCertificate(
setIsDownloading props?.pdfDetails,
) setIsDownloading
} )
> }
<div className="border-none bg-[#fff]"> >
<i <div className="border-none bg-[#fff]">
className="fa-light fa-award mr-[3px]" <i
aria-hidden="true" className="fa-light fa-award mr-[3px]"
></i> aria-hidden="true"
{t("certificate")} ></i>
</div> {t("certificate")}
</DropdownMenu.Item> </div>
)} </DropdownMenu.Item>
)
}
{props?.isSignYourself && ( {props?.isSignYourself && (
<DropdownMenu.Item <DropdownMenu.Item
className="DropdownMenuItem" className="DropdownMenuItem"
@@ -290,7 +305,7 @@ function Header(props) {
<div <div
onClick={() => { onClick={() => {
if (!props?.isMailSend) { if (!props?.isMailSend) {
props?.alertSendEmail(); props?.handleSaveDoc();
} }
}} }}
className={`${ className={`${
@@ -348,13 +363,7 @@ function Header(props) {
)} )}
<DropdownMenu.Item <DropdownMenu.Item
className="DropdownMenuItem" className="DropdownMenuItem"
onClick={() => onClick={() => handleDownloadDoc()}
handleDownloadPdf(
props?.pdfDetails,
setIsDownloading,
props.pdfBase64
)
}
> >
<div className="flex flex-row"> <div className="flex flex-row">
<i <i
@@ -474,42 +483,21 @@ function Header(props) {
/> />
{props?.isPlaceholder ? ( {props?.isPlaceholder ? (
<> <>
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none"> <div className="flex mx-[100px] lg:mx-0 order-last lg:order-none"></div>
{!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"> <div className="flex">
{props?.setIsEditTemplate && ( {props?.setIsEditTemplate && (
<button <button
onClick={() => props?.setIsEditTemplate(true)} onClick={() => props?.setIsEditTemplate(true)}
className="outline-none border-none text-center mr-[3px]" 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> </button>
)} )}
{enabledBackBtn && ( {enabledBackBtn && (
<button <button
onClick={() => window.history.go(-2)} onClick={() => window.history.go(-2)}
type="button" 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")} {t("back")}
</button> </button>
@@ -518,7 +506,7 @@ function Header(props) {
disabled={props?.isMailSend && true} disabled={props?.isMailSend && true}
data-tut="headerArea" data-tut="headerArea"
className="op-btn op-btn-primary op-btn-sm mr-[3px]" className="op-btn op-btn-primary op-btn-sm mr-[3px]"
onClick={() => props?.alertSendEmail()} onClick={() => props?.handleSaveDoc()}
> >
{props?.completeBtnTitle {props?.completeBtnTitle
? props?.completeBtnTitle ? props?.completeBtnTitle
@@ -544,38 +532,32 @@ function Header(props) {
></i> ></i>
<span className="hidden lg:block">{t("print")}</span> <span className="hidden lg:block">{t("print")}</span>
</button> </button>
{props?.isCompleted && ( {
<button props?.isCompleted && (
type="button" <button
onClick={() => type="button"
handleDownloadCertificate( onClick={() =>
props?.pdfDetails, handleDownloadCertificate(
setIsDownloading props?.pdfDetails,
) setIsDownloading
} )
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow" }
> className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
<i >
className="fa-light fa-award py-[3px]" <i
aria-hidden="true" className="fa-light fa-award py-[3px]"
></i> aria-hidden="true"
<span className="hidden lg:block">{t("certificate")}</span> ></i>
</button> <span className="hidden lg:block">
)} {t("certificate")}
</span>
</button>
)
}
<button <button
type="button" type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow" className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
onClick={() => { onClick={() => handleDownloadBtn()}
if (props?.isCompleted) {
props?.setIsDownloadModal(true);
} else {
handleDownloadPdf(
props?.pdfDetails,
setIsDownloading,
props.pdfBase64
);
}
}}
> >
<i <i
className="fa-light fa-download py-[3px]" className="fa-light fa-download py-[3px]"
@@ -590,15 +572,9 @@ function Header(props) {
<> <>
{props?.templateId && ( {props?.templateId && (
<button <button
onClick={() => onClick={() => handleDownloadDoc()}
handleDownloadPdf(
props?.pdfDetails,
setIsDownloading,
props.pdfBase64
)
}
type="button" 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> <span className="hidden lg:block">{t("download")}</span>
</button> </button>
@@ -614,14 +590,8 @@ function Header(props) {
{!props?.templateId && ( {!props?.templateId && (
<button <button
type="button" 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]"
onClick={() => onClick={() => handleDownloadDoc()}
handleDownloadPdf(
props?.pdfDetails,
setIsDownloading,
props.pdfBase64
)
}
> >
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i> <i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
<span className="hidden lg:block">{t("download")}</span> <span className="hidden lg:block">{t("download")}</span>
@@ -640,23 +610,25 @@ function Header(props) {
) )
) : props?.isCompleted ? ( ) : props?.isCompleted ? (
<div className="flex flex-row"> <div className="flex flex-row">
{props?.isCompleted && ( {
<button props?.isCompleted && (
type="button" <button
onClick={() => type="button"
handleDownloadCertificate( onClick={() =>
props?.pdfDetails, handleDownloadCertificate(
setIsDownloading props?.pdfDetails,
) setIsDownloading
} )
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow" }
> 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"> <i className="fa-light fa-award" aria-hidden="true"></i>
{t("certificate")} <span className="hidden lg:block ml-1">
</span> {t("certificate")}
</button> </span>
)} </button>
)
}
<button <button
onClick={(e) => onClick={(e) =>
handleToPrint(e, setIsDownloading, props?.pdfDetails) handleToPrint(e, setIsDownloading, props?.pdfDetails)
@@ -670,7 +642,8 @@ function Header(props) {
<button <button
type="button" type="button"
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow" 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> <i className="fa-light fa-download" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">{t("download")}</span> <span className="hidden lg:block ml-1">{t("download")}</span>
@@ -699,7 +672,7 @@ function Header(props) {
<button <button
onClick={() => window.history.go(-2)} onClick={() => window.history.go(-2)}
type="button" 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")} {t("back")}
</button> </button>
@@ -744,8 +717,8 @@ function Header(props) {
handleClose={() => setIsDeletePage(false)} handleClose={() => setIsDeletePage(false)}
> >
<div className="h-[100%] p-[20px]"> <div className="h-[100%] p-[20px]">
<p className="font-medium">{t("delete-alert-2")}</p> <p className="font-medium text-base-content">{t("delete-alert-2")}</p>
<p className="pt-3">{t("delete-note")}</p> <p className="pt-3 text-base-content">{t("delete-note")}</p>
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div> <div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
<button <button
onClick={() => handleDetelePage()} onClick={() => handleDetelePage()}
@@ -757,7 +730,7 @@ function Header(props) {
<button <button
onClick={() => setIsDeletePage(false)} onClick={() => setIsDeletePage(false)}
type="button" type="button"
className="op-btn op-btn-ghost" className="op-btn op-btn-ghost text-base-content"
> >
{t("no")} {t("no")}
</button> </button>
@@ -14,7 +14,7 @@ import { PDFDocument } from "pdf-lib";
import { maxFileSize } from "../../constant/const"; import { maxFileSize } from "../../constant/const";
import PageReorderModal from "./PageReorderModal"; import PageReorderModal from "./PageReorderModal";
function PdfZoom(props) { function PdfTools(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const mergePdfInputRef = useRef(null); const mergePdfInputRef = useRef(null);
const [isDeletePage, setIsDeletePage] = useState(false); const [isDeletePage, setIsDeletePage] = useState(false);
@@ -57,18 +57,22 @@ function PdfZoom(props) {
}; };
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
props.setIsTour && props.setIsTour(false);
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) { if (!file) {
alert("Please upload a valid PDF file."); alert(t("please-select-pdf"));
return; return;
} }
if (!file.type.includes("pdf")) { if (!file.type.includes("pdf")) {
alert("Only PDF files are allowed."); alert(t("only-pdf-allowed"));
return; return;
} }
const mb = Math.round(file?.size / Math.pow(1024, 2)); const fileSize =
if (mb > maxFileSize) { maxFileSize;
alert(`${t("file-alert-1")} ${maxFileSize} MB`); const pdfsize = file?.size;
const fileSizeBytes = fileSize * 1024 * 1024;
if (pdfsize > fileSizeBytes) {
alert(`${t("file-alert-1")} ${fileSize} MB`);
removeFile(e); removeFile(e);
return; return;
} }
@@ -95,18 +99,18 @@ function PdfZoom(props) {
// Upload the file to Parse Server // Upload the file to Parse Server
} catch (err) { } catch (err) {
console.error("Incorrect password or decryption failed", err); console.error("Incorrect password or decryption failed", err);
alert("Incorrect password or decryption failed."); alert(t("incorrect-password-or-decryption-failed"));
} }
} else { } else {
alert("Please provided Password."); alert(t("provide-password"));
} }
} else { } else {
console.log("Err ", err); console.log("Err ", err);
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
} else { } else {
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -125,6 +129,13 @@ function PdfZoom(props) {
useObjectStreams: false useObjectStreams: false
}); });
const pdfBuffer = base64ToArrayBuffer(pdfBase64); 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.setPdfArrayBuffer(pdfBuffer);
props.setPdfBase64Url(pdfBase64); props.setPdfBase64Url(pdfBase64);
props.setIsUploadPdf && props.setIsUploadPdf(true); props.setIsUploadPdf && props.setIsUploadPdf(true);
@@ -150,13 +161,42 @@ function PdfZoom(props) {
setIsReorderModal(false); 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 ( 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 && ( {!props.isDisableEditTools && (
<> <>
<span <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()} onClick={() => mergePdfInputRef.current.click()}
title={t("add-pages")} title={t("add-pages")}
> >
@@ -170,15 +210,15 @@ function PdfZoom(props) {
<i className="fa-light fa-plus text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-plus text-gray-500 2xl:text-[25px]"></i>
</span> </span>
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
onClick={() => setIsDeletePage(true)} onClick={handleDeletePage}
title={t("delete-page")} title={t("delete-page")}
> >
<i className="fa-light fa-trash text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-trash text-gray-500 2xl:text-[25px]"></i>
</span> </span>
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
onClick={() => setIsReorderModal(true)} onClick={handleReorderPages}
title={t("reorder-pages")} title={t("reorder-pages")}
> >
<i className="fa-light fa-list-ol text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-list-ol text-gray-500 2xl:text-[25px]"></i>
@@ -187,7 +227,7 @@ function PdfZoom(props) {
)} )}
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
onClick={() => props.clickOnZoomIn()} onClick={handleZoomIn}
title={t("zoom-in")} title={t("zoom-in")}
> >
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[25px]"></i>
@@ -197,7 +237,7 @@ function PdfZoom(props) {
<> <>
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
onClick={() => props.handleRotationFun(90)} onClick={handleRotate}
title={t("rotate-right")} title={t("rotate-right")}
> >
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-rotate-right text-gray-500 2xl:text-[25px]"></i>
@@ -205,18 +245,15 @@ function PdfZoom(props) {
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
title={t("rotate-left")} title={t("rotate-left")}
onClick={() => props.handleRotationFun(-90)} onClick={handleAntiRotate}
> >
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[25px]"></i> <i className="fa-light fa-rotate-left text-gray-500 2xl:text-[25px]"></i>
</span> </span>
</> </>
)} )}
<span <span
className="bg-gray-50 px-[4px] 2xl:py-[10px]" className="bg-gray-50 px-[4px] 2xl:py-[10px] cursor-pointer"
onClick={() => props.clickOnZoomOut()} onClick={handleZoomOut}
style={{
cursor: props.zoomPercent > 0 ? "pointer" : "default"
}}
title={t("zoom-out")} title={t("zoom-out")}
> >
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i> <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)} handleClose={() => setIsDeletePage(false)}
> >
<div className="h-[100%] p-[20px]"> <div className="h-[100%] p-[20px]">
<p className="font-medium">{t("delete-alert-2")}</p> <p className="font-medium text-base-content">{t("delete-alert-2")}</p>
<p className="pt-3">{t("delete-note")}</p> <p className="pt-3 text-base-content">{t("delete-note")}</p>
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div> <div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
<button <button
onClick={() => handleDetelePage()} onClick={() => handleDetelePage()}
@@ -242,7 +279,7 @@ function PdfZoom(props) {
<button <button
onClick={() => setIsDeletePage(false)} onClick={() => setIsDeletePage(false)}
type="button" type="button"
className="op-btn op-btn-ghost ml-1" className="op-btn op-btn-ghost text-base-content ml-1"
> >
{t("no")} {t("no")}
</button> </button>
@@ -258,4 +295,4 @@ function PdfZoom(props) {
); );
} }
export default PdfZoom; export default PdfTools;
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,6 @@
import React, { useState } from "react"; import { useState } from "react";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { import { handleCopyNextToWidget, randomId } from "../../constant/Utils";
handleCopyNextToWidget,
randomId,
textWidget
} from "../../constant/Utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function PlaceholderCopy(props) { function PlaceholderCopy(props) {
@@ -125,7 +121,13 @@ function PlaceholderCopy(props) {
); );
for (let i = 0; i < props.allPages; i++) { for (let i = 0; i < props.allPages; i++) {
const newId = randomId(); 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 //get exist placeholder position for particular page
const existPlaceholder = filterSignerPosition[0].placeHolder.filter( const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
(data) => data.pageNumber === newPageNumber (data) => data.pageNumber === newPageNumber
@@ -213,6 +215,7 @@ function PlaceholderCopy(props) {
//function for getting selected type placeholder copy //function for getting selected type placeholder copy
const handleApplyCopy = () => { const handleApplyCopy = () => {
const newId = randomId();
if (selectCopyType === 4) { if (selectCopyType === 4) {
const signerPosition = props.xyPosition; const signerPosition = props.xyPosition;
let currentXYposition; let currentXYposition;
@@ -230,8 +233,8 @@ function PlaceholderCopy(props) {
); );
//function to create new widget next to just widget //function to create new widget next to just widget
handleCopyNextToWidget( handleCopyNextToWidget(
newId,
currentXYposition, currentXYposition,
props.widgetType,
props.xyPosition, props.xyPosition,
props.pageNumber, props.pageNumber,
props.setXyPosition, props.setXyPosition,
@@ -248,8 +251,8 @@ function PlaceholderCopy(props) {
); );
//function to create new widget next to just widget //function to create new widget next to just widget
handleCopyNextToWidget( handleCopyNextToWidget(
newId,
currentXYposition, currentXYposition,
props.widgetType,
props.xyPosition, props.xyPosition,
getIndex, getIndex,
props.setXyPosition props.setXyPosition
@@ -260,11 +263,6 @@ function PlaceholderCopy(props) {
} }
}; };
const handleUniqueId = () => { 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); props.setIsPageCopy(false);
setSelectCopyType(1); setSelectCopyType(1);
}; };
@@ -306,7 +304,7 @@ function PlaceholderCopy(props) {
</button> </button>
<button <button
type="button" type="button"
className="op-btn op-btn-ghost ml-2" className="op-btn op-btn-ghost text-base-content ml-2"
onClick={() => handleUniqueId()} onClick={() => handleUniqueId()}
> >
{t("cancel")} {t("cancel")}
@@ -1,4 +1,4 @@
import React, { useEffect, useState, forwardRef } from "react"; import { useEffect, useMemo, useState, forwardRef } from "react";
import { import {
getMonth, getMonth,
getYear, getYear,
@@ -9,29 +9,36 @@ import {
months, months,
years, years,
selectCheckbox, selectCheckbox,
checkRegularExpress checkRegularExpress,
isBase64,
getSignerPages
} from "../../constant/Utils"; } from "../../constant/Utils";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css"; import "../../styles/signature.css";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CellsWidget from "./CellsWidget"; import CellsWidget from "./CellsWidget";
import { useSelector } from "react-redux";
import Loader from "../../primitives/Loader";
const textWidgetCls = const textWidgetCls =
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap"; "w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap";
const selectWidgetCls =
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
const widgetCls = const widgetCls =
"select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center"; "select-none-cls overflow-hidden w-full h-full text-black flex flex-col justify-center items-center";
function PlaceholderType(props) { function PlaceholderType(props) {
const selectWidgetCls = `w-full h-full absolute left-0 top-0 focus:outline-none text-base-content`;
const { t } = useTranslation(); const { t } = useTranslation();
const prefillImg = useSelector((state) => state.widget.prefillImg);
const prefillImgLoad = useSelector((state) => state.widget.prefillImgLoad);
const type = props?.pos?.type; const type = props?.pos?.type;
const iswidgetEnable = const iswidgetEnable =
props.isSignYourself || props.isSignYourself ||
((props.isSelfSign || props.isNeedSign) && ((props.isSelfSign || props.isNeedSign) &&
props.data?.signerObjId === props.signerObjId); props.data?.signerObjId === props.signerObjId);
const isReadOnly = const isReadOnly =
props.pos.options?.isReadOnly || props?.data?.Role !== "prefill" &&
props.data?.signerObjId !== props.signerObjId; (props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId);
// prefer the latest response value over any default value // prefer the latest response value over any default value
const widgetData = const widgetData =
props.pos?.options?.response ?? props.pos?.options?.defaultValue ?? ""; props.pos?.options?.response ?? props.pos?.options?.defaultValue ?? "";
@@ -39,6 +46,7 @@ function PlaceholderType(props) {
const [widgetValue, setwidgetValue] = useState(); const [widgetValue, setwidgetValue] = useState();
const [selectedCheckbox, setSelectedCheckbox] = useState([]); const [selectedCheckbox, setSelectedCheckbox] = useState([]);
const [hint, setHint] = useState(""); const [hint, setHint] = useState("");
const [imgUrl, setImgUrl] = useState("");
const fontSize = props.calculateFont(props.pos.options?.fontSize); const fontSize = props.calculateFont(props.pos.options?.fontSize);
const fontColor = props.pos.options?.fontColor || "black"; const fontColor = props.pos.options?.fontColor || "black";
const textWidgetStyle = { const textWidgetStyle = {
@@ -51,7 +59,6 @@ function PlaceholderType(props) {
display: "flex", display: "flex",
height: "100%" height: "100%"
}; };
useEffect(() => { useEffect(() => {
if (type !== "date") { if (type !== "date") {
if (type && type === "checkbox") { if (type && type === "checkbox") {
@@ -60,18 +67,21 @@ function PlaceholderType(props) {
props?.pos?.options?.defaultValue || props?.pos?.options?.defaultValue ||
[] []
); );
} else { }
else {
// keep displayed value in sync with the stored response // keep displayed value in sync with the stored response
setwidgetValue(widgetData); setwidgetValue(widgetData);
} }
if (props.pos?.options?.hint) { if (props.pos?.options?.hint) {
setHint(props.pos?.options.hint); setHint(props.pos?.options.hint);
} else if (props.pos?.options?.validation?.type) { } else if (props.pos?.options?.validation?.type) {
checkRegularExpress(props.pos?.options?.validation?.type, setHint); checkRegularExpress(props.pos?.options?.validation?.type, setHint);
} }
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos]); }, [props.pos, widgetData, type]);
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => ( const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div <div
style={{ style={{
@@ -79,8 +89,7 @@ function PlaceholderType(props) {
color: fontColor, color: fontColor,
fontFamily: "Arial, sans-serif" fontFamily: "Arial, sans-serif"
}} }}
className={`${isReadOnly ? `select-none` : ``} ${selectWidgetCls} overflow-hidden`} className={`${isReadOnly ? `select-none opacity-25` : ``} ${selectWidgetCls} overflow-hidden`}
disabled={isReadOnly}
onClick={onClick} onClick={onClick}
ref={ref} ref={ref}
> >
@@ -100,6 +109,26 @@ function PlaceholderType(props) {
return false; 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) { switch (type) {
case "signature": case "signature":
@@ -108,7 +137,7 @@ function PlaceholderType(props) {
alt="signature" alt="signature"
draggable="false" draggable="false"
src={props.pos.SignUrl} 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}> <div className={widgetCls}>
@@ -132,7 +161,7 @@ function PlaceholderType(props) {
alt="stamp" alt="stamp"
draggable="false" draggable="false"
src={props.pos.SignUrl} 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}> <div className={widgetCls}>
@@ -154,9 +183,9 @@ function PlaceholderType(props) {
const checkBoxLayout = props.pos.options?.layout || "vertical"; const checkBoxLayout = props.pos.options?.layout || "vertical";
const isMultipleCheckbox = const isMultipleCheckbox =
props.pos.options?.values?.length > 0 ? true : false; props.pos.options?.values?.length > 0 ? true : false;
const checkBoxWrapperClass = `flex items-start ${ const checkBoxWrapperClass = `flex items-start whitespace-pre-wrap ${
checkBoxLayout === "horizontal" 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]" : ""}` : `flex-col ${isMultipleCheckbox ? "gap-y-[3px]" : ""}`
}`; // Using gap-y-1 for consistency, adjust if needed }`; // Using gap-y-1 for consistency, adjust if needed
@@ -229,7 +258,7 @@ function PlaceholderType(props) {
height={height} height={height}
value={cells.join("")} value={cells.join("")}
editable={isEditable} editable={isEditable}
resizable={isEditable} resizable={props?.isAllowModify}
fontSize={fontSize} fontSize={fontSize}
fontColor={fontColor} fontColor={fontColor}
hint={hint} hint={hint}
@@ -253,7 +282,7 @@ function PlaceholderType(props) {
alt="initials" alt="initials"
draggable="false" draggable="false"
src={props.pos.SignUrl} 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}> <div className={widgetCls}>
@@ -338,7 +367,7 @@ function PlaceholderType(props) {
</div> </div>
); );
case "date": case "date":
return iswidgetEnable ? ( return iswidgetEnable || props?.data?.Role === "prefill" ? (
<DatePicker <DatePicker
renderCustomHeader={({ date, changeYear, changeMonth }) => ( renderCustomHeader={({ date, changeYear, changeMonth }) => (
<div className="flex justify-start ml-2 "> <div className="flex justify-start ml-2 ">
@@ -375,11 +404,9 @@ function PlaceholderType(props) {
popperPlacement="top-end" popperPlacement="top-end"
customInput={<ExampleCustomInput />} customInput={<ExampleCustomInput />}
dateFormat={ dateFormat={
props.selectDate props?.selectDate?.format ||
? props.selectDate?.format props.pos?.options?.validation?.format ||
: props.pos?.options?.validation?.format "MM/dd/yyyy"
? props.pos?.options?.validation?.format
: "MM/dd/yyyy"
} }
/> />
) : ( ) : (
@@ -388,21 +415,23 @@ function PlaceholderType(props) {
className="select-none-cls overflow-hidden" className="select-none-cls overflow-hidden"
> >
<span> <span>
{props.selectDate {props?.selectDate?.format ||
? props.selectDate?.format props.pos?.options?.validation?.format ||
: props.pos?.options?.validation?.format "MM/dd/yyyy"}
? props.pos?.options?.validation?.format
: "MM/dd/yyyy"}
</span> </span>
</div> </div>
); );
case "image": 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 <img
alt="image" alt="image"
draggable="false" draggable="false"
src={props.pos.SignUrl} src={imgUrl}
className="w-full h-full select-none-cls" className="w-full h-full select-none-cls object-contain"
/> />
) : ( ) : (
<div className={widgetCls}> <div className={widgetCls}>
@@ -445,9 +474,9 @@ function PlaceholderType(props) {
case radioButtonWidget: case radioButtonWidget:
const radioLayout = props.pos.options?.layout || "vertical"; const radioLayout = props.pos.options?.layout || "vertical";
const isOnlyOneBtn = props.pos.options?.values?.length > 0 ? true : false; 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" 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]" : ""}` : `flex-col ${isOnlyOneBtn ? "gap-y-[5px]" : ""}`
}`; // Using gap-y-1 for consistency, adjust if needed }`; // Using gap-y-1 for consistency, adjust if needed
return ( 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} onClick={previousPage}
> >
<span className="block"> <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> </span>
</button> </button>
<span className="text-xs text-base-content font-medium mx-2 2xl:text-[20px]"> <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} onClick={nextPage}
> >
<span className="block"> <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> </span>
</button> </button>
</div> </div>
@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react"; import { useRef, useState } from "react";
import { useAutoAnimate } from "@formkit/auto-animate/react"; import { useAutoAnimate } from "@formkit/auto-animate/react";
import { import {
color, color,
@@ -12,6 +12,7 @@ const cursor =
const RecipientList = (props) => { const RecipientList = (props) => {
const [animationParent] = useAutoAnimate(); const [animationParent] = useAutoAnimate();
const [isHover, setIsHover] = useState(); const [isHover, setIsHover] = useState();
const [isPrefill, setIsPrefill] = useState(false);
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
//function for onhover signer name change background color //function for onhover signer name change background color
const inputRef = useRef(null); const inputRef = useRef(null);
@@ -80,9 +81,43 @@ const RecipientList = (props) => {
}); });
props?.setSignerPos(changeOrderSignerList); 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 ( 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) => { props.signersdata.map((obj, ind) => {
return ( return (
<div <div
@@ -118,20 +153,11 @@ const RecipientList = (props) => {
: color[ind % color.length] : color[ind % color.length]
: "transparent" : "transparent"
}} }}
onClick={(e) => { onClick={(e) => handleSelectRecipient(e, ind, obj)}
e.preventDefault();
props.setIsSelectId(ind);
props.setUniqueId(obj.Id);
props.setRoleName(obj.Role);
props.setBlockColor(obj?.blockColor);
if (props.handleModal) {
props.handleModal();
}
}}
> >
<div className="flex flex-row items-center w-full"> <div className="flex flex-row items-center w-full overflow-hidden pr-2">
<div <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={{ style={{
background: obj?.blockColor background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4) ? darkenColor(obj?.blockColor, 0.4)
@@ -142,38 +168,22 @@ const RecipientList = (props) => {
{isWidgetExist(obj.Id) ? ( {isWidgetExist(obj.Id) ? (
<i className="fa-light fa-check"></i> <i className="fa-light fa-check"></i>
) : ( ) : (
<> <>{getFirstLetter(obj?.Name ?? obj?.Role)}</>
{obj.Name
? getFirstLetter(obj.Name)
: getFirstLetter(obj.Role)}
</>
)} )}
</span> </span>
</div> </div>
<div <div
className={`${ className={`${obj.Name ? "flex-col" : "flex-row"} ${
obj.Name ? "flex-col" : "flex-row" isSelected(ind) ? "text-[#424242]" : "text-base-content"
} flex items-center`} } flex overflow-hidden flex-grow-0`}
> >
{obj.Name ? ( {obj.Name ? (
<span <span className="text-[12px] font-bold truncate whitespace-nowrap">
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj.Name} {obj.Name}
</span> </span>
) : ( ) : (
<span <span
className={`${ className="text-[12px] font-bold truncate whitespace-nowrap cursor-pointer"
(!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`}
onClick={() => { onClick={() => {
setIsEdit({ [obj.Id]: true }); setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role); props.setRoleName(obj.Role);
@@ -182,7 +192,7 @@ const RecipientList = (props) => {
{isEdit?.[obj.Id] && props.handleRoleChange ? ( {isEdit?.[obj.Id] && props.handleRoleChange ? (
<input <input
ref={inputRef} ref={inputRef}
className="bg-transparent p-[3px]" className="bg-transparent p-[3px] w-full"
value={obj.Role} value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)} onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => { onBlur={() => {
@@ -204,14 +214,7 @@ const RecipientList = (props) => {
</span> </span>
)} )}
{obj.Name && ( {obj.Name && (
<span <span className="text-[10px] font-medium truncate whitespace-nowrap">
className={` ${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj?.Role || obj?.Email} {obj?.Role || obj?.Email}
</span> </span>
)} )}
@@ -247,18 +250,13 @@ const RecipientList = (props) => {
</div> </div>
</div> </div>
)} )}
{props.handleDeleteUser && ( {props.handleDeleteUser && obj?.Role !== "prefill" && (
<div <div
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
props.handleDeleteUser(obj.Id); props.handleDeleteUser(obj.Id);
}} }}
className={`${ className={`${isSelected(ind) ? "text-[#424242]" : "text-base-content"} cursor-pointer`}
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} cursor-pointer`}
> >
<i className="fa-light fa-trash-can 2xl:text-[22px]"></i> <i className="fa-light fa-trash-can 2xl:text-[22px]"></i>
</div> </div>
@@ -17,7 +17,7 @@ function RenderAllPdfPage(props) {
const mergePdfInputRef = useRef(null); const mergePdfInputRef = useRef(null);
const [signPageNumber, setSignPageNumber] = useState([]); const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState(""); const [bookmarkColor, setBookmarkColor] = useState("");
const isHeader = useSelector((state) => state.showHeader); const isSidebar = useSelector((state) => state.sidebar.isOpen);
const [pageWidth, setPageWidth] = useState(""); const [pageWidth, setPageWidth] = useState("");
//set all number of pages after load pdf //set all number of pages after load pdf
@@ -51,7 +51,7 @@ function RenderAllPdfPage(props) {
const timer = setTimeout(updateSize, 100); // match the transition duration const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer); 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. //'function `addSignatureBookmark` is used to display the page where the user's signature is located.
const addSignatureBookmark = (index) => { const addSignatureBookmark = (index) => {
const ispageNumber = signPageNumber.includes(index + 1); const ispageNumber = signPageNumber.includes(index + 1);
@@ -78,16 +78,19 @@ function RenderAllPdfPage(props) {
const handleFileUpload = async (e) => { const handleFileUpload = async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) { if (!file) {
alert("Please upload a valid PDF file."); alert(t("please-select-pdf"));
return; return;
} }
if (!file.type.includes("pdf")) { if (!file.type.includes("pdf")) {
alert("Only PDF files are allowed."); alert(t("only-pdf-allowed"));
return; return;
} }
const mb = Math.round(file?.size / Math.pow(1024, 2)); const fileSize =
if (mb > maxFileSize) { maxFileSize;
alert(`${t("file-alert-1")} ${maxFileSize} MB`); const pdfsize = file?.size;
const fileSizeBytes = fileSize * 1024 * 1024;
if (pdfsize > fileSizeBytes) {
alert(`${t("file-alert-1")} ${fileSize} MB`);
removeFile(e); removeFile(e);
return; return;
} }
@@ -114,18 +117,18 @@ function RenderAllPdfPage(props) {
// Upload the file to Parse Server // Upload the file to Parse Server
} catch (err) { } catch (err) {
console.error("Incorrect password or decryption failed", err); console.error("Incorrect password or decryption failed", err);
alert("Incorrect password or decryption failed."); alert(t("incorrect-password-or-decryption-failed"));
} }
} else { } else {
alert("Please provided Password."); alert(t("provide-password"));
} }
} else { } else {
console.log("Err ", err); console.log("Err ", err);
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
} else { } else {
alert("error while uploading pdf."); alert(t("error-uploading-pdf"));
} }
} }
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
@@ -144,6 +147,13 @@ function RenderAllPdfPage(props) {
useObjectStreams: false useObjectStreams: false
}); });
const pdfBuffer = base64ToArrayBuffer(pdfBase64); 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.setPdfArrayBuffer(pdfBuffer);
props.setPdfBase64Url(pdfBase64); props.setPdfBase64Url(pdfBase64);
props.setIsUploadPdf && props.setIsUploadPdf(true); props.setIsUploadPdf && props.setIsUploadPdf(true);
+140 -59
View File
@@ -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 RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf"; import { Document, Page } from "react-pdf";
import { import {
@@ -12,22 +18,30 @@ import Placeholder from "./Placeholder";
import Alert from "../../primitives/Alert"; import Alert from "../../primitives/Alert";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import usePdfPinchZoom from "../../hook/usePdfPinchZoom"; 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";
function RenderPdf(props) { function RenderPdf(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useDispatch();
const [scaledHeight, setScaledHeight] = useState(); const [scaledHeight, setScaledHeight] = useState();
const [guideline, setGuideline] = useState({ const { guideline } = useGuidelinesContext();
show: false,
x1: 0,
x2: 0,
y1: 0,
y2: 0
});
//check isGuestSigner is present in local if yes than handle login flow header in mobile view //check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner"); 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); const pdfContainerRef = useRef(null);
useEffect(() => {
dispatch(toggleSidebar(false));
return () => {
dispatch(toggleSidebar(true));
};
}, []);
// enable pinch to zoom only on actual pdf wrapper // enable pinch to zoom only on actual pdf wrapper
usePdfPinchZoom( usePdfPinchZoom(
pdfContainerRef, pdfContainerRef,
@@ -36,20 +50,6 @@ function RenderPdf(props) {
props.setZoomPercent 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 // handle signature block width and height according to screen
const posWidth = (pos, signYourself) => { const posWidth = (pos, signYourself) => {
const containerScale = getContainerScale( const containerScale = getContainerScale(
@@ -111,6 +111,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) // function for render placeholder block over pdf document (all signing flow)
const checkSignedSigners = (data) => { const checkSignedSigners = (data) => {
let checkSign = []; let checkSign = [];
@@ -147,7 +239,6 @@ function RenderPdf(props) {
isSignYourself={false} isSignYourself={false}
posWidth={posWidth} posWidth={posWidth}
posHeight={posHeight} posHeight={posHeight}
showGuidelines={handleGuideline}
isDragging={props.isDragging} isDragging={props.isDragging}
pdfDetails={props.pdfDetails} pdfDetails={props.pdfDetails}
unSignedWidgetId={props.unSignedWidgetId} unSignedWidgetId={props.unSignedWidgetId}
@@ -160,13 +251,11 @@ function RenderPdf(props) {
ispublicTemplate={props.ispublicTemplate} ispublicTemplate={props.ispublicTemplate}
handleUserDetails={props.handleUserDetails} handleUserDetails={props.handleUserDetails}
isResize={props.isResize} isResize={props.isResize}
setIsAgreeTour={props.setIsAgreeTour}
isAgree={props.isAgree}
handleTabDrag={props.handleTabDrag} handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop} handleStop={props.handleStop}
setUniqueId={props.setUniqueId} setUniqueId={props.setUniqueId}
setIsSelectId={props.setIsSelectId} setIsSelectId={props.setIsSelectId}
handleDeleteSign={props.handleDeleteSign} handleDeleteWidget={props.handleDeleteWidget}
setIsPageCopy={props.setIsPageCopy} setIsPageCopy={props.setIsPageCopy}
handleTextSettingModal={props.handleTextSettingModal} handleTextSettingModal={props.handleTextSettingModal}
handleCellSettingModal={props.handleCellSettingModal} handleCellSettingModal={props.handleCellSettingModal}
@@ -174,16 +263,15 @@ function RenderPdf(props) {
isFreeResize={props.isSelfSign ? true : false} isFreeResize={props.isSelfSign ? true : false}
isOpenSignPad={true} isOpenSignPad={true}
assignedWidgetId={props.assignedWidgetId} assignedWidgetId={props.assignedWidgetId}
isApplyAll={true}
setCellCount={props.setCellCount} setCellCount={props.setCellCount}
setFontSize={props.setFontSize} setFontSize={props.setFontSize}
fontSize={props.fontSize} fontSize={props.fontSize}
fontColor={props.fontColor} fontColor={props.fontColor}
setFontColor={props.setFontColor} setFontColor={props.setFontColor}
setRequestSignTour={props.setRequestSignTour} setIsReqSignTourDisabled={props.setIsReqSignTourDisabled}
calculateFontsize={calculateFontsize} calculateFontsize={calculateFontsize}
currWidgetsDetails={props?.currWidgetsDetails} currWidgetsDetails={props?.currWidgetsDetails}
setTempSignerId={props.setTempSignerId} handleNameModal={props?.handleNameModal}
/> />
</React.Fragment> </React.Fragment>
) )
@@ -220,6 +308,7 @@ function RenderPdf(props) {
<Alert type={"success"}>{t("success-email-alert")}</Alert> <Alert type={"success"}>{t("success-email-alert")}</Alert>
)} )}
<RSC <RSC
ref={scrollRef}
style={{ style={{
position: "relative", position: "relative",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px", boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
@@ -273,8 +362,8 @@ function RenderPdf(props) {
<Placeholder <Placeholder
pos={pos} pos={pos}
setIsPageCopy={props.setIsPageCopy} setIsPageCopy={props.setIsPageCopy}
handleDeleteSign={ handleDeleteWidget={
props.handleDeleteSign props.handleDeleteWidget
} }
handleTabDrag={props.handleTabDrag} handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop} handleStop={props.handleStop}
@@ -294,7 +383,6 @@ function RenderPdf(props) {
isSignYourself={false} isSignYourself={false}
posWidth={posWidth} posWidth={posWidth}
posHeight={posHeight} posHeight={posHeight}
showGuidelines={handleGuideline}
isDragging={props.isDragging} isDragging={props.isDragging}
setIsValidate={props.setIsValidate} setIsValidate={props.setIsValidate}
setIsRadio={props.setIsRadio} setIsRadio={props.setIsRadio}
@@ -303,7 +391,6 @@ function RenderPdf(props) {
props.setCurrWidgetsDetails props.setCurrWidgetsDetails
} }
handleNameModal={props.handleNameModal} handleNameModal={props.handleNameModal}
setTempSignerId={props.setTempSignerId}
uniqueId={props.uniqueId} uniqueId={props.uniqueId}
handleTextSettingModal={ handleTextSettingModal={
props.handleTextSettingModal props.handleTextSettingModal
@@ -330,6 +417,11 @@ function RenderPdf(props) {
currWidgetsDetails={ currWidgetsDetails={
props?.currWidgetsDetails props?.currWidgetsDetails
} }
setRoleName={props?.setRoleName}
pdfDetails={props.pdfDetails}
setIsReqSignTourDisabled={
props.setIsReqSignTourDisabled
}
/> />
</React.Fragment> </React.Fragment>
))} ))}
@@ -348,7 +440,9 @@ function RenderPdf(props) {
key={id} key={id}
pos={pos} pos={pos}
setIsPageCopy={props.setIsPageCopy} setIsPageCopy={props.setIsPageCopy}
handleDeleteSign={props.handleDeleteSign} handleDeleteWidget={
props.handleDeleteWidget
}
handleTabDrag={props.handleTabDrag} handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop} handleStop={props.handleStop}
handleSignYourselfImageResize={ handleSignYourselfImageResize={
@@ -362,8 +456,6 @@ function RenderPdf(props) {
isSignYourself={true} isSignYourself={true}
posWidth={posWidth} posWidth={posWidth}
posHeight={posHeight} posHeight={posHeight}
showGuidelines={handleGuideline}
pdfDetails={props.pdfDetails[0]}
isDragging={props.isDragging} isDragging={props.isDragging}
setIsCheckbox={props.setIsCheckbox} setIsCheckbox={props.setIsCheckbox}
setCurrWidgetsDetails={ setCurrWidgetsDetails={
@@ -390,6 +482,11 @@ function RenderPdf(props) {
currWidgetsDetails={ currWidgetsDetails={
props?.currWidgetsDetails props?.currWidgetsDetails
} }
setIsReqSignTourDisabled={
props.setIsReqSignTourDisabled
}
unSignedWidgetId={props.unSignedWidgetId}
handleNameModal={props.handleNameModal}
/> />
) )
)} )}
@@ -428,28 +525,12 @@ function RenderPdf(props) {
/> />
</Document> </Document>
{guideline.show && ( {guideline.show && (
<> <Guidelines
{/* top guide */} x1={guideline.x1}
<div x2={guideline.x2}
className="absolute pointer-events-none z-[1000] left-0 w-full border-t-[1px] border-dashed border-[#3b82f6]" y1={guideline.y1}
style={{ top: guideline.y1 }} y2={guideline.y2}
/> />
{/* 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 }}
/>
</>
)} )}
</div> </div>
</RSC> </RSC>
@@ -27,12 +27,12 @@ function SignerListComponent(props) {
return ( return (
<div <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) }} style={{ background: checkSignerBackColor(props.obj) }}
> >
<div <div
style={{ background: checkUserNameColor(props.obj) }} 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"> <span className="text-[12px] text-center font-bold text-black uppercase">
{getFirstLetter( {getFirstLetter(
@@ -40,11 +40,11 @@ function SignerListComponent(props) {
)} )}
</span> </span>
</div> </div>
<div className="flex flex-col"> <div className="flex flex-grow-0 flex-col overflow-hidden pr-2">
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis"> <span className="text-[12px] font-bold truncate whitespace-nowrap">
{props.obj?.Name || props?.obj?.Role} {props.obj?.Name || props?.obj?.Role}
</span> </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} {props.obj?.Email || props.obj?.email}
</span> </span>
</div> </div>
@@ -1,16 +1,23 @@
import React from "react";
import RecipientList from "./RecipientList"; import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip"; // import { Tooltip } from "react-tooltip";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function SignerListPlace(props) { function SignerListPlace(props) {
const { t } = useTranslation(); const { t } = useTranslation();
const handleAddRecipient = () => {
props?.setIsAddSigner(true);
props.setIsTour && props.setIsTour(false);
};
return ( return (
<div> <div>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300"> <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"> <span className="relative">
{props.title ? props.title : "Recipients"} {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" && ( {props?.title === "Roles" && (
<> <>
<a data-tooltip-id="my-tooltip"> <a data-tooltip-id="my-tooltip">
@@ -30,7 +37,7 @@ function SignerListPlace(props) {
</Tooltip> </Tooltip>
</> </>
)} )}
</span> </span> */}
</span> </span>
</div> </div>
<div className="overflow-auto hide-scrollbar max-h-[180px]"> <div className="overflow-auto hide-scrollbar max-h-[180px]">
@@ -53,7 +60,7 @@ function SignerListPlace(props) {
data-tut="addRecipient" data-tut="addRecipient"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]" className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false} disabled={props?.isMailSend ? true : false}
onClick={() => props.setIsAddSigner(true)} onClick={handleAddRecipient}
> >
<i className="fa-light fa-plus"></i> {t("add-recipients")} <i className="fa-light fa-plus"></i> {t("add-recipients")}
</div> </div>
@@ -4,7 +4,6 @@ import RecipientList from "./RecipientList";
import { useDrag } from "react-dnd"; import { useDrag } from "react-dnd";
import WidgetList from "./WidgetList"; import WidgetList from "./WidgetList";
import { import {
color,
isMobile, isMobile,
radioButtonWidget, radioButtonWidget,
textInputWidget, textInputWidget,
@@ -88,18 +87,18 @@ function WidgetComponent(props) {
signature, signature,
stamp, stamp,
initials, initials,
textInput,
name, name,
jobTitle, jobTitle,
company, company,
email,
date, date,
text, text,
textInput,
cells, cells,
checkbox, checkbox,
dropdown, dropdown,
radioButton, radioButton,
image, image
email
]; ];
const getWidgetArray = widgets; const getWidgetArray = widgets;
const newUpdateSigner = getWidgetArray.map((obj, ind) => { const newUpdateSigner = getWidgetArray.map((obj, ind) => {
@@ -110,6 +109,7 @@ function WidgetComponent(props) {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
// allow only (signature, stamp, initials, text, name, job title, company, email, cells) widget when isAllowModification true and user have session token
const modifiedWidgets = widget.filter( const modifiedWidgets = widget.filter(
(data) => (data) =>
![ ![
@@ -121,6 +121,7 @@ function WidgetComponent(props) {
"checkbox" "checkbox"
].includes(data.type) ].includes(data.type)
); );
// allow only (signature, stamp, initials, text, cells) widget when isAllowModification true and user does not have session token
const unlogedInUserWidgets = widget.filter( const unlogedInUserWidgets = widget.filter(
(data) => (data) =>
![ ![
@@ -136,25 +137,37 @@ function WidgetComponent(props) {
"company" "company"
].includes(data.type) ].includes(data.type)
); );
const filterWidgets = widget.filter( const selfSignWidgets = widget.filter(
(data) => (data) =>
!["dropdown", radioButtonWidget, textInputWidget].includes( !["dropdown", radioButtonWidget, textInputWidget].includes(data.type)
data.type
)
); );
const textWidgetData = widget.filter((data) => data.type !== textWidget); //if user select prefill role then allow only date,image,text,checkbox,radio,dropdownAdd commentMore actions
const updateWidgets = props.isSignYourself //dropdown widget should only be show in template flow
? filterWidgets const prefillAllowWidgets = widget.filter((data) =>
: props.isTemplateFlow (props.isPrefillDropdown ? ["dropdown"] : [])
? textWidgetData .concat([radioButtonWidget, textWidget, "date", "image", "checkbox"])
: props.isAlllowModify .includes(data.type)
? userInformation );
? modifiedWidgets //function to show widget on the base of conditionAdd commentMore actions
: unlogedInUserWidgets const handleWidgetType = () => {
: widget; 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 widget.filter((data) => ![textWidget].includes(data.type));
}
};
const handleSelectRecipient = () => { const handleSelectRecipient = () => {
if ( if (props?.roleName === "prefill") {
return "Prefill by owner";
} else if (
props.signersdata[props.isSelectListId]?.Email || props.signersdata[props.isSelectListId]?.Email ||
props.signersdata[props.isSelectListId]?.Role props.signersdata[props.isSelectListId]?.Role
) { ) {
@@ -180,9 +193,10 @@ function WidgetComponent(props) {
className="w-full op-select op-select-bordered pointer-events-none" className="w-full op-select op-select-bordered pointer-events-none"
value={handleSelectRecipient()} value={handleSelectRecipient()}
style={{ style={{
backgroundColor: props.blockColor backgroundColor:
? props.blockColor props.roleName === "prefill"
: color[0] ? "#edf6fc"
: props?.blockColor || "#edf6fc"
}} }}
> >
<option value={handleSelectRecipient()}> <option value={handleSelectRecipient()}>
@@ -221,7 +235,7 @@ function WidgetComponent(props) {
> >
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]"> <div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
<WidgetList <WidgetList
updateWidgets={updateWidgets} updateWidgets={handleWidgetType}
handleDivClick={props.handleDivClick} handleDivClick={props.handleDivClick}
handleMouseLeave={props.handleMouseLeave} handleMouseLeave={props.handleMouseLeave}
signRef={signRef} signRef={signRef}
@@ -240,12 +254,23 @@ function WidgetComponent(props) {
} hidden md:block h-full bg-base-100`} } 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"> <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>
<div
<div className="p-[15px] flex flex-col pt-4" data-tut="addWidgets"> className="p-[12px] grid lg:grid-cols-2 gap-x-2 lg:gap-y-1.5 pt-3"
data-tut="addWidgets"
role="list"
aria-label="Add widgets"
>
<WidgetList <WidgetList
updateWidgets={updateWidgets} updateWidgets={handleWidgetType}
handleDivClick={props.handleDivClick} handleDivClick={props.handleDivClick}
handleMouseLeave={props.handleMouseLeave} handleMouseLeave={props.handleMouseLeave}
signRef={signRef} signRef={signRef}
@@ -260,7 +285,7 @@ function WidgetComponent(props) {
isOpen={isSignersModal} isOpen={isSignersModal}
handleClose={handleModal} handleClose={handleModal}
> >
{props.signersdata.length > 0 ? ( {props.signersdata.length > 0 || props.prefillSigner.length > 0 ? (
<div className="max-h-[600px] overflow-auto pb-1"> <div className="max-h-[600px] overflow-auto pb-1">
<RecipientList <RecipientList
signerPos={props.signerPos} signerPos={props.signerPos}
@@ -278,6 +303,7 @@ function WidgetComponent(props) {
setBlockColor={props.setBlockColor} setBlockColor={props.setBlockColor}
uniqueId={props.uniqueId} uniqueId={props.uniqueId}
setSignerPos={props.setSignerPos} setSignerPos={props.setSignerPos}
prefillSigner={props.prefillSigner}
/> />
</div> </div>
) : ( ) : (
@@ -1,10 +1,11 @@
import React from "react"; import { isMobile } from "../../constant/Utils";
import { getWidgetType, isMobile } from "../../constant/Utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import getWidgetType from "./getWidgetType";
function WidgetList(props) { function WidgetList(props) {
const { t } = useTranslation(); const { t } = useTranslation();
return props.updateWidgets.map((item, ind) => { const getWidgetList = props.updateWidgets();
return getWidgetList?.map((item, ind) => {
return ( return (
<div className="2xl:p-1 mb-[5px]" key={ind}> <div className="2xl:p-1 mb-[5px]" key={ind}>
<div <div
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css"; import "../../styles/AddUser.css";
import RegexParser from "regex-parser"; import RegexParser from "regex-parser";
@@ -21,25 +21,44 @@ const WidgetNameModal = (props) => {
hint: "", hint: "",
textvalidate: "", textvalidate: "",
isReadOnly: false, isReadOnly: false,
cellCount: 5 cellCount: 5,
}); });
const [isValid, setIsValid] = useState(true); const [isValid, setIsValid] = useState(true);
const statusArr = ["Required", "Optional"]; const statusArr = ["Required", "Optional"];
const [signatureType, setSignatureType] = useState([]); const [signatureType, setSignatureType] = useState([]);
const type = props?.defaultdata?.type;
const isCellWidget = useMemo(() => type === cellsWidget, [type]);
const isSignOrInitials = useMemo(
() => ["signature", "initials"].includes(type),
[type]
);
const showFontControls = useMemo(
() =>
[
textInputWidget,
textWidget,
cellsWidget,
"name",
"company",
"job title",
"email"
].includes(props.defaultdata?.type),
[type]
);
const handleHint = () => { const handleHint = () => {
const type = props.defaultdata?.type; const type = props.defaultdata?.type;
if (type === "signature") { if (type === "signature") {
return "Draw signature"; return t("draw-signature");
} else if (type === "stamp" || type === "image") { } else if (type === "stamp" || type === "image") {
return `Upload ${type}`; return type === "stamp" ? t("upload-stamp-image") : t("upload-image");
} else if (type === "initials") { } else if (type === "initials") {
return "Draw initial"; return t("draw-initials");
} else if (type === textInputWidget) { } else if (type === textInputWidget) {
return "Enter text"; return t("enter-text");
} else { } else {
return `Enter ${type}`; return t("enter-widgettype", { widgetType: type });
} }
}; };
useEffect(() => { useEffect(() => {
@@ -54,13 +73,13 @@ const WidgetNameModal = (props) => {
? props.defaultdata?.options?.validation?.pattern ? props.defaultdata?.options?.validation?.pattern
: props.defaultdata?.options?.validation?.type || "", : props.defaultdata?.options?.validation?.type || "",
isReadOnly: props.defaultdata?.options?.isReadOnly || false, isReadOnly: props.defaultdata?.options?.isReadOnly || false,
cellCount: props.defaultdata?.options?.cellCount || 5 cellCount: props.defaultdata?.options?.cellCount || 5,
}); });
} else { } else {
setFormdata({ setFormdata({
...formdata, ...formdata,
name: props.defaultdata?.options?.name || "", name: props.defaultdata?.options?.name || "",
cellCount: props.defaultdata?.options?.cellCount || 5 cellCount: props.defaultdata?.options?.cellCount || 5,
}); });
} }
@@ -68,12 +87,13 @@ const WidgetNameModal = (props) => {
const defaultSignatureType = signTypes || []; const defaultSignatureType = signTypes || [];
setSignatureType(defaultSignatureType); setSignatureType(defaultSignatureType);
} }
// eslint-disable-next-line // eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.defaultdata]); }, [props.defaultdata]);
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
if (props.handleData) { if (props.handleData) {
if (["signature", "initials"].includes(props.defaultdata?.type)) { if (isSignOrInitials) {
const enabledSignTypes = signatureType?.filter((x) => x.enabled); const enabledSignTypes = signatureType?.filter((x) => x.enabled);
const isDefaultSignTypeOnly = const isDefaultSignTypeOnly =
enabledSignTypes?.length === 1 && enabledSignTypes?.length === 1 &&
@@ -81,15 +101,17 @@ const WidgetNameModal = (props) => {
if (enabledSignTypes.length === 0) { if (enabledSignTypes.length === 0) {
alert(t("at-least-one-signature-type")); alert(t("at-least-one-signature-type"));
} else if (isDefaultSignTypeOnly) { } else if (isDefaultSignTypeOnly) {
alert(t("expect-default-one-more-signature-type")); alert(t("expect-default-one-signature-type"));
} else { } else {
const data = { ...formdata, signatureType }; const data = { ...formdata, signatureType };
props.handleData(data, props.defaultdata?.type); props.handleData(data, props.defaultdata?.type);
} }
} else { } else {
const isTextInput = [textInputWidget, cellsWidget].includes(
props.defaultdata?.type const isTextInput = [
); textInputWidget,
cellsWidget,
].includes(props.defaultdata?.type);
const { isReadOnly, defaultValue, status } = formdata; const { isReadOnly, defaultValue, status } = formdata;
// If its a textinput widget, enforce that read-only fields have // If its a textinput widget, enforce that read-only fields have
// either a defaultValue or an "optional" status. // either a defaultValue or an "optional" status.
@@ -110,7 +132,7 @@ const WidgetNameModal = (props) => {
status: "required", status: "required",
hint: "", hint: "",
textvalidate: "", textvalidate: "",
cellCount: 5 cellCount: 5,
}); });
setSignatureType(signTypes); setSignatureType(signTypes);
} }
@@ -148,10 +170,11 @@ const WidgetNameModal = (props) => {
} else { } else {
setIsValid(true); setIsValid(true);
} }
const val =
props.defaultdata?.type === cellsWidget const val = isCellWidget
? e.target.value.slice(0, formdata.cellCount) ? e.target.value.slice(0, formdata.cellCount)
: e.target.value; : e.target.value;
setFormdata({ ...formdata, [e.target.name]: val }); setFormdata({ ...formdata, [e.target.name]: val });
}; };
@@ -160,7 +183,7 @@ const WidgetNameModal = (props) => {
case "email": case "email":
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/"; return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
case "number": case "number":
return "/^\\d+$/"; return "^\\d+(?:\\.\\d+)?$"; // "/^\\d+$/";
case "text": case "text":
//allow space in text regex //allow space in text regex
return "/^[a-zA-Z ]+$/"; return "/^[a-zA-Z ]+$/";
@@ -184,23 +207,19 @@ const WidgetNameModal = (props) => {
<ModalUi <ModalUi
isOpen={props.isOpen} isOpen={props.isOpen}
handleClose={props.handleClose && props.handleClose} handleClose={props.handleClose && props.handleClose}
title={ title={isSignOrInitials ? t("signature-setting") : t("widget-info")}
["signature", "initials"].includes(props.defaultdata?.type)
? t("signature-setting")
: t("widget-info")
}
> >
<form <form
onSubmit={handleSubmit} onSubmit={handleSubmit}
className={`${ className={`${
[textInputWidget, cellsWidget].includes(props.defaultdata?.type) [textInputWidget, cellsWidget].includes(props.defaultdata?.type)
? "pt-0" ? "pt-0"
: ["signature", "initials"].includes(props.defaultdata?.type) : isSignOrInitials
? "pt-2" ? "pt-2"
: "" : ""
} p-[20px] text-base-content`} } p-[20px] text-base-content`}
> >
{!["signature", "initials"].includes(props.defaultdata?.type) && ( {!isSignOrInitials && (
<div className="mb-[0.75rem] text-[13px]"> <div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name"> <label htmlFor="name">
{t("name")} {t("name")}
@@ -217,7 +236,7 @@ const WidgetNameModal = (props) => {
/> />
</div> </div>
)} )}
{props.defaultdata?.type === cellsWidget && ( {isCellWidget && (
<div className="mb-[0.75rem] text-[13px]"> <div className="mb-[0.75rem] text-[13px]">
<label htmlFor="cellCount">{t("cell-count")}</label> <label htmlFor="cellCount">{t("cell-count")}</label>
<input <input
@@ -231,102 +250,107 @@ const WidgetNameModal = (props) => {
/> />
</div> </div>
)} )}
{[textInputWidget, cellsWidget].includes(props.defaultdata?.type) && ( {[
<> textInputWidget,
<div className="mb-[0.75rem]"> cellsWidget,
<label htmlFor="name" className="text-[13px]"> ].includes(props.defaultdata?.type) &&
{t("default-value")} props?.roleName !== "prefill" && (
</label> <>
<input <div className="mb-[0.75rem]">
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" <label htmlFor="name" className="text-[13px]">
name="defaultValue" {t("default-value")}
value={formdata.defaultValue} </label>
onChange={(e) => handledefaultChange(e)} <input
autoComplete="off" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
maxLength={ name="defaultValue"
props.defaultdata?.type === cellsWidget value={formdata.defaultValue}
? formdata.cellCount onChange={(e) => handledefaultChange(e)}
: undefined autoComplete="off"
} maxLength={isCellWidget ? formdata.cellCount : undefined}
onBlur={() => { onBlur={() => {
if (isValid === false) { if (isValid === false) {
setFormdata({ ...formdata, defaultValue: "" }); setFormdata({ ...formdata, defaultValue: "" });
setIsValid(true); setIsValid(true);
} }
}} }}
/> />
{isValid === false && ( {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 (
<div <div
key={ind} className="warning defaultvalueWarning"
className="flex flex-row gap-[5px] items-center" 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 <input
className="mr-[2px] op-radio op-radio-xs" id="isReadOnly"
type="radio" name="isReadOnly"
name="status" type="checkbox"
checked={formdata.isReadOnly}
className="op-checkbox op-checkbox-xs"
onChange={() => onChange={() =>
setFormdata({ ...formdata, status: data.toLowerCase() }) setFormdata((prev) => ({
} ...formdata,
checked={ isReadOnly: !prev.isReadOnly
formdata.status.toLowerCase() === data.toLowerCase() }))
} }
/> />
<div className="text-[13px] font-medium"> <label
{t(`widget-status.${data}`)} className="ml-1.5 mb-0 capitalize text-[13px]"
</div> htmlFor="isreadonly"
>
{t("read-only")}
</label>
</div> </div>
); )}
})}
</div> </div>
{[textInputWidget, cellsWidget].includes( )}
props.defaultdata?.type {isSignOrInitials && (
) && (
<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) && (
<div className="mb-[0.75rem]"> <div className="mb-[0.75rem]">
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]"> <label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
{t("allowed-signature-types")} {t("allowed-signature-types")}
@@ -354,7 +378,7 @@ const WidgetNameModal = (props) => {
</div> </div>
</div> </div>
)} )}
{props.defaultdata?.type !== textWidget && ( {!props?.isSelfSign && props?.roleName !== "prefill" && (
<div className="mb-[0.75rem]"> <div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]"> <label htmlFor="hint" className="text-[13px]">
{t("hint")} {t("hint")}
@@ -368,15 +392,7 @@ const WidgetNameModal = (props) => {
/> />
</div> </div>
)} )}
{[ {showFontControls && (
textInputWidget,
textWidget,
cellsWidget,
"name",
"company",
"job title",
"email"
].includes(props.defaultdata?.type) && (
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3"> <div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
<div className="flex items-center gap-2 "> <div className="flex items-center gap-2 ">
<span className="whitespace-nowrap">{t("font-size")}: </span> <span className="whitespace-nowrap">{t("font-size")}: </span>
File diff suppressed because it is too large Load Diff
@@ -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]"></i>
)}
<span className="md:inline-block text-center text-[11px] mx-[3px] truncate font-medium capitalize">
{widgetName}
</span>
</div>
<div className="text-[13px] op-btn op-btn-primary rounded-none w-[20px] h-full flex justify-center items-center">
<i className={item.icon}></i>
</div>
</div>
</div>
);
};
export default getWidgetType;
@@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { formatDateTime } from "../../../constant/Utils"; import { formatDateTime } from "../../constant/Utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const DateFormatSelector = (props) => { const DateFormatSelector = (props) => {
@@ -17,7 +17,9 @@ const DateFormatSelector = (props) => {
"YYYY-MM-DD", "YYYY-MM-DD",
"MM-DD-YYYY", "MM-DD-YYYY",
"MM.DD.YYYY", "MM.DD.YYYY",
"MMM DD, YYYY" "MMM DD, YYYY",
"DD.MM.YYYY",
"DD/MM/YYYY"
]; ];
// Handle format change // 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;
@@ -197,7 +197,7 @@ const FolderModal = (props) => {
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" 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" /> <path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg> </svg>
@@ -183,7 +183,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" 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" /> <path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg> </svg>
@@ -262,7 +262,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" 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" /> <path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
</svg> </svg>
@@ -63,6 +63,10 @@ const SelectSigners = (props) => {
if (props.closePopup) { if (props.closePopup) {
props.closePopup(); props.closePopup();
} }
} else if (selected?.value) {
if (props.closePopup) {
props.closePopup();
}
} else { } else {
setIsError(true); setIsError(true);
setTimeout(() => setIsError(false), 1000); setTimeout(() => setIsError(false), 1000);
@@ -217,6 +217,7 @@ const SignersInput = (props) => {
{isModal && ( {isModal && (
<AddContact <AddContact
isDisableTitle isDisableTitle
isAddYourSelfCheckbox={props?.isAddYourSelfCheckbox}
details={handleNewDetails} details={handleNewDetails}
closePopup={handleModalCloseClick} closePopup={handleModalCloseClick}
/> />
@@ -1,13 +1,19 @@
import React, { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Menu from "./Menu"; import Menu from "./Menu";
import Submenu from "./SubMenu"; import Submenu from "./SubMenu";
import SocialMedia from "../SocialMedia"; import SocialMedia from "../SocialMedia";
import dp from "../../assets/images/dp.png"; import dp from "../../assets/images/dp.png";
import sidebarList, { subSetting } from "../../json/menuJson"; import sidebarList, { subSetting } from "../../json/menuJson";
import { useNavigate } from "react-router"; 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 navigate = useNavigate();
const dispatch = useDispatch();
const isOpen = useSelector((state) => state.sidebar.isOpen);
const [menuList, setmenuList] = useState([]); const [menuList, setmenuList] = useState([]);
const [submenuOpen, setSubmenuOpen] = useState(false); const [submenuOpen, setSubmenuOpen] = useState(false);
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
@@ -22,44 +28,29 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
} }
}, []); }, []);
const closeSidebar = () => {
if (width <= 1023) {
dispatch(toggleSidebar(false));
}
};
const menuItem = async () => { const menuItem = async () => {
try { try {
if (localStorage.getItem("defaultmenuid")) { if (localStorage.getItem("defaultmenuid")) {
const Extand_Class = localStorage.getItem("Extand_Class"); const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class); const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass); const userRole = extClass?.[0]?.UserRole || "contracts_User";
let userRole = "contracts_User"; const isAdmin =
if (extClass && extClass.length > 0) { userRole === "contracts_Admin" || userRole === "contracts_OrgAdmin";
userRole = extClass[0].UserRole; const newSidebarList = sidebarList.map((item) => {
} if (item.title !== "Settings") return item;
if ( const newItem = { ...item };
userRole === "contracts_Admin" || const baseChildren = isAdmin ? subSetting : subSetting?.slice(0, 1);
userRole === "contracts_OrgAdmin" const mysignature = newItem.children.slice(0, 1);
) { newItem.children = [...mysignature, ...baseChildren];
const newSidebarList = sidebarList.map((item) => { return newItem;
if (item.title === "Settings") { });
// Make a shallow copy of the item setmenuList(newSidebarList);
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);
}
} }
} catch (e) { } catch (e) {
console.error("Problem", e); console.error("Problem", e);
@@ -80,8 +71,8 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
}; };
return ( return (
<aside <aside
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[500] shadow-lg hide-scrollbar 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-[300px]" : "w-0"}`} ${isOpen ? "w-full md:w-64" : "w-0"}`}
> >
<div className="flex px-2 py-3 gap-2 items-center shadow-md"> <div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div <div
@@ -140,7 +131,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
)} )}
</ul> </ul>
</nav> </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 /> <SocialMedia />
</footer> </footer>
</aside> </aside>
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@ export const getThemeIconColor = () => {
return theme === "opensigndark" ? "#CCCCCC" : "#686968"; return theme === "opensigndark" ? "#CCCCCC" : "#686968";
}; };
export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 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 maxTitleLength = 250; // 250 characters
export const maxNoteLength = 200; // 200 characters export const maxNoteLength = 200; // 200 characters
export const maxDescriptionLength = 500; // 500 characters export const maxDescriptionLength = 500; // 500 characters
export const maxFileSize = 80; // for cloud 10MB / 80MB for self-hosted
+17 -21
View File
@@ -4,13 +4,22 @@ const parseAppId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID
: "opensign"; : "opensign";
const serverUrl = serverUrl_fn(); const serverUrl = serverUrl_fn();
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 //checking server url and save file's size
const tenantPtr = { const tenantPtr = {
__type: "Pointer", __type: "Pointer",
className: "partners_Tenant", className: "partners_Tenant",
objectId: tenantId objectId: tenantId
}; };
const UserPtr = userId && {
__type: "Pointer",
className: "_User",
objectId: userId
};
const _tenantPtr = JSON.stringify(tenantPtr); const _tenantPtr = JSON.stringify(tenantPtr);
try { try {
const res = await axios.get( const res = await axios.get(
@@ -24,7 +33,6 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => {
); );
const response = res.data.results; const response = res.data.results;
let data; let data;
// console.log("response", response);
if (response && response.length > 0) { if (response && response.length > 0) {
data = { data = {
usedStorage: response[0].usedStorage usedStorage: response[0].usedStorage
@@ -34,43 +42,31 @@ export const SaveFileSize = async (size, imageUrl, tenantId) => {
await axios.put( await axios.put(
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`, `${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
data, data,
{ { headers: commonheader }
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}
); );
} else { } else {
data = { usedStorage: size, PartnersTenant: tenantPtr }; data = { usedStorage: size, PartnersTenant: tenantPtr };
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, { await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
headers: { headers: commonheader
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}); });
} }
} catch (err) { } catch (err) {
console.log("err in save usage", 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 //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 = { const data = {
FileUrl: imageUrl, FileUrl: imageUrl,
FileSize: size, FileSize: size,
TenantPtr: tenantPtr TenantPtr: tenantPtr,
...(UserId ? { UserId: UserId } : {})
}; };
// console.log("data save",file, data)
try { try {
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, { await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
headers: { headers: commonheader
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}); });
} catch (err) { } catch (err) {
console.log("err in save usage ", err); console.log("err in save usage ", err);
@@ -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;
};
+35
View File
@@ -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;
}
+59 -46
View File
@@ -24,7 +24,7 @@ body {
@media screen and (max-width: 766px) { @media screen and (max-width: 766px) {
.reactour__close { .reactour__close {
width: 17px !important; width: 17px !important;
padding-left: 3px !important; padding-left: 3px !important;
} }
} }
@@ -100,49 +100,62 @@ body {
border-radius: 10px; 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 */ /* 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 */ /* You can use classes like: icon-improved, icon-muted, icon-disabled, op-btn-vscode-disabled */
/* React-tour and ReactTooltip dark mode styling */ /* React-tour and ReactTooltip dark mode styling */
[data-theme="opensigndark"] { [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 */ /* React-tour modal styling */
.reactour__helper { .reactour__helper {
background-color: #1F2937 !important; background-color: #1f2937 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
border: 1px solid #374151 !important; border: 1px solid #374151 !important;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5) !important; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5) !important;
} }
.reactour__close { .reactour__close {
color: #E5E7EB !important; color: #e5e7eb !important;
background-color: #374151 !important; background-color: #374151 !important;
border: 1px solid #4B5563 !important; border: 1px solid #4b5563 !important;
} }
.reactour__close:hover { .reactour__close:hover {
background-color: #4B5563 !important; background-color: #4b5563 !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
/* React-tour navigation buttons */ /* React-tour navigation buttons */
.reactour__controls { .reactour__controls {
background-color: #1F2937 !important; background-color: #1f2937 !important;
border-top: 1px solid #374151 !important; border-top: 1px solid #374151 !important;
} }
.reactour__controls button { .reactour__controls button {
background-color: #374151 !important; background-color: #374151 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
border: 1px solid #4B5563 !important; border: 1px solid #4b5563 !important;
} }
.reactour__controls button:hover { .reactour__controls button:hover {
background-color: #4B5563 !important; background-color: #4b5563 !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
.reactour__controls button:disabled { .reactour__controls button:disabled {
background-color: #3C3C3C !important; background-color: #3c3c3c !important;
color: #858585 !important; color: #858585 !important;
border-color: #565656 !important; border-color: #565656 !important;
cursor: not-allowed !important; cursor: not-allowed !important;
@@ -150,28 +163,28 @@ body {
/* ReactTooltip styling */ /* ReactTooltip styling */
.react-tooltip { .react-tooltip {
background-color: #1F2937 !important; background-color: #1f2937 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
border: 1px solid #374151 !important; border: 1px solid #374151 !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
} }
.react-tooltip.type-dark { .react-tooltip.type-dark {
background-color: #1F2937 !important; background-color: #1f2937 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-tooltip.place-top:after, .react-tooltip.place-top:after,
.react-tooltip.place-bottom:after, .react-tooltip.place-bottom:after,
.react-tooltip.place-left:after, .react-tooltip.place-left:after,
.react-tooltip.place-right:after { .react-tooltip.place-right:after {
border-color: #1F2937 !important; border-color: #1f2937 !important;
} }
/* Tour content improvements */ /* Tour content improvements */
.reactour__badge { .reactour__badge {
background-color: #007ACC !important; background-color: #007acc !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
.reactour__helper h1, .reactour__helper h1,
@@ -180,82 +193,82 @@ body {
.reactour__helper h4, .reactour__helper h4,
.reactour__helper h5, .reactour__helper h5,
.reactour__helper h6 { .reactour__helper h6 {
color: #FFFFFF !important; color: #ffffff !important;
} }
.reactour__helper p { .reactour__helper p {
color: #E5E7EB !important; color: #e5e7eb !important;
} }
/* VS Code-style tour buttons */ /* VS Code-style tour buttons */
.reactour__controls .op-btn { .reactour__controls .op-btn {
background-color: #007ACC !important; background-color: #007acc !important;
color: #FFFFFF !important; color: #ffffff !important;
border: 1px solid #007ACC !important; border: 1px solid #007acc !important;
} }
.reactour__controls .op-btn:hover { .reactour__controls .op-btn:hover {
background-color: #0086D1 !important; background-color: #0086d1 !important;
border-color: #0086D1 !important; border-color: #0086d1 !important;
} }
.reactour__controls .op-btn-secondary { .reactour__controls .op-btn-secondary {
background-color: #374151 !important; background-color: #374151 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
border: 1px solid #4B5563 !important; border: 1px solid #4b5563 !important;
} }
.reactour__controls .op-btn-secondary:hover { .reactour__controls .op-btn-secondary:hover {
background-color: #4B5563 !important; background-color: #4b5563 !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
/* React-datepicker dark mode styling */ /* React-datepicker dark mode styling */
.react-datepicker { .react-datepicker {
background-color: #1F2937 !important; background-color: #1f2937 !important;
border: 1px solid #374151 !important; border: 1px solid #374151 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__header { .react-datepicker__header {
background-color: #374151 !important; background-color: #374151 !important;
border-bottom: 1px solid #4B5563 !important; border-bottom: 1px solid #4b5563 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__current-month, .react-datepicker__current-month,
.react-datepicker__day-name { .react-datepicker__day-name {
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__day { .react-datepicker__day {
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__day:hover { .react-datepicker__day:hover {
background-color: #4B5563 !important; background-color: #4b5563 !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
.react-datepicker__day--selected { .react-datepicker__day--selected {
background-color: #007ACC !important; background-color: #007acc !important;
color: #FFFFFF !important; color: #ffffff !important;
} }
.react-datepicker__day--keyboard-selected { .react-datepicker__day--keyboard-selected {
background-color: #374151 !important; background-color: #374151 !important;
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__day--outside-month { .react-datepicker__day--outside-month {
color: #6B7280 !important; color: #6b7280 !important;
} }
.react-datepicker__navigation { .react-datepicker__navigation {
color: #E5E7EB !important; color: #e5e7eb !important;
} }
.react-datepicker__navigation:hover { .react-datepicker__navigation:hover {
background-color: #4B5563 !important; background-color: #4b5563 !important;
} }
} }
+9 -43
View File
@@ -1,19 +1,10 @@
import React from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import "./index.css"; import "./index.css";
import "./styles/dark-theme-improvements.css"; import "./styles/dark-theme-improvements.css";
import App from "./App"; import App from "./App";
import { showUpgradeProgress, hideUpgradeProgress } from "./utils";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { store } from "./redux/store"; 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 Parse from "parse";
import "./polyfills"; import "./polyfills";
import { serverUrl_fn } from "./constant/appinfo"; import { serverUrl_fn } from "./constant/appinfo";
@@ -25,47 +16,22 @@ const serverUrl = serverUrl_fn();
Parse.initialize(appId); Parse.initialize(appId);
Parse.serverURL = serverUrl; Parse.serverURL = serverUrl;
if (localStorage.getItem("showUpgradeProgress")) {
showUpgradeProgress();
}
const savedTheme = localStorage.getItem("theme"); const savedTheme = localStorage.getItem("theme");
if (savedTheme === "dark") { if (savedTheme === "dark") {
document.documentElement.setAttribute("data-theme", "opensigndark"); 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")); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<Provider store={store}> <Provider store={store}>
<DndProvider options={HTML5toTouch}> <App />
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider> </Provider>
); );
hideUpgradeProgress();
localStorage.removeItem("showUpgradeProgress");
+14 -45
View File
@@ -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) { export default function reportJson(id) {
// console.log("json ", json);
const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"]; const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"];
const declineHead = ["Title", "Reason", "Folder", "File", "Owner", "Signers"]; const declineHead = ["Title", "Reason", "Folder", "File", "Owner", "Signers"];
const iphead = ["Title", "Note", "Folder", "File", "Signers"]; const iphead = ["Title", "Note", "Folder", "File", "Signers", "Sent Date"];
const contactbook = ["Name", "Email", "Phone"]; const contactbook = ["Name", "Email", "Phone", "Company", "JobTitle"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"]; const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Title", "File", "Owner", "Signers"]; const templateReport = ["Title", "File", "Owner", "Signers"];
switch (id) { switch (id) {
@@ -28,25 +37,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-trash", btnIcon: "fa-light fa-trash",
redirectUrl: "", redirectUrl: "",
action: "delete" 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: helpMsg:
@@ -81,7 +71,7 @@ export default function reportJson(id) {
btnId: "8901", btnId: "8901",
hoverLabel: "Share", hoverLabel: "Share",
btnColor: "op-btn-primary", btnColor: "op-btn-primary",
btnIcon: "fa-light fa-share", btnIcon: "fa-light fa-copy",
redirectUrl: "", redirectUrl: "",
action: "share" action: "share"
}, },
@@ -317,7 +307,7 @@ export default function reportJson(id) {
btnId: "1999", btnId: "1999",
hoverLabel: "Share", hoverLabel: "Share",
btnColor: "op-btn-primary", btnColor: "op-btn-primary",
btnIcon: "fa-light fa-share", btnIcon: "fa-light fa-copy",
redirectUrl: "", redirectUrl: "",
action: "share" action: "share"
}, },
@@ -419,25 +409,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-trash", btnIcon: "fa-light fa-trash",
redirectUrl: "", redirectUrl: "",
action: "delete" 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" action: "delete"
} }
], ],
import: true,
form: "ContactBook",
helpMsg: 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." "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."
}; };
+47
View File
@@ -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" }
}
];
+1 -1
View File
@@ -36,7 +36,7 @@ const dashboardJson = [
queryType: "", queryType: "",
class: "contracts_Document", class: "contracts_Document",
query: 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", key: "count",
Redirect_type: "Report", Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF", Redirect_id: "1MwEuxLEkF",
+93 -113
View File
@@ -1,35 +1,31 @@
import React, { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Header from "../components/Header"; import Header from "../components/Header";
import Footer from "../components/Footer"; import Footer from "../components/Footer";
import Sidebar from "../components/sidebar/Sidebar"; import Sidebar from "../components/sidebar/Sidebar";
import { useWindowSize } from "../hook/useWindowSize";
import Tour from "../primitives/Tour"; import Tour from "../primitives/Tour";
import axios from "axios"; import axios from "axios";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import Parse from "parse"; import Parse from "parse";
import ModalUi from "../primitives/ModalUi"; import { useNavigate, Outlet } from "react-router";
import { useNavigate, useLocation, Outlet } from "react-router";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { showHeader } from "../redux/reducers/showHeader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { sessionStatus } from "../redux/reducers/userReducer";
import SessionExpiredModal from "../primitives/SessionExpiredModal";
const HomeLayout = () => { const HomeLayout = () => {
const appName = const appName =
"OpenSign™"; "OpenSign™";
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { width } = useWindowSize();
const [isOpen, setIsOpen] = useState(true);
const arr = useSelector((state) => state.TourSteps); const arr = useSelector((state) => state.TourSteps);
const [isUserValid, setIsUserValid] = useState(true); const isValidSession = useSelector((state) => state.user.isValidSession);
const [isLoader, setIsLoader] = useState(true); const [isLoader, setIsLoader] = useState(true);
const [isCloseBtn, setIsCloseBtn] = useState(true); const [isCloseBtn, setIsCloseBtn] = useState(true);
const [isTour, setIsTour] = useState(false); const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]); const [tourStatusArr, setTourStatusArr] = useState([]);
const [tourConfigs, setTourConfigs] = useState([]); const [tourConfigs, setTourConfigs] = useState([]);
const [isLoggingOut, setIsLoggingOut] = useState(false);
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
useEffect(() => { useEffect(() => {
@@ -40,42 +36,36 @@ const HomeLayout = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!tenantId) { if (localStorage.getItem("accesstoken")) {
setIsUserValid(false); if (!tenantId) {
} else { dispatch(sessionStatus(false));
(async () => { } else {
try { (async () => {
// Use the session token to validate the user try {
const userQuery = new Parse.Query(Parse.User); // Use the session token to validate the user
const user = await userQuery.get(Parse?.User?.current()?.id, { const userQuery = new Parse.Query(Parse.User);
sessionToken: localStorage.getItem("accesstoken") const user = await userQuery.get(Parse?.User?.current()?.id, {
}); sessionToken: localStorage.getItem("accesstoken")
if (user) { });
localStorage.setItem("profileImg", user.get("ProfilePic") || ""); if (user) {
setIsUserValid(true); localStorage.setItem("profileImg", user.get("ProfilePic") || "");
setIsLoader(false); dispatch(sessionStatus(true));
} else { setIsLoader(false);
setIsUserValid(false); } else {
dispatch(sessionStatus(true));
}
} catch (error) {
console.log("catch");
// Session token is invalid or there was an error
dispatch(sessionStatus(false));
} }
} catch (error) { })();
// Session token is invalid or there was an error }
setIsUserValid(false);
}
})();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [tenantId]); }, [tenantId]);
const showSidebar = () => {
setIsOpen((value) => !value);
dispatch(showHeader(!isOpen));
};
useEffect(() => {
if (width && width <= 768) {
setIsOpen(false);
}
}, [width]);
useEffect(() => { useEffect(() => {
if (arr && arr.length > 0) { if (arr && arr.length > 0) {
@@ -87,6 +77,7 @@ const HomeLayout = () => {
}, [arr]); }, [arr]);
const handleDynamicSteps = () => { const handleDynamicSteps = () => {
const github = "https://github.com/OpenSignLabs/OpenSign";
if (arr && arr.length > 0) { if (arr && arr.length > 0) {
// const resArr = arr; // const resArr = arr;
const resArr = arr.map((obj, index) => { const resArr = arr.map((obj, index) => {
@@ -105,7 +96,7 @@ const HomeLayout = () => {
{ {
selector: '[data-tut="nonpresentmask"]', selector: '[data-tut="nonpresentmask"]',
content: t("tour-mssg.home-layout-1"), content: t("tour-mssg.home-layout-1"),
position: "center", position: "center"
}, },
{ {
selector: '[data-tut="tourbutton"]', selector: '[data-tut="tourbutton"]',
@@ -115,8 +106,23 @@ const HomeLayout = () => {
...resArr, ...resArr,
{ {
selector: '[data-tut="nonpresentmask"]', selector: '[data-tut="nonpresentmask"]',
content: t("tour-mssg.home-layout-3", { appName }), content: () => (
position: "center", <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(); checkTourStatus();
@@ -146,19 +152,11 @@ const HomeLayout = () => {
updatedTourStatus = [{ loginTour: true }]; updatedTourStatus = [{ loginTour: true }];
} }
// console.log("updatedTourStatus ", updatedTourStatus);
await axios.put( await axios.put(
serverUrl + "classes/contracts_Users/" + extUserId, serverUrl + "classes/contracts_Users/" + extUserId,
{ { TourStatus: updatedTourStatus },
TourStatus: updatedTourStatus { headers: { "X-Parse-Application-Id": appId } }
},
{
headers: {
"X-Parse-Application-Id": appId
}
}
); );
// console.log("updatedRes ", updatedRes);
}; };
async function checkTourStatus() { async function checkTourStatus() {
@@ -175,74 +173,56 @@ const HomeLayout = () => {
} }
} }
const closeSidebar = () => { return isValidSession && localStorage.getItem("accesstoken") ? (
if (width <= 1023) { <div className="flex flex-col h-screen overflow-hidden">
setIsOpen(false); {/* HEADER */}
} <header className="z-[501]">
}; {!isLoader && <Header setIsLoggingOut={setIsLoggingOut} />}
</header>
const handleLoginBtn = async () => { {isLoader ? (
try { <div className="flex h-[100vh] justify-center items-center">
await Parse?.User?.logOut(); <Loader />
} catch (err) { </div>
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 ? (
<> <>
{isLoader ? ( {isLoggingOut && (
<div className="flex h-[100vh] justify-center items-center"> <div className="inset-0 bg-black/30 z-[1000] fixed flex justify-center items-center">
<Loader /> <Loader />
</div> </div>
) : ( )}
<> {/* BODY */}
<div className="flex md:flex-row flex-col z-50"> <div className="flex flex-1 overflow-hidden">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} /> {/* SIDEBAR with width animation */}
<div <Sidebar />
id="renderList" {/* MAIN (includes both content + footer in one scrollable column) */}
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto" <main
> id="renderList"
<div className="bg-base-200 p-3">{<Outlet />}</div> className="flex-1 overflow-auto transition-all duration-300 ease-in-out"
<div className="z-30"> >
<Footer /> <div className="flex flex-col min-h-full">
</div> {/* your page content */}
<div className="p-3">{<Outlet />}</div>
{/* sticky-but-scrollable footer */}
<div className="mt-auto z-30">
<Footer />
</div> </div>
</div> </div>
<Tour </main>
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>
</div> </div>
</ModalUi> <Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
showCloseButton={isCloseBtn}
/>
</>
)} )}
</div> </div>
) : (
<SessionExpiredModal />
); );
}; };
+4 -4
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { NavLink, useNavigate } from "react-router"; import { NavLink, useNavigate } from "react-router";
@@ -11,7 +11,6 @@ import {
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant"; import { showTenant } from "../redux/reducers/ShowTenant";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import Title from "../components/Title";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const"; import { emailRegex } from "../constant/const";
@@ -73,6 +72,7 @@ const AddAdmin = () => {
const defaultmenuid = localStorage.getItem("defaultmenuid"); const defaultmenuid = localStorage.getItem("defaultmenuid");
const PageLanding = localStorage.getItem("PageLanding"); const PageLanding = localStorage.getItem("PageLanding");
const userSettings = localStorage.getItem("userSettings"); const userSettings = localStorage.getItem("userSettings");
const favicon = localStorage.getItem("favicon");
localStorage.clear(); localStorage.clear();
saveLanguageInLocal(i18n); saveLanguageInLocal(i18n);
@@ -84,12 +84,13 @@ const AddAdmin = () => {
localStorage.setItem("userSettings", userSettings); localStorage.setItem("userSettings", userSettings);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
localStorage.setItem("favicon", favicon);
}; };
const handleSubmit = async (event) => { const handleSubmit = async (event) => {
event.preventDefault(); event.preventDefault();
if (!emailRegex.test(email)) { if (!emailRegex.test(email)) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
} else { } else {
if (lengthValid && caseDigitValid && specialCharValid) { if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage(); clearStorage();
@@ -265,7 +266,6 @@ const AddAdmin = () => {
}; };
return ( return (
<div className="h-screen flex justify-center"> <div className="h-screen flex justify-center">
<Title title="Add admin" />
{state.loading ? ( {state.loading ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl"> <div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader /> <Loader />
+1 -3
View File
@@ -1,7 +1,6 @@
import React, { useState } from "react"; import { useState } from "react";
import Parse from "parse"; import Parse from "parse";
import { Navigate } from "react-router"; import { Navigate } from "react-router";
import Title from "../components/Title";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function ChangePassword() { function ChangePassword() {
@@ -56,7 +55,6 @@ function ChangePassword() {
} }
return ( return (
<div className="w-full bg-base-100 text-base-content shadow rounded-box p-2"> <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"> <div className="text-xl font-bold border-b-[1px] border-gray-300">
{t("change-password")} {t("change-password")}
</div> </div>
-2
View File
@@ -1,7 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import GetDashboard from "../components/dashboard/GetDashboard"; import GetDashboard from "../components/dashboard/GetDashboard";
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import Title from "../components/Title";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer"; import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson"; import dashboardJson from "../json/dashboardJson";
@@ -57,7 +56,6 @@ const Dashboard = () => {
return ( return (
<React.Fragment> <React.Fragment>
<Title title="Dashboard" />
{loading ? ( {loading ? (
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md"> <div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
<Loader /> <Loader />
+4 -4
View File
@@ -1,12 +1,12 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import Title from "../components/Title";
import RenderDebugPdf from "../components/RenderDebugPdf"; import RenderDebugPdf from "../components/RenderDebugPdf";
import { pdfjs } from "react-pdf"; import { pdfjs } from "react-pdf";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import HandleError from "../primitives/HandleError"; import HandleError from "../primitives/HandleError";
import { useWindowSize } from "../hook/useWindowSize"; import { useWindowSize } from "../hook/useWindowSize";
import { useTranslation } from "react-i18next";
function processDimensions(x, y, width, height) { function processDimensions(x, y, width, height) {
if (width < 0) { if (width < 0) {
@@ -27,6 +27,7 @@ function processDimensions(x, y, width, height) {
}; };
} }
const DebugPdf = () => { const DebugPdf = () => {
const { t } = useTranslation();
const { width } = useWindowSize(); const { width } = useWindowSize();
const [pdf, setPdf] = useState(""); const [pdf, setPdf] = useState("");
const [isModal, setIsModal] = useState(true); const [isModal, setIsModal] = useState(true);
@@ -215,8 +216,7 @@ const DebugPdf = () => {
}; };
return ( return (
<div> <div>
{copied && <Alert type="success">Copied</Alert>} {copied && <Alert type="success">{t("copied")}</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? ( {width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} /> <HandleError handleError={"Debug PDF only availble for PC"} />
) : ( ) : (
+25 -20
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Confetti from "react-confetti"; // Import the confetti library import Confetti from "react-confetti"; // Import the confetti library
import { import {
@@ -10,7 +10,6 @@ import {
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import DownloadPdfZip from "../primitives/DownloadPdfZip"; import DownloadPdfZip from "../primitives/DownloadPdfZip";
import Title from "../components/Title";
import CheckCircle from "../primitives/CheckCircle"; import CheckCircle from "../primitives/CheckCircle";
const DocSuccessPage = () => { const DocSuccessPage = () => {
@@ -42,7 +41,7 @@ const DocSuccessPage = () => {
objectId: docId, objectId: docId,
SignedUrl: docUrl, SignedUrl: docUrl,
CertificateUrl: certificate, CertificateUrl: certificate,
IsCompleted: completed IsCompleted: completed,
}; };
setPdfDetails([details]); setPdfDetails([details]);
const base64Pdf = await getBase64FromUrl(docUrl); const base64Pdf = await getBase64FromUrl(docUrl);
@@ -53,7 +52,9 @@ const DocSuccessPage = () => {
}; };
const handleDownload = () => { const handleDownload = () => {
if (pdfDetails?.[0]?.IsCompleted) { if (
pdfDetails?.[0]?.IsCompleted
) {
setIsDownloadModal(true); setIsDownloadModal(true);
} else { } else {
handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url); handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url);
@@ -62,7 +63,6 @@ const DocSuccessPage = () => {
return ( return (
<> <>
<Title title="Success" />
{/* Confetti Effect */} {/* Confetti Effect */}
{showConfetti && ( {showConfetti && (
<Confetti width={window.innerWidth} height={window.innerHeight} /> <Confetti width={window.innerWidth} height={window.innerHeight} />
@@ -101,21 +101,26 @@ const DocSuccessPage = () => {
<span>{t("download")}</span> <span>{t("download")}</span>
</button> </button>
{pdfDetails?.[0]?.IsCompleted && ( {
<button pdfDetails?.[0]?.IsCompleted && (
type="button" <button
onClick={() => type="button"
handleDownloadCertificate(pdfDetails, setIsDownloading) onClick={() =>
} handleDownloadCertificate(
className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary" pdfDetails,
> setIsDownloading
<i )
className="fa-light fa-award mx-[3px] md:mx-0" }
aria-hidden="true" className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary"
></i> >
<span>{t("certificate")}</span> <i
</button> className="fa-light fa-award mx-[3px] md:mx-0"
)} aria-hidden="true"
></i>
<span>{t("certificate")}</span>
</button>
)
}
<button <button
onClick={(e) => onClick={(e) =>
handleToPrint(e, setIsDownloading, pdfDetails) handleToPrint(e, setIsDownloading, pdfDetails)
+2 -4
View File
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Title from "../components/Title";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import login_img from "../assets/images/login_img.svg"; import login_img from "../assets/images/login_img.svg";
import Parse from "parse"; import Parse from "parse";
@@ -40,7 +39,7 @@ function ForgotPassword() {
const handleSubmit = async (event) => { const handleSubmit = async (event) => {
event.preventDefault(); event.preventDefault();
if (!emailRegex.test(state.email)) { if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
} else { } else {
setIsLoading(true); setIsLoading(true);
localStorage.setItem("appLogo", appInfo.applogo); localStorage.setItem("appLogo", appInfo.applogo);
@@ -87,7 +86,6 @@ function ForgotPassword() {
<Loader /> <Loader />
</div> </div>
)} )}
<Title title="Forgot password" />
{toast?.message && <Alert type={toast.type}>{toast.message}</Alert>} {toast?.message && <Alert type={toast.type}>{toast.message}</Alert>}
<div className="md:p-10 lg:p-16"> <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"> <div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
+48 -28
View File
@@ -5,7 +5,6 @@ import Parse from "parse";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import SelectFolder from "../components/shared/fields/SelectFolder"; import SelectFolder from "../components/shared/fields/SelectFolder";
import SignersInput from "../components/shared/fields/SignersInput"; import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound"; import PageNotFound from "./PageNotFound";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
import { import {
@@ -31,6 +30,8 @@ import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip"; import { Tooltip } from "react-tooltip";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { sessionStatus } from "../redux/reducers/userReducer";
// `Form` render all type of Form on this basis of their provided in path // `Form` render all type of Form on this basis of their provided in path
function Form() { function Form() {
@@ -51,6 +52,7 @@ const Forms = (props) => {
const abortController = new AbortController(); const abortController = new AbortController();
const inputFileRef = useRef(null); const inputFileRef = useRef(null);
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch();
const [signers, setSigners] = useState([]); const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" }); const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
@@ -68,7 +70,7 @@ const Forms = (props) => {
NotifyOnSignatures: "", NotifyOnSignatures: "",
Bcc: [], Bcc: [],
RedirectUrl: "", RedirectUrl: "",
AllowModifications: false AllowModifications: false,
}); });
const [fileupload, setFileUpload] = useState(""); const [fileupload, setFileUpload] = useState("");
const [selectedFiles, setSelectedFiles] = useState([]); const [selectedFiles, setSelectedFiles] = useState([]);
@@ -99,6 +101,8 @@ const Forms = (props) => {
extUserData?.IsTourEnabled === false extUserData?.IsTourEnabled === false
? "false" ? "false"
: "true"; : "true";
const fileSize =
maxFileSize;
useEffect(() => { useEffect(() => {
handleReset(); handleReset();
return () => abortController.abort(); return () => abortController.abort();
@@ -137,11 +141,10 @@ const Forms = (props) => {
return; return;
} }
// setFormData((prev) => ({ ...prev, file: files[0] })); // setFormData((prev) => ({ ...prev, file: files[0] }));
const totalMb = Math.round( const totalBytes = Math.round(files.reduce((sum, f) => sum + f.size, 0)); // in bytes
files.reduce((sum, f) => sum + f.size, 0) / Math.pow(1024, 2) const fileSizeBytes = fileSize * 1024 * 1024;
); if (totalBytes > fileSizeBytes) {
if (totalMb > maxFileSize) { alert(`${t("file-alert-1")} ${fileSize} MB`);
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
setFileUpload(""); setFileUpload("");
setSelectedFiles([]); setSelectedFiles([]);
removeFile(e); removeFile(e);
@@ -195,7 +198,9 @@ const Forms = (props) => {
setIsDecrypting(false); setIsDecrypting(false);
setfileload(false); setfileload(false);
removeFile(e); removeFile(e);
alert(`Incorrect password for file: ${file.name}`); alert(
t("incorrect-password-for-file", { file: file.name })
);
} }
} else { } else {
console.error("password not provided"); console.error("password not provided");
@@ -273,10 +278,13 @@ const Forms = (props) => {
setfileload(false); setfileload(false);
removeFile(e); removeFile(e);
console.log("err in docx to pdf ", err); console.log("err in docx to pdf ", err);
const error = isOpenSignDomain const error =
? `${t("docx-error")} ${t("docx-error-contact")}` t("docx-error");
: t("docx-error"); if (err?.code === 209) {
alert(error); dispatch(sessionStatus(false));
} else {
alert(error);
}
return; return;
} }
} }
@@ -321,7 +329,8 @@ const Forms = (props) => {
} }
if (uploadedUrl) { if (uploadedUrl) {
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
SaveFileSize(pdfBytes.byteLength, uploadedUrl, tenantId); const userId = extUserData?.UserId?.objectId;
SaveFileSize(pdfBytes.byteLength, uploadedUrl, tenantId, userId);
setFileUpload(uploadedUrl); setFileUpload(uploadedUrl);
setfileload(false); setfileload(false);
const title = generateTitleFromFilename(filesNameArr?.[0]); const title = generateTitleFromFilename(filesNameArr?.[0]);
@@ -333,8 +342,13 @@ const Forms = (props) => {
setSelectedFiles([]); setSelectedFiles([]);
} }
} catch (error) { } catch (error) {
alert(error.message); if (error?.code === 209) {
dispatch(sessionStatus(false));
} else {
alert(error.message);
}
setSelectedFiles([]); setSelectedFiles([]);
removeFile(e);
} }
}; };
// `isValidURL` is used to check valid webhook url // `isValidURL` is used to check valid webhook url
@@ -460,7 +474,7 @@ const Forms = (props) => {
IsEnableOTP: "false", IsEnableOTP: "false",
IsTourEnabled: istourenabled, IsTourEnabled: istourenabled,
RedirectUrl: "", RedirectUrl: "",
AllowModifications: false AllowModifications: false,
}); });
setFileUpload(""); setFileUpload("");
setSelectedFiles([]); setSelectedFiles([]);
@@ -469,7 +483,9 @@ const Forms = (props) => {
} }
} catch (err) { } catch (err) {
console.log("err ", err); console.log("err ", err);
if (err.message === "only 15 reminder allowed") { if (err?.code === 209) {
dispatch(sessionStatus(false));
} else if (err.message === "only 15 reminder allowed") {
setIsAlert({ setIsAlert({
type: "danger", type: "danger",
message: t("only-15-reminder-allowed") message: t("only-15-reminder-allowed")
@@ -540,7 +556,7 @@ const Forms = (props) => {
IsTourEnabled: istourenabled, IsTourEnabled: istourenabled,
NotifyOnSignatures: notifySign, NotifyOnSignatures: notifySign,
RedirectUrl: "", RedirectUrl: "",
AllowModifications: false AllowModifications: false,
}; };
setFormData(obj); setFormData(obj);
removeFile(); removeFile();
@@ -555,6 +571,8 @@ const Forms = (props) => {
e.preventDefault(); e.preventDefault();
setIsPassword(false); setIsPassword(false);
setfileload(true); setfileload(true);
const tenantId = localStorage.getItem("TenantId");
const userId = extUserData?.UserId?.objectId;
try { try {
const size = formData?.file?.size; const size = formData?.file?.size;
const name = generatePdfName(16); const name = generatePdfName(16);
@@ -579,8 +597,7 @@ const Forms = (props) => {
removeFile(); removeFile();
const title = generateTitleFromFilename(formData?.file?.name); const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, password: "", Name: title })); setFormData((obj) => ({ ...obj, password: "", Name: title }));
const tenantId = localStorage.getItem("TenantId"); SaveFileSize(size, fileRes?.url, tenantId, userId);
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url; return fileRes.url;
} else { } else {
removeFile(); removeFile();
@@ -600,7 +617,9 @@ const Forms = (props) => {
} }
} catch (err) { } catch (err) {
removeFile(); removeFile();
if (err?.response?.status === 401) { if (err?.code === 209) {
dispatch(sessionStatus(false));
} else if (err?.response?.status === 401) {
setIsPassword(true); setIsPassword(true);
setIsCorrectPass(false); setIsCorrectPass(false);
} else { } else {
@@ -629,11 +648,12 @@ const Forms = (props) => {
}; };
return ( return (
<div className="shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content"> <div
<Title title={props?.title} /> 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>} {isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
{isSubmit ? ( {isSubmit ? (
<div className="h-[300px] flex justify-center items-center"> <div className="flex flex-col justify-center items-center h-[100vh]">
<Loader /> <Loader />
</div> </div>
) : ( ) : (
@@ -680,9 +700,7 @@ const Forms = (props) => {
</ModalUi> </ModalUi>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="mb-[11px]"> <div className="mb-[11px]">
<h1 className="text-[20px] font-semibold"> <h1 className="text-[20px] font-semibold">{t(props?.title)}</h1>
{t(`form-name.${props?.title}`)}
</h1>
{props.title === "Sign Yourself" && ( {props.title === "Sign Yourself" && (
<div className="text-gray-500 text-xs mt-1"> <div className="text-gray-500 text-xs mt-1">
{t("signyour-self-description")} {t("signyour-self-description")}
@@ -793,6 +811,7 @@ const Forms = (props) => {
onChange={handleSigners} onChange={handleSigners}
isReset={isReset} isReset={isReset}
helptextZindex={50} helptextZindex={50}
isAddYourSelfCheckbox
required required
/> />
)} )}
@@ -1029,6 +1048,7 @@ const Forms = (props) => {
helptextZindex={50} helptextZindex={50}
helpText={t("bcc-help")} helpText={t("bcc-help")}
isCaptureAllData isCaptureAllData
isAddYourSelfCheckbox
/> />
)} )}
</> </>
@@ -1046,7 +1066,7 @@ const Forms = (props) => {
></div> ></div>
)} )}
{isAdvanceOpt && ( {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 && {formData?.autoreminder === true &&
props.title !== "New Template" && ( props.title !== "New Template" && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
@@ -1223,7 +1243,7 @@ const Forms = (props) => {
{t("next")} {t("next")}
</button> </button>
<div <div
className="op-btn op-btn-ghost" className="op-btn op-btn-ghost text-base-content"
onClick={() => handleCancel()} onClick={() => handleCancel()}
> >
{t("cancel")} {t("cancel")}
+84 -25
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import axios from "axios"; import axios from "axios";
import { import {
@@ -14,7 +14,6 @@ import Parse from "parse";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage"; import SelectLanguage from "../components/pdf/SelectLanguage";
import LoaderWithMsg from "../primitives/LoaderWithMsg"; import LoaderWithMsg from "../primitives/LoaderWithMsg";
import Title from "../components/Title";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
@@ -36,8 +35,14 @@ function GuestLogin() {
const [documentId, setDocumentId] = useState(id); const [documentId, setDocumentId] = useState(id);
const [contactId, setContactId] = useState(contactBookId); const [contactId, setContactId] = useState(contactBookId);
const [sendmail, setSendmail] = useState(); const [sendmail, setSendmail] = useState();
const [contact, setContact] = useState({ name: "", phone: "", email: "" }); const [contact, setContact] = useState({
name: "",
phone: "",
email: "",
jobTitle: "",
company: ""
});
const [isOptionalDetails, setIsOptionalDetails] = useState(false);
const navigateToDoc = async (docId, contactId) => { const navigateToDoc = async (docId, contactId) => {
try { try {
@@ -72,8 +77,10 @@ function GuestLogin() {
//function generate serverUrl and parseAppId from url and save it in local storage //function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = async () => { const handleServerUrl = async () => {
setAppLogo(logo); setAppLogo(logo);
const favicon = localStorage.getItem("favicon");
localStorage.clear(); // Clears everything localStorage.clear(); // Clears everything
localStorage.setItem("favicon", favicon);
localStorage.setItem( localStorage.setItem(
"appname", "appname",
"OpenSign™" "OpenSign™"
@@ -214,7 +221,7 @@ function GuestLogin() {
const handleUserData = async (e) => { const handleUserData = async (e) => {
e.preventDefault(); e.preventDefault();
if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) { if (!emailRegex.test(contact.email?.toLowerCase()?.replace(/\s/g, ""))) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
} else { } else {
const params = { ...contact, docId: documentId }; const params = { ...contact, docId: documentId };
try { try {
@@ -253,7 +260,6 @@ function GuestLogin() {
return ( return (
<div> <div>
<Title title="Request Sign" />
{/* OTP Verification Modal */} {/* OTP Verification Modal */}
{EnterOTP && ( {EnterOTP && (
@@ -356,7 +362,7 @@ function GuestLogin() {
<div className="mb-2"> <div className="mb-2">
<label <label
htmlFor="name" htmlFor="name"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("name")} {t("name")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
@@ -372,13 +378,14 @@ function GuestLogin() {
e.target.setCustomValidity(t("input-required")) e.target.setCustomValidity(t("input-required"))
} }
onInput={(e) => e.target.setCustomValidity("")} onInput={(e) => e.target.setCustomValidity("")}
placeholder={t("enter-name")}
required required
/> />
</div> </div>
<div className="mb-2"> <div className="mb-2">
<label <label
htmlFor="email" htmlFor="email"
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("email")} {t("email")}
<span className="text-[red] text-[13px]"> *</span> <span className="text-[red] text-[13px]"> *</span>
@@ -389,27 +396,79 @@ function GuestLogin() {
value={contact.email} value={contact.email}
onChange={handleInputChange} onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
placeholder={t("enter-email")}
required required
disabled disabled
/> />
</div> </div>
<div className="mt-2.5"> {isOptionalDetails && (
<label <>
htmlFor="phone" <div className="mb-2">
className="block text-xs text-gray-700 font-semibold" <label
> htmlFor="phone"
{t("phone")} className="block text-xs font-semibold"
</label> >
<input {t("phone")}
type="text" </label>
name="phone" <input
value={contact.phone} type="text"
onChange={handleInputChange} name="phone"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" value={contact.phone}
disabled={loading} onChange={handleInputChange}
placeholder={t("phone-optional")} className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/> disabled={loading}
</div> 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"> <div className="mt-2 flex justify-start">
<button <button
type="submit" type="submit"
+15 -11
View File
@@ -1,8 +1,7 @@
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import axios from "axios"; import axios from "axios";
import Title from "../components/Title";
import { NavLink, useNavigate, useLocation } from "react-router"; import { NavLink, useNavigate, useLocation } from "react-router";
import login_img from "../assets/images/login_img.svg"; import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize"; import { useWindowSize } from "../hook/useWindowSize";
@@ -125,13 +124,17 @@ function Login() {
} }
} catch (error) { } catch (error) {
console.error("Error while logging in user", 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) => { const handleLoginBtn = async (event) => {
event.preventDefault(); event.preventDefault();
if (!emailRegex.test(state.email)) { if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address."); alert(t("valid-email-alert"));
return; return;
} }
await handleLogin(); await handleLogin();
@@ -329,6 +332,7 @@ function Login() {
let PageLanding = localStorage.getItem("PageLanding"); let PageLanding = localStorage.getItem("PageLanding");
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
let favicon = localStorage.getItem("favicon");
localStorage.clear(); localStorage.clear();
saveLanguageInLocal(i18n); saveLanguageInLocal(i18n);
@@ -339,6 +343,7 @@ function Login() {
localStorage.setItem("userSettings", appdata); localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
localStorage.setItem("favicon", favicon);
}; };
const continueLoginFlow = async () => { const continueLoginFlow = async () => {
@@ -403,8 +408,7 @@ function Login() {
{errMsg} {errMsg}
</div> </div>
) : ( ) : (
<div> <>
<Title title="Login" />
{state.loading && ( {state.loading && (
<div <div
aria-live="assertive" aria-live="assertive"
@@ -495,7 +499,7 @@ function Login() {
to="/forgetpassword" to="/forgetpassword"
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1" className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
> >
{t("forgot-password")} {t("forgot-password")}?
</NavLink> </NavLink>
</div> </div>
</div> </div>
@@ -539,7 +543,7 @@ function Login() {
<label <label
htmlFor="Company" htmlFor="Company"
style={{ display: "flex" }} style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("company")}{" "} {t("company")}{" "}
<span className="text-[red] text-[13px]">*</span> <span className="text-[red] text-[13px]">*</span>
@@ -566,7 +570,7 @@ function Login() {
<label <label
htmlFor="JobTitle" htmlFor="JobTitle"
style={{ display: "flex" }} style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold" className="block text-xs font-semibold"
> >
{t("job-title")} {t("job-title")}
<span className="text-[red] text-[13px]">*</span> <span className="text-[red] text-[13px]">*</span>
@@ -599,7 +603,7 @@ function Login() {
</button> </button>
<button <button
type="button" type="button"
className="op-btn op-btn-ghost" className="op-btn op-btn-ghost text-base-content"
onClick={logOutUser} onClick={logOutUser}
> >
{t("cancel")} {t("cancel")}
@@ -616,7 +620,7 @@ function Login() {
<Loader /> <Loader />
</div> </div>
)} )}
</div> </>
); );
} }
export default Login; export default Login;
+26 -56
View File
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas"; import SignatureCanvas from "react-signature-canvas";
import "../styles/managesign.css"; import "../styles/managesign.css";
import "../styles/signature.css"; import "../styles/signature.css";
@@ -12,8 +12,7 @@ import { SaveFileSize } from "../constant/saveFileSize";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import sanitizeFileName from "../primitives/sanitizeFileName"; import { sanitizeFileName } from "../utils";
import Title from "../components/Title";
const ManageSign = () => { const ManageSign = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [penColor, setPenColor] = useState("blue"); const [penColor, setPenColor] = useState("blue");
@@ -47,10 +46,9 @@ const ManageSign = () => {
objectId: User.id objectId: User.id
}; };
try { try {
const signCls = "contracts_Signature"; const signRes = await Parse.Cloud.run("getdefaultsignature", {
const signQuery = new Parse.Query(signCls); userId: User.id
signQuery.equalTo("UserId", userId); });
const signRes = await signQuery.first();
if (signRes) { if (signRes) {
const res = signRes.toJSON(); const res = signRes.toJSON();
setId(res.objectId); setId(res.objectId);
@@ -206,7 +204,8 @@ const ManageSign = () => {
const fileRes = await getSecureUrl(response?.url()); const fileRes = await getSecureUrl(response?.url());
if (fileRes?.url) { if (fileRes?.url) {
const tenantId = localStorage.getItem("TenantId"); const tenantId = localStorage.getItem("TenantId");
SaveFileSize(file.size, fileRes?.url, tenantId); const userId = Parse?.User?.current()?.id;
SaveFileSize(file.size, fileRes?.url, tenantId, userId);
return fileRes?.url; return fileRes?.url;
} else { } else {
alert(t("something-went-wrong-mssg")); alert(t("something-went-wrong-mssg"));
@@ -226,44 +225,23 @@ const ManageSign = () => {
}; };
const saveEntry = async (obj) => { const saveEntry = async (obj) => {
const signCls = "contracts_Signature"; try {
const User = Parse?.User?.current()?.id; const User = Parse?.User?.current()?.id;
const userId = { __type: "Pointer", className: "_User", objectId: User }; const res = await Parse.Cloud.run("managesign", {
if (id) { signature: obj.url,
try { userId: User,
const updateSign = new Parse.Object(signCls); initials: obj.initialsUrl,
updateSign.id = id; id: id,
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : ""); title: obj.name
updateSign.set("ImageURL", obj.url ? obj.url : ""); });
updateSign.set("SignatureName", obj.name); setIsAlert({ type: "success", message: t("signature-saved-alert") });
updateSign.set("UserId", userId); return res;
const res = await updateSign.save(); } catch (err) {
setIsAlert({ type: "success", message: t("signature-saved-alert") }); console.log(err);
return res; setIsAlert({ type: "danger", message: `${err.message}` });
} catch (err) { } finally {
console.log(err); setIsLoader(false);
setIsAlert({ type: "danger", message: `${err.message}` }); setTimeout(() => setIsAlert({}), 2000);
} finally {
setIsLoader(false);
setTimeout(() => setIsAlert({}), 2000);
}
} else {
try {
const updateSign = new Parse.Object(signCls);
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : "");
updateSign.set("ImageURL", obj.url);
updateSign.set("SignatureName", obj.name);
updateSign.set("UserId", userId);
const res = await updateSign.save();
setIsAlert({ type: "success", message: t("signature-saved-alert") });
return res;
} catch (err) {
console.log(err);
setIsAlert({ type: "success", message: `${err.message}` });
} finally {
setIsLoader(false);
setTimeout(() => setIsAlert({}), 2000);
}
} }
}; };
@@ -298,7 +276,6 @@ const ManageSign = () => {
}; };
return ( return (
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto"> <div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
<Title title="My signature" />
{isLoader && ( {isLoader && (
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center"> <div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
<Loader /> <Loader />
@@ -324,13 +301,8 @@ const ManageSign = () => {
hidden hidden
/> />
<div className="relative"> <div className="relative">
{isLoader && (
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
<Loader />
</div>
)}
{image ? ( {image ? (
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden"> <div className="mysignatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden">
<img <img
alt="signature" alt="signature"
src={image} src={image}
@@ -342,10 +314,8 @@ const ManageSign = () => {
ref={canvasRef} ref={canvasRef}
penColor={penColor} penColor={penColor}
canvasProps={{ canvasProps={{
width: "456px",
height: "180px",
className: className:
"signatureCanvas border-[2px] border-[#888] rounded-box" "mysignatureCanvas border-[2px] border-[#888] rounded-box"
}} }}
onEnd={() => onEnd={() =>
handleSignatureChange(canvasRef.current.toDataURL()) handleSignatureChange(canvasRef.current.toDataURL())
+72 -47
View File
@@ -1,4 +1,5 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef, useCallback } from "react";
import { lazyWithRetry } from "../utils";
import "../styles/opensigndrive.css"; import "../styles/opensigndrive.css";
import { import {
getThemeIconColor, getThemeIconColor,
@@ -7,7 +8,6 @@ import {
getDrive getDrive
} from "../constant/Utils"; } from "../constant/Utils";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import Title from "../components/Title";
import Parse from "parse"; import Parse from "parse";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn"; import TourContentWithBtn from "../primitives/TourContentWithBtn";
@@ -16,7 +16,7 @@ import axios from "axios";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const DriveBody = React.lazy( const DriveBody = lazyWithRetry(
() => import("../components/opensigndrive/DriveBody") () => import("../components/opensigndrive/DriveBody")
); );
const dropdowncss = const dropdowncss =
@@ -35,6 +35,8 @@ function Opensigndrive() {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const scrollRef = useRef(null); const scrollRef = useRef(null);
// Create a ref for the "sentinel" element at the bottom of the list
const bottomRef = useRef(null);
const [isList, setIsList] = useState(false); const [isList, setIsList] = useState(false);
const [selectedSort, setSelectedSort] = useState("Date"); const [selectedSort, setSelectedSort] = useState("Date");
const [sortingOrder, setSortingOrder] = useState("Descending"); const [sortingOrder, setSortingOrder] = useState("Descending");
@@ -60,7 +62,7 @@ function Opensigndrive() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const sortOrder = ["Ascending", "Descending"]; const sortOrder = ["Ascending", "Descending"];
const sortingValue = ["Name", "Date"]; const sortingValue = ["Name", "Date"];
const [isDontShow, setIsDontShow] = useState(false); const [isDontShow, setIsDontShow] = useState(true);
const [tourData, setTourData] = useState(); const [tourData, setTourData] = useState();
const [showTourFirstTIme, setShowTourFirstTime] = useState(true); const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
@@ -221,39 +223,38 @@ function Opensigndrive() {
} }
}; };
//function to fetch drive details list on scroll bottom // Memoize the loader function so it only changes when deps change
const handleScroll = () => { const loadMore = useCallback(() => {
//get document of render openSign-drive component using id // Only fetch if we're not already loading and we've got a full "page" (50 items)
const documentList = document.getElementById("renderList"); if (!loading && pdfData.length % 50 === 0) {
const { scrollTop, clientHeight, scrollHeight } = documentList; // Pass `true` to disable the initial loader UI
const scrolled = Math.ceil(scrollTop + clientHeight); // ceil return e.g 3.14 => 4 getPdfDocumentList(true);
const totalHeight = Math.floor(scrollHeight); // floor return e.g 3.14 => 3
// clientHeight property returns the height of an element's content area, including padding but not including borders, margins, or scrollbars.
// scrollHeight property returns the entire height of an element,including the parts that are not visible due to overflow..
// scrollTop property show height of element, how much the content has been scrolled from the top.
// When the sum of scrollTop and clientHeight is equal to scrollHeight, it means that the user has scrolled to the bottom of the div.
if (scrolled >= totalHeight) {
//disableLoading is used disable initial loader
const disableLoading = true;
// If the fetched data length is less than the limit, it means there's no more data to fetch
if (!loading && pdfData.length % 50 === 0) {
getPdfDocumentList(disableLoading);
}
} }
}; }, [loading, pdfData.length, getPdfDocumentList]);
//useEffect is used to call handleScroll function on scrolling event
useEffect(() => { useEffect(() => {
const documentList = document.getElementById("renderList"); const container = document.getElementById("renderList") || null;
if (documentList) { if (!container) return;
documentList.addEventListener("scroll", handleScroll); // Set up the IntersectionObserver
return () => { const observer = new IntersectionObserver(
documentList.removeEventListener("scroll", handleScroll); ([entry]) => {
}; // 5. When the sentinel comes fully into view, trigger loadMore()
} if (entry.isIntersecting) loadMore();
// eslint-disable-next-line },
}, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes {
root: container, // Determine the scroll container (null = viewport)
rootMargin: "0px", // no extra margin
threshold: 1.0 // fire when 100% of sentinel is visible
}
);
// Start observing the sentinel DOM node
if (bottomRef.current) observer.observe(bottomRef.current);
// Clean up on unmount or if loadMore changes
return () => {
if (bottomRef.current) observer.unobserve(bottomRef.current);
observer.disconnect();
};
}, [loadMore]);
//function for handle folder name path //function for handle folder name path
const handleRoute = (index, folderData) => { const handleRoute = (index, folderData) => {
@@ -554,9 +555,27 @@ function Opensigndrive() {
}; };
}, []); }, []);
const handleHighlightClick = () => {
setIsTour(false);
setShowTourFirstTime(false);
};
const handleFolderOptions = () => {
setIsOptions(!isOptions);
handleHighlightClick();
};
const handleSortOptions = () => {
setIsShowSort(!isShowSort);
handleHighlightClick();
};
const handleViewOption = () => {
setIsList(!isList);
handleHighlightClick();
};
return ( return (
<div className="bg-base-100 text-base-content rounded-box w-full shadow-md"> <div className="bg-base-100 text-base-content rounded-box w-full shadow-md">
<Title title={`${drivename} Drive`} drive={true} />
<ModalUi <ModalUi
isOpen={isAlert.isShow} isOpen={isAlert.isShow}
title={t("alert")} title={t("alert")}
@@ -614,7 +633,7 @@ function Opensigndrive() {
</button> </button>
<button <button
type="button" type="button"
className="op-btn op-btn-ghost ml-1" className="op-btn op-btn-ghost text-base-content ml-1"
onClick={oncloseFolder} onClick={oncloseFolder}
> >
{t("close")} {t("close")}
@@ -643,14 +662,13 @@ function Opensigndrive() {
onRequestClose={closeTour} onRequestClose={closeTour}
steps={tourData} steps={tourData}
isOpen={isTour} isOpen={isTour}
closeWithMask={false}
scrollOffset={-100} scrollOffset={-100}
rounded={5}
/> />
)} )}
<div <div
data-tut="reactourFirst" data-tut="reactourFirst"
onMouseEnter={(e) => handleMouseEnter(e)} onMouseEnter={(e) => handleMouseEnter(e)}
onClick={handleHighlightClick}
ref={scrollRef} ref={scrollRef}
className="w-full whitespace-nowrap cursor-pointer select-none overflow-x-auto" className="w-full whitespace-nowrap cursor-pointer select-none overflow-x-auto"
> >
@@ -682,7 +700,7 @@ function Opensigndrive() {
<div <div
id="folder-menu" id="folder-menu"
className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`} className={`${isOptions ? "dropdown show dropDownStyle" : "dropdown"} hidden md:block cursor-pointer hover:bg-gray-200 p-2 rounded-md`}
onClick={() => setIsOptions(!isOptions)} onClick={handleFolderOptions}
> >
<div data-tut="reactourSecond"> <div data-tut="reactourSecond">
<i <i
@@ -712,14 +730,14 @@ function Opensigndrive() {
onClick={() => navigate("/form/sHAnZphf69")} onClick={() => navigate("/form/sHAnZphf69")}
> >
<i className="fa-light fa-pen-nib mr-[5px]"></i> <i className="fa-light fa-pen-nib mr-[5px]"></i>
{t("form-name.Sign Yourself")} {t("Sign Yourself")}
</span> </span>
<span <span
className="dropdown-item text-[10px] md:text-[13px]" className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/8mZzFxbG1z")} onClick={() => navigate("/form/8mZzFxbG1z")}
> >
<i className="fa-light fa-file-signature mr-[5px]"></i> <i className="fa-light fa-file-signature mr-[5px]"></i>
{t("form-name.Request Signatures")} {t("Request Signatures")}
</span> </span>
</div> </div>
</div> </div>
@@ -727,7 +745,7 @@ function Opensigndrive() {
<div <div
id="menu-container" id="menu-container"
className={isShowSort ? "dropdown show" : "dropdown"} className={isShowSort ? "dropdown show" : "dropdown"}
onClick={() => setIsShowSort(!isShowSort)} onClick={handleSortOptions}
> >
<div <div
data-tut="reactourThird" data-tut="reactourThird"
@@ -739,7 +757,12 @@ function Opensigndrive() {
aria-hidden="true" aria-hidden="true"
style={{ color: `${getThemeIconColor()}` }} style={{ color: `${getThemeIconColor()}` }}
></i> ></i>
<span style={{ fontSize: "15px", color: `${getThemeIconColor()}` }}> <span
style={{
fontSize: "15px",
color: `${getThemeIconColor()}`
}}
>
{selectedSort} {selectedSort}
</span> </span>
</div> </div>
@@ -796,7 +819,7 @@ function Opensigndrive() {
<div <div
className="cursor-pointer p-2 hover:bg-gray-200 rounded-md flex justify-center items-center" className="cursor-pointer p-2 hover:bg-gray-200 rounded-md flex justify-center items-center"
data-tut="reactourForth" data-tut="reactourForth"
onClick={() => setIsList(!isList)} onClick={handleViewOption}
> >
<i <i
className={`${isList ? "fa-light fa-th-large" : "fa-light fa-list"} text-[20px]`} className={`${isList ? "fa-light fa-th-large" : "fa-light fa-list"} text-[20px]`}
@@ -840,14 +863,14 @@ function Opensigndrive() {
onClick={() => navigate("/form/sHAnZphf69")} onClick={() => navigate("/form/sHAnZphf69")}
> >
<i className="fa-light fa-pen-nib mr-[5px]"></i> <i className="fa-light fa-pen-nib mr-[5px]"></i>
{t("form-name.Sign Yourself")} {t("Sign Yourself")}
</span> </span>
<span <span
className="dropdown-item text-[10px] md:text-[13px]" className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/8mZzFxbG1z")} onClick={() => navigate("/form/8mZzFxbG1z")}
> >
<i className="fa-light fa-file-signature mr-[5px]"></i> <i className="fa-light fa-file-signature mr-[5px]"></i>
{t("form-name.Request Signatures")} {t("Request Signatures")}
</span> </span>
</div> </div>
</div> </div>
@@ -891,6 +914,8 @@ function Opensigndrive() {
setSkip={setSkip} setSkip={setSkip}
sortingData={sortingData} sortingData={sortingData}
/> />
{/* sentinel */}
<div ref={bottomRef} className="h-1" />
{loading && ( {loading && (
<div className="text-center pb-[20px]">{t("loading")}</div> <div className="text-center pb-[20px]">{t("loading")}</div>
)} )}
+3 -5
View File
@@ -1,15 +1,13 @@
// NotFound.js import { useTranslation } from "react-i18next";
import React from "react";
import Title from "../components/Title";
const PageNotFound = ({ prefix }) => { const PageNotFound = ({ prefix }) => {
const { t } = useTranslation();
return ( return (
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box"> <div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
<Title title={"Page Not Found"} />
<div className="text-center"> <div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1> <h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1>
<p className="text-[30px] lg:text-[50px]"> <p className="text-[30px] lg:text-[50px]">
{prefix ? prefix : "Page"} Not Found {prefix ? `${prefix} Not Found` : t("page-not-found")}
</p> </p>
</div> </div>
</div> </div>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+27 -9
View File
@@ -1,5 +1,4 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import Title from "../components/Title";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
@@ -12,7 +11,6 @@ import {
} from "../constant/Utils"; } from "../constant/Utils";
import Parse from "parse"; import Parse from "parse";
import { Tooltip as ReactTooltip } from "react-tooltip"; import { Tooltip as ReactTooltip } from "react-tooltip";
import TimezoneSelector from "../components/shared/fields/TimezoneSelector";
import ReactQuill from "react-quill-new"; import ReactQuill from "react-quill-new";
import "../styles/quill.css"; import "../styles/quill.css";
import EditorToolbar, { import EditorToolbar, {
@@ -20,7 +18,9 @@ import EditorToolbar, {
module2, module2,
formats formats
} from "../components/pdf/EditorToolbar"; } from "../components/pdf/EditorToolbar";
import DateFormatSelector from "../components/shared/fields/DateFormatSelector"; import TimezoneSelector from "../components/preferences/TimezoneSelector";
import DateFormatSelector from "../components/preferences/DateFormatSelector";
import FilenameFormatSelector from "../components/preferences/FilenameFormatSelector";
const Preferences = () => { const Preferences = () => {
const appName = const appName =
@@ -60,6 +60,8 @@ const Preferences = () => {
requestMail: false, requestMail: false,
completionMail: false completionMail: false
}); });
const [fileNameFormat, setFileNameFormat] = useState("DOCNAME");
useEffect(() => { useEffect(() => {
fetchSignType(); fetchSignType();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -125,6 +127,9 @@ const Preferences = () => {
const isLTVEnabled = const isLTVEnabled =
_getUser?.IsLTVEnabled !== undefined ? _getUser?.IsLTVEnabled : false; _getUser?.IsLTVEnabled !== undefined ? _getUser?.IsLTVEnabled : false;
setIsLTVEnabled(isLTVEnabled); setIsLTVEnabled(isLTVEnabled);
const downloadFilenameFormat =
_getUser?.DownloadFilenameFormat || "DOCNAME";
setFileNameFormat(downloadFilenameFormat);
} }
} catch (err) { } catch (err) {
console.log("err while getting user details", err); console.log("err while getting user details", err);
@@ -164,11 +169,17 @@ const Preferences = () => {
type: "danger", type: "danger",
msg: t("at-least-one-signature-type") msg: t("at-least-one-signature-type")
}); });
setIsLoader(false);
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
return;
} else if (isDefaultSignTypeOnly) { } else if (isDefaultSignTypeOnly) {
setIsAlert({ setIsAlert({
type: "danger", type: "danger",
msg: t("expect-default-one-more-signature-type") msg: t("expect-default-one-signature-type")
}); });
setIsLoader(false);
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
return;
} else { } else {
params = { ...params, SignatureType: signatureType }; params = { ...params, SignatureType: signatureType };
} }
@@ -183,7 +194,8 @@ const Preferences = () => {
IsTourEnabled: isTourEnabled, IsTourEnabled: isTourEnabled,
DateFormat: dateFormat, DateFormat: dateFormat,
Is12HourTime: is12HourTime, Is12HourTime: is12HourTime,
IsLTVEnabled: isLTVEnabled IsLTVEnabled: isLTVEnabled,
DownloadFilenameFormat: fileNameFormat,
}; };
const updateRes = await Parse.Cloud.run("updatepreferences", params); const updateRes = await Parse.Cloud.run("updatepreferences", params);
if (updateRes) { if (updateRes) {
@@ -197,6 +209,7 @@ const Preferences = () => {
extUser.IsTourEnabled = isTourEnabled; extUser.IsTourEnabled = isTourEnabled;
extUser.DateFormat = dateFormat; extUser.DateFormat = dateFormat;
extUser.Is12HourTime = is12HourTime; extUser.Is12HourTime = is12HourTime;
extUser.DownloadFilenameFormat = fileNameFormat;
const _extUser = JSON.parse(JSON.stringify(extUser)); const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("Extand_Class", JSON.stringify([_extUser])); localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
} }
@@ -217,7 +230,7 @@ const Preferences = () => {
}; };
const tenantEmailTemplate = async (tenantRes) => { const tenantEmailTemplate = async (tenantRes) => {
if (tenantRes === "user does not exist!") { if (tenantRes === "user does not exist!") {
alert("User does not exist"); alert(t("user-not-exist"));
} else if (tenantRes) { } else if (tenantRes) {
setIsLoader(true); setIsLoader(true);
const updateRes = tenantRes; const updateRes = tenantRes;
@@ -389,7 +402,6 @@ const Preferences = () => {
}; };
return ( return (
<React.Fragment> <React.Fragment>
<Title title={t("Preferences")} />
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>} {isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
{isTopLoader ? ( {isTopLoader ? (
<div className="flex justify-center items-center h-screen"> <div className="flex justify-center items-center h-screen">
@@ -761,6 +773,12 @@ const Preferences = () => {
setDateFormat={setDateFormat} setDateFormat={setDateFormat}
/> />
</div> </div>
<div className="mb-6">
<FilenameFormatSelector
fileNameFormat={fileNameFormat}
setFileNameFormat={setFileNameFormat}
/>
</div>
</div> </div>
{/* Save Button - Full Width */} {/* Save Button - Full Width */}
<div className="md:col-span-12 flex justify-start mt-2"> <div className="md:col-span-12 flex justify-start mt-2">
@@ -787,7 +805,7 @@ const Preferences = () => {
onClick={() => handleModifyMail("request")} onClick={() => handleModifyMail("request")}
className="op-btn op-btn-primary shadow-lg" className="op-btn op-btn-primary shadow-lg"
> >
Modify {t("modify")}
</button> </button>
</div> </div>
) )
@@ -863,7 +881,7 @@ const Preferences = () => {
onClick={() => handleModifyMail("completion")} onClick={() => handleModifyMail("completion")}
className="op-btn op-btn-primary shadow-lg" className="op-btn op-btn-primary shadow-lg"
> >
Modify {t("modify")}
</button> </button>
</div> </div>
) )
+90 -74
View File
@@ -1,19 +1,20 @@
import React, { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import ReportTable from "../primitives/GetReportDisplay";
import Parse from "parse"; import Parse from "parse";
import axios from "axios"; import axios from "axios";
import reportJson from "../json/ReportJson"; import reportJson, { extraCols } from "../json/ReportJson";
import { useParams } from "react-router"; import { useParams } from "react-router";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound"; import PageNotFound from "./PageNotFound";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next"; import Contactbook from "../reports/contact/Contactbook";
import ColumnSelector from "../components/ColumnSelector";
import TemplatesReport from "../reports/template/TemplatesReport";
import DocumentsReport from "../reports/document/DocumentsReport";
import { templateReportTour } from "../json/ReportTour";
const Report = () => { const Report = () => {
const { t } = useTranslation();
const { id } = useParams(); const { id } = useParams();
const [List, setList] = useState([]); const abortController = new AbortController();
const [list, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true); const [isLoader, setIsLoader] = useState(true);
const [reportName, setReportName] = useState(""); const [reportName, setReportName] = useState("");
const [reporthelp, setReportHelp] = useState(""); const [reporthelp, setReportHelp] = useState("");
@@ -21,16 +22,21 @@ const Report = () => {
const [heading, setHeading] = useState([]); const [heading, setHeading] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false); const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true); const [isMoreDocs, setIsMoreDocs] = useState(true);
const [form, setForm] = useState("");
const [tourData, setTourData] = useState([]); const [tourData, setTourData] = useState([]);
const [isDontShow, setIsDontShow] = useState(false);
const [isImport, setIsImport] = useState(false);
const abortController = new AbortController();
const docPerPage = 10;
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [mobileSearchOpen, setMobileSearchOpen] = useState(false); const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
const [isSearchResult, setIsSearchResult] = useState(false); const [isSearchResult, setIsSearchResult] = useState(false);
const [allColumns, setAllColumns] = useState([]);
const [visibleColumns, setVisibleColumns] = useState([]);
const [columnLabels, setColumnLabels] = useState({});
const [defaultColumns, setDefaultColumns] = useState([]);
const [isColumnModal, setIsColumnModal] = useState(false);
const debounceTimer = useRef(null); const debounceTimer = useRef(null);
// Number of documents to display per page (should always be half of docLimit for proper pagination)
const docPerPage = 10;
// Number of documents to fetch per API call
const docLimit = 20;
// below useEffect is call when id param change // below useEffect is call when id param change
useEffect(() => { useEffect(() => {
@@ -38,7 +44,15 @@ const Report = () => {
setList([]); setList([]);
setSearchTerm(""); setSearchTerm("");
setMobileSearchOpen(false); setMobileSearchOpen(false);
getReportData(0, docPerPage, ""); const saved = JSON.parse(localStorage.getItem("reportColumns") || "{}");
if (saved[id]) {
setVisibleColumns(saved[id].visible || saved[id]);
setColumnLabels(saved[id].labels || {});
} else {
setVisibleColumns([]);
setColumnLabels({});
}
getReportData(0, docLimit, "");
// Function returned from useEffect is called on unmount // Function returned from useEffect is called on unmount
return () => { return () => {
@@ -54,15 +68,11 @@ const Report = () => {
// below useEffect call when isNextRecord state is true and fetch next record // below useEffect call when isNextRecord state is true and fetch next record
useEffect(() => { useEffect(() => {
if (isNextRecord) { if (isNextRecord) {
getReportData(List.length, 20, searchTerm); getReportData(list?.length, docLimit, searchTerm);
} }
// eslint-disable-next-line // eslint-disable-next-line
}, [isNextRecord]); }, [isNextRecord]);
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const handleSearchChange = async (e) => { const handleSearchChange = async (e) => {
const term = e.target.value.toLowerCase(); const term = e.target.value.toLowerCase();
setSearchTerm(term); setSearchTerm(term);
@@ -118,11 +128,21 @@ const Report = () => {
const json = reportJson(id); const json = reportJson(id);
if (json) { if (json) {
setActions(json.actions); setActions(json.actions);
setHeading(json.heading); const savedCols = JSON.parse(
localStorage.getItem("reportColumns") || "{}"
);
const visible = savedCols[id]?.visible || json.heading;
const labels = savedCols[id]?.labels || {};
if (!savedCols[id] || id === "contacts") {
savedCols[id] = { visible: json.heading, labels: {} };
localStorage.setItem("reportColumns", JSON.stringify(savedCols));
}
setVisibleColumns(visible);
setColumnLabels(labels);
setHeading(visible);
setDefaultColumns(json.heading);
setReportName(json.reportName); setReportName(json.reportName);
setForm(json.form);
setReportHelp(json?.helpMsg); setReportHelp(json?.helpMsg);
setIsImport(json?.import || false);
const currentUser = Parse.User.current().id; const currentUser = Parse.User.current().id;
const headers = { const headers = {
@@ -142,39 +162,11 @@ const Report = () => {
headers: headers, headers: headers,
signal: abortController.signal // is used to cancel fetch query signal: abortController.signal // is used to cancel fetch query
}); });
const extraHeads =
id === "4Hhwbp482K" ? [...extraCols, "Expiry Date"] : extraCols;
setAllColumns(Array.from(new Set([...json.heading, ...extraHeads])));
if (id === "6TeaPr321t") { if (id === "6TeaPr321t") {
const tourConfig = [ setTourData(templateReportTour);
{
selector: "[data-tut=reactourFirst]",
content: () => (
<TourContentWithBtn
message={t("tour-mssg.report-1")}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
];
if (res.data.result && res.data.result?.length > 0) {
json.actions.map((data) => {
const newConfig = {
selector: `[data-tut="${data?.selector}"]`,
content: () => (
<TourContentWithBtn
message={t(`tour-mssg.${data?.action}`)}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
};
tourConfig.push(newConfig);
});
}
setTourData(tourConfig);
} }
if (id === "4Hhwbp482K") { if (id === "4Hhwbp482K") {
const listData = res.data?.result.filter((x) => x.Signers.length > 0); const listData = res.data?.result.filter((x) => x.Signers.length > 0);
@@ -234,42 +226,66 @@ const Report = () => {
setIsLoader(false); setIsLoader(false);
} }
}; };
const commonProps = {
ReportName: reportName,
List: list,
setList,
actions: actions,
heading: heading,
setIsNextRecord,
isMoreDocs,
docPerPage,
mobileSearchOpen,
setMobileSearchOpen,
searchTerm,
handleSearchChange,
handleSearchPaste,
isSearchResult,
columnLabels,
openColumnModal: () => setIsColumnModal(true)
};
return ( return (
<> <>
<Title title={reportName} />
{isLoader ? ( {isLoader ? (
<div className="h-[100vh] flex justify-center items-center"> <div className="h-[100vh] flex justify-center items-center">
<Loader /> <Loader />
</div> </div>
) : ( ) : (
<> <>
{reportName ? ( {id === "contacts" ? (
<ReportTable <Contactbook {...commonProps} />
ReportName={reportName} ) : id === "6TeaPr321t" ? (
List={List} <TemplatesReport
setList={setList} {...commonProps}
actions={actions}
heading={heading}
setIsNextRecord={setIsNextRecord}
isMoreDocs={isMoreDocs}
docPerPage={docPerPage}
form={form}
report_help={reporthelp} report_help={reporthelp}
tourData={tourData} tourData={tourData}
isDontShow={isDontShow}
mobileSearchOpen={mobileSearchOpen}
setMobileSearchOpen={setMobileSearchOpen}
searchTerm={searchTerm}
handleSearchChange={handleSearchChange}
handleSearchPaste={handleSearchPaste}
isSearchResult={isSearchResult}
isImport={isImport}
/> />
) : reportName ? (
<DocumentsReport {...commonProps} report_help={reporthelp} />
) : ( ) : (
<PageNotFound prefix={"Report"} /> <PageNotFound prefix={"Report"} />
)} )}
</> </>
)} )}
<ColumnSelector
isOpen={isColumnModal}
allColumns={allColumns}
visibleColumns={visibleColumns}
columnLabels={columnLabels}
defaultColumns={defaultColumns}
onApply={(cols, labels) => {
setVisibleColumns(cols);
setHeading(cols);
setColumnLabels(labels);
const saved = JSON.parse(
localStorage.getItem("reportColumns") || "{}"
);
saved[id] = { visible: cols, labels };
localStorage.setItem("reportColumns", JSON.stringify(saved));
}}
onClose={() => setIsColumnModal(false)}
/>
</> </>
); );
}; };

Some files were not shown because too many files have changed in this diff Show More