Compare commits

...
Author SHA1 Message Date
RaktimaandGitHub e5f2c5c0a9 Merge pull request #2247 from OpenSignLabs/updates-32480718420
Merge pull request #2737 from nxglabs/sync-to-public_repo-32459158600
2026-08-21 17:50:18 +05:30
raktima-opensignlabs 7e0c2517f2 Merge pull request #2737 from nxglabs/sync-to-public_repo-32459158600
Merge pull request #2736 from nxglabs/raktima-main-patch-security
2026-08-21 12:11:59 +00:00
RaktimaandGitHub a5456dadcf Merge pull request #2245 from OpenSignLabs/updates-32235783890
Merge pull request #2730 from nxglabs/sync-to-public_repo-32233884118
2026-08-19 15:14:23 +05:30
raktima-opensignlabs 0158b0e176 Merge pull request #2730 from nxglabs/sync-to-public_repo-32233884118
Merge pull request #2729 from nxglabs/raktima-main-patch-security
2026-08-19 09:05:08 +00:00
RaktimaNXG 5d385a0ea3 Merge branch 'main' of https://github.com/OpenSignLabs/OpenSign into staging 2026-08-17 13:29:58 +05:30
RaktimaandGitHub 82584d6be9 Merge pull request #2243 from OpenSignLabs/raktima-patch-24
fix: update lockfile
2026-08-17 13:15:27 +05:30
RaktimaNXG cba774a336 fix: update lockfile 2026-08-17 13:14:44 +05:30
RaktimaNXG c44351ea67 fix: update lockfile 2026-08-17 12:48:00 +05:30
RaktimaandGitHub 3a861f3e0f v2.41.2 (#2241) 2026-08-14 23:15:48 +05:30
RaktimaNXG 348ff6de70 Merge branch 'main' of https://github.com/OpenSignLabs/OpenSign into staging 2026-08-14 22:24:09 +05:30
AmolandGitHub a773c50774 Merge pull request #2240 from OpenSignLabs/updates-31819970805 2026-08-14 22:08:53 +05:30
raktima-opensignlabs 7c414157dd Merge pull request #2717 from nxglabs/sync-to-public_repo-31818759561
Merge pull request #2716 from nxglabs/raktima-main-patch-security
2026-08-14 16:34:38 +00:00
RaktimaandGitHub 7b4785461c v2.41.1 (#2233) 2026-08-11 17:09:48 +05:30
AmolandGitHub 7f114dc124 Merge pull request #2234 from OpenSignLabs/updates-31487056035 2026-08-11 17:06:37 +05:30
raktima-opensignlabs 50cc654166 Merge pull request #2709 from nxglabs/sync-to-public_repo-31486925834
Merge pull request #2708 from nxglabs/raktima-patch-main-7
2026-08-11 11:32:14 +00:00
RaktimaNXG dc8555dfec Merge branch 'main' of https://github.com/OpenSignLabs/OpenSign into staging 2026-08-11 16:09:43 +05:30
AmolandGitHub 43e911dfdc Merge pull request #2232 from OpenSignLabs/updates-31406529761 2026-08-11 12:40:51 +05:30
raktima-opensignlabs 05f472ace8 Merge pull request #2707 from nxglabs/sync-to-public_repo-31405962035
Merge pull request #2706 from nxglabs/raktima-patch-main-7
2026-08-10 15:59:21 +00:00
AmolandGitHub 3729564878 Merge pull request #2705 from nxglabs/sync-to-public_repo-31391986146 (#2231) 2026-08-10 20:21:00 +05:30
ZathielandGitHub f72624fa26 Merge pull request #2150 from OpenSignLabs/dependabot/npm_and_yarn/npm_and_yarn-66413a1f6e
build(deps-dev): bump picomatch from 2.3.1 to 2.3.2 in the npm_and_yarn group across 1 directory
2026-07-06 23:16:24 +02:00
raktima-opensignlabsandAmol 8140588898 Merge pull request #2611 from nxglabs/sync-to-public_repo-28355409725
Merge pull request #2610 from nxglabs/staging
2026-06-30 16:28:06 +05:30
RaktimaandGitHub f2ada47302 Merge pull request #2199 from OpenSignLabs/updates-28428983168
Merge pull request #2611 from nxglabs/sync-to-public_repo-28355409725
2026-06-30 13:49:11 +05:30
raktima-opensignlabs b63d609a70 Merge pull request #2611 from nxglabs/sync-to-public_repo-28355409725
Merge pull request #2610 from nxglabs/staging
2026-06-30 07:52:10 +00:00
RaktimaandGitHub d97b353a81 Merge pull request #2194 from OpenSignLabs/staging
Merge pull request #2192 from OpenSignLabs/updates-27678195317
2026-06-22 10:43:43 +05:30
RaktimaNXG 80ccd5d022 update package-lock.json file 2026-06-22 10:21:08 +05:30
RaktimaandGitHub e948fdc60b Merge branch 'main' into staging 2026-06-22 09:29:24 +05:30
RaktimaandGitHub 4bdfe9d725 Merge pull request #2192 from OpenSignLabs/updates-27678195317
Merge pull request #2576 from nxglabs/sync-to-public_repo-27677371306
2026-06-17 14:54:02 +05:30
raktima-opensignlabs be0c4a2cbf Merge pull request #2576 from nxglabs/sync-to-public_repo-27677371306
Merge pull request #2575 from nxglabs/staging
2026-06-17 09:08:15 +00:00
b417754a0c fix: handle server.cjs file (#2184)
Co-authored-by: raktima-opensignlabs <raktima-opensignlabs@users.noreply.github.com>
2026-05-26 10:50:21 +05:30
RaktimaandGitHub 6271fedcbf Merge pull request #2183 from OpenSignLabs/updates-26432416990
Merge pull request #2474 from nxglabs/sync-to-public_repo-26403170706
2026-05-26 10:03:53 +05:30
RaktimaandGitHub 1e228c86c3 v2.39.0 (#2180) 2026-05-22 21:03:28 +05:30
dependabot[bot]andGitHub bea891691b build(deps-dev): bump picomatch
Bumps the npm_and_yarn group with 1 update in the / directory: [picomatch](https://github.com/micromatch/picomatch).


Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-31 13:17:37 +00:00
AmolandGitHub b9a309fa36 Merge pull request #2143 from OpenSignLabs/staging
v2.38.0
2026-03-16 17:47:26 +05:30
AmolandGitHub dd499ba0d0 Merge pull request #2130 from OpenSignLabs/staging 2026-03-04 13:13:57 +05:30
prafull-opensignlabsandGitHub 606eb6a93b Merge pull request #2102 from OpenSignLabs/staging
v2.36.0
2026-02-06 18:21:57 +05:30
prafull-opensignlabsandGitHub fe8ab56d47 Merge pull request #2084 from OpenSignLabs/staging
fix: handle overlapping of date modal
2026-01-09 14:47:18 +05:30
prafull-opensignlabsandGitHub 4c5a3509cd Merge pull request #2081 from OpenSignLabs/staging
refactor: spelling correction
2026-01-07 19:26:30 +05:30
AmolandGitHub f751a97f24 Merge pull request #2077 from OpenSignLabs/staging 2026-01-07 15:16:25 +05:30
AmolandGitHub e4ecb5dbfd Merge pull request #2060 from OpenSignLabs/staging 2025-12-22 15:29:21 +05:30
prafull-opensignlabsandGitHub 2297d6fc22 Merge pull request #2051 from OpenSignLabs/staging
v2.33.0
2025-12-08 17:28:27 +05:30
AmolandGitHub 8922b61ecd Merge pull request #2043 from OpenSignLabs/staging
v2.32.0
2025-11-28 18:56:30 +05:30
AmolandGitHub db4b2d58d6 Merge pull request #2030 from OpenSignLabs/staging
fix: remove type parameter from parse.save method
2025-11-20 20:18:58 +05:30
AmolandGitHub 7017930709 Merge pull request #2028 from OpenSignLabs/staging
fix: add password validation in change password page
2025-11-20 18:27:50 +05:30
AmolandGitHub 810037e3c8 Merge pull request #2026 from OpenSignLabs/staging
build(deps): update dependencies
2025-11-20 16:24:43 +05:30
AmolandGitHub f8987a57d9 Merge pull request #2024 from OpenSignLabs/staging
v2.31.0
2025-11-20 12:20:55 +05:30
prafull-opensignlabsandGitHub f3d20391fa Merge pull request #1995 from OpenSignLabs/staging
v2.30.0
2025-10-16 14:24:55 +05:30
AndrewandGitHub 6594ba03a3 Merge pull request #1974 from OpenSignLabs/staging
v2.29.2
2025-10-01 19:39:25 +05:30
AmolandGitHub 9c2a388f66 Merge pull request #1970 from OpenSignLabs/staging
v2.29.1
2025-09-30 17:59:48 +05:30
AndrewandGitHub 8c49e1ac18 Merge pull request #1967 from OpenSignLabs/staging
v2.29.0
2025-09-29 10:56:48 +05:30
AmolandGitHub e1bf2ed94b Merge pull request #1946 from OpenSignLabs/staging
refactor: remove unnecessary code
2025-09-17 19:11:11 +05:30
AmolandGitHub 797fcbd624 Merge pull request #1944 from OpenSignLabs/staging
build(deps): update dependencies and dark mode ui
2025-09-17 14:38:38 +05:30
AmolandGitHub 847657dc76 Merge pull request #1942 from OpenSignLabs/staging
v2.28.0
2025-09-15 20:31:08 +05:30
prafull-opensignlabsandGitHub 3a361cc21e Merge pull request #1932 from OpenSignLabs/staging
fix: getting error 'user not found' while deleting user
2025-09-08 16:24:29 +05:30
prafull-opensignlabsandGitHub 1496c1b1da Merge pull request #1930 from OpenSignLabs/staging
v2.27.2
2025-09-08 10:38:37 +05:30
prafull-opensignlabsandGitHub d87fa38976 Merge pull request #1836 from OpenSignLabs/staging
v2.26.0
2025-07-07 15:16:19 +05:30
prafull-opensignlabsandGitHub df4630aa31 Merge pull request #1805 from OpenSignLabs/staging
v2.25.0
2025-06-19 17:37:33 +05:30
prafull-opensignlabsandGitHub d8a0d9137e Merge pull request #1790 from OpenSignLabs/staging
v2.24.1
2025-06-06 18:17:54 +05:30
prafull-opensignlabsandGitHub 1b2528ac1d Merge pull request #1765 from OpenSignLabs/staging
v2.23.0
2025-05-30 20:32:35 +05:30
prafull-opensignlabsandGitHub 9b357acbdd Merge pull request #1757 from OpenSignLabs/staging
v2.22.0
2025-05-28 11:27:44 +05:30
prafull-opensignlabsandGitHub ff4fa3a15f v2.21.1
handle undefined placeholders
2025-05-15 16:52:45 +05:30
prafull-opensignlabsandGitHub f104b0ca86 Merge pull request #1736 from OpenSignLabs/staging
v2.21.0
2025-05-15 12:23:07 +05:30
prafull-opensignlabsandGitHub 31affb53f1 Merge pull request #1718 from OpenSignLabs/staging
v2.20.0
2025-05-05 18:31:52 +05:30
prafull-opensignlabsandGitHub 338b76029f Merge pull request #1710 from OpenSignLabs/staging
fix: quick send not working
2025-04-29 12:17:44 +05:30
prafull-opensignlabsandGitHub 646e202b91 Merge pull request #1705 from OpenSignLabs/staging
refactor: remove extra comment from migration
2025-04-27 12:21:49 +05:30
prafull-opensignlabsandGitHub 8a34917343 Merge pull request #1703 from OpenSignLabs/staging
fix: update migration script
2025-04-27 12:03:23 +05:30
prafull-opensignlabsandGitHub 9b8c9403d1 Merge pull request #1701 from OpenSignLabs/staging
v2.19.0
2025-04-25 19:18:06 +05:30
prafull-opensignlabsandGitHub 40198e1bd8 Merge pull request #1693 from OpenSignLabs/staging
fix: correct public URL, normalize emails, migrate user/orgAdmin logic to cloud function
2025-04-22 14:10:35 +05:30
prafull-opensignlabsandGitHub 0e6220e16c Merge pull request #1690 from OpenSignLabs/staging
v2.18.0
2025-04-17 12:07:30 +05:30
prafull-opensignlabsandGitHub fc3a5ad1bf Merge pull request #1682 from OpenSignLabs/staging
v2.17.0
2025-04-11 13:21:32 +05:30
prafull-opensignlabsandGitHub 4919d6c3d3 Merge pull request #1673 from OpenSignLabs/staging
v2.16.0
2025-04-05 11:37:25 +05:30
prafull-opensignlabsandGitHub 91e89bb0e2 Merge pull request #1664 from OpenSignLabs/staging
v2.15.0
2025-03-28 11:38:46 +05:30
prafull-opensignlabsandGitHub f942b2571a Merge pull request #1662 from OpenSignLabs/staging
feat: changes to implement one click deployment
2025-03-24 13:48:24 +05:30
prafull-opensignlabsandGitHub ae2c15660d Merge pull request #1655 from OpenSignLabs/staging
refactor: remove unnecessary code
2025-03-23 13:12:52 +05:30
prafull-opensignlabsandGitHub 721d52d539 Merge pull request #1649 from OpenSignLabs/staging
v2.14.0
2025-03-21 20:10:43 +05:30
prafull-opensignlabsandGitHub b03e27cf70 Merge pull request #1647 from OpenSignLabs/staging
v2.13.0
2025-03-20 17:38:11 +05:30
prafull-opensignlabsandGitHub 4eae44c9b4 Merge pull request #1604 from OpenSignLabs/staging
v2.13.1
2025-02-21 13:02:46 +05:30
prafull-opensignlabsandGitHub 79234441db Merge pull request #1600 from OpenSignLabs/staging
v2.13.0
2025-02-21 12:47:30 +05:30
35 changed files with 5865 additions and 5334 deletions
+2812 -2388
View File
File diff suppressed because it is too large Load Diff
+15 -7
View File
@@ -26,7 +26,7 @@
"@usewaypoint/block-text": "^0.0.7",
"@usewaypoint/document-core": "^0.0.6",
"@usewaypoint/email-builder": "^0.0.9",
"axios": "^1.15.2",
"axios": "^1.18.0",
"date-fns-tz": "^3.2.0",
"dompurify": "^3.4.1",
"file-saver": "^2.0.5",
@@ -63,7 +63,7 @@
"react-quill-new": "^3.8.3",
"react-redux": "^9.2.0",
"react-rnd": "^10.5.3",
"react-router": "^7.14.2",
"react-router": "^7.18.2",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.10.2",
"react-signature-canvas": "^1.1.0-alpha.2",
@@ -128,7 +128,7 @@
"autoprefixer": "^10.5.0",
"babel-loader": "^9.2.1",
"commitizen": "^4.3.1",
"concurrently": "^9.2.1",
"concurrently": "^10.0.4",
"css-loader": "^7.1.4",
"daisyui": "^4.12.24",
"eslint": "^9.39.4",
@@ -136,20 +136,28 @@
"eslint-plugin-react": "^7.37.5",
"jsdom": "^29.0.2",
"lint-staged": "^16.4.0",
"postcss": "^8.5.12",
"postcss": "^8.5.23",
"pretty-quick": "^4.2.2",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.19",
"tmp": "^0.2.5",
"vite": "^8.0.10",
"tmp": "^0.2.7",
"vite": ">=8.0.16",
"vite-plugin-svgr": "^5.2.0",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.1.5"
},
"overrides": {
"brace-expansion": ">=2.1.2",
"tmp": "$tmp",
"react": "$react",
"react-dom": "$react-dom"
"react-dom": "$react-dom",
"form-data": ">=4.0.6",
"js-yaml": "^3.15.0",
"undici": "^7.29.0",
"fast-uri": "^3.1.5",
"nanoid": ">=3.3.16",
"ws": ">=8.21.0",
"protobufjs": ">=7.6.5"
},
"engines": {
"node": "18 || 20 || 22"
@@ -284,6 +284,8 @@
"yes": "Ja",
"copied": "Kopiert",
"something-went-wrong-mssg": "Etwas ist schiefgelaufen, Das Aktualisieren dieser Seite kann das Problem lösen.",
"attachments-docx-upload-failed": "Derzeit liegt ein Problem vor. Bitte versuchen Sie es erneut.",
"attachments-docx-upload-limit": "Das Upload-Limit für große DOCX-Dateien ist erreicht. Bitte warten Sie 4 Minuten und versuchen Sie es erneut.",
"token-generated": "Token erfolgreich generiert.",
"webhook": "Webhook",
"update-webhook": "Webhook aktualisieren",
@@ -421,7 +423,10 @@
"image": "Bild",
"email": "E-Mail",
"number": "Zahl",
"draw": "Zeichnen"
"draw": "Zeichnen",
"attachments": "Anhänge",
"attachments-add": "Klicken, um Anhänge hochzuladen",
"attachments-count": "{{count}} Anhang/Anhänge"
},
"fields": "Felder",
"recipients": "Empfänger",
@@ -795,6 +800,7 @@
"quota-mail-info": "Um die Servicequalität aufrechtzuerhalten und Spam zu verhindern, erlaubt OpenSign bis zu 15 E-Mails pro Monat im kostenlosen Plan. Upgraden Sie jetzt für unbegrenzten E-Mail-Versand.",
"quota-mail-reset": "Ihre Credits für Signaturanfragen per E-Mail werden zurückgesetzt am",
"quota-mail": "Sie haben Ihr monatliches Limit von 15 Signaturanfragen per E-Mail erreicht. Jetzt upgraden, um weiter E-Mails direkt zu senden.",
"quota-mail-limit-reached": "Sie haben Ihr monatliches Limit von 15 E-Mail-Credits erreicht. Bitte upgraden Sie Ihren Tarif, um weiterhin E-Mails direkt zu senden.\nIhre E-Mail-Credits werden am {{resetDate}} zurückgesetzt.",
"quota-mail-tip": "Tipp: Sie können weiterhin <1>unbegrenzt Dokumente</1> signieren, indem Sie den Signaturanfragelink manuell teilen.",
"quota-mail-head": "Kontingent erreicht",
"unauthorized-modal": "Sie haben keine Berechtigung, diese Aktion auszuführen. Bitte kontaktieren Sie {{adminEmail}}.",
@@ -1359,7 +1365,7 @@
"export-all-formdata-error": "Sie können die Formulardaten nicht exportieren, da die Vorlage doppelte Widget-Namen enthält. Bitte erstellen Sie eine neue Vorlage, um Formulardaten exportieren zu können.",
"export-formdata-error": "Sie können die Formulardaten nicht exportieren, da das Dokument doppelte Widget-Namen enthält. Bitte erstellen Sie ein neues Dokument ohne doppelte Widget-Namen, um Formulardaten exportieren zu können.",
"duplicate-widget-name-error": "Dieser Widget-Name ist bereits einem anderen Unterzeichner zugewiesen. Bitte wählen Sie einen anderen Namen.",
"duplicate-template-widget-error": "Sie können diese Vorlage nicht duplizieren, da sie doppelte Widget-Namen enthält.",
"duplicate-template-widget-error": "Die Vorlage kann nicht dupliziert werden, weil einige Widget-Namen in verschiedenen Rollen doppelt vorkommen. Benennen Sie die doppelten Widgets um und versuchen Sie es erneut.",
"credit-alert": "Kreditwarnung",
"document-form-data": "Dokumenten-Formulardaten",
"view-formdata-paid-title-error": "Die Funktion 'Formulardaten anzeigen' ermöglicht es Ihnen, die von Unterzeichnern ausgefüllten Widget-Daten auszudrucken und herunterzuladen.",
@@ -1558,5 +1564,53 @@
"attach-all-role-to-signer": "Bitte weisen Sie dem Unterzeichner alle Rollen zu.",
"upgrade-to-edit": "Zum Bearbeiten upgraden",
"hide-live-chat-for-signers": "Live-Chat für Unterzeichner ausblenden",
"hide-live-chat-for-signers-help": "Wenn aktiviert, werden Live-Chat-Widgets für Unterzeichner auf Signierseiten ausgeblendet."
"hide-live-chat-for-signers-help": "Wenn aktiviert, werden Live-Chat-Widgets für Unterzeichner auf Signierseiten ausgeblendet.",
"attachments-creator-hint": "Unterzeichner können Dateien hochladen, die am Ende des Dokuments angehängt werden.",
"attachments-drop-hint": "Dateien hier ablegen oder klicken, um auszuwählen",
"attachments-limits": "PDF, DOCX, PNG, JPG. Max. {{maxFiles}} Dateien, je {{maxSizeMB}} MB.",
"attachments-invalid-type": "Dateityp wird nicht unterstützt: {{name}}",
"attachments-max-files": "Maximal {{maxFiles}} Anhänge erlaubt.",
"attachments-remove-confirm": "Das Entfernen löscht nur die Metadaten. Bereits angehängte Seiten bleiben im Dokument. Fortfahren?",
"conditional-logic": "Bedingte Logik",
"enabled": "Aktiviert",
"disabled": "Deaktiviert",
"logic-no-sources": "Keine anderen Felder dieses Unterzeichners können als Quelle verwendet werden.",
"logic-action": "Aktion",
"logic-when": "Wenn",
"logic-all": "alle",
"logic-any": "eine",
"logic-of-following": "der folgenden Bedingungen erfüllt sind",
"logic-add-rule": "Regel hinzufügen",
"logic-pick-value": "Wert auswählen",
"logic-value-placeholder": "Wert eingeben",
"logic-action-show": "Dieses Feld anzeigen",
"logic-action-hide": "Dieses Feld ausblenden",
"logic-action-require": "Pflichtfeld machen",
"logic-action-optional": "Optional machen",
"logic-op-equals": "ist gleich",
"logic-op-notEquals": "ist ungleich",
"logic-op-checked": "ist angekreuzt",
"logic-op-unchecked": "ist nicht angekreuzt",
"widget-hidden-due-to-logic": "Dieses Widget ist aufgrund bedingter Logik ausgeblendet.",
"remove": "Entfernen",
"attachments-docx-not-supported": "Derzeit tritt ein Problem beim Hochladen von DOCX-Dateien auf. Bitte versuchen Sie, das Dokument in einem anderen unterstützten Format hochzuladen, oder versuchen Sie es später erneut.",
"access-code": "Zugangscode",
"access-code-set": "Zugangscode festgelegt",
"access-code-add": "Zugangscode hinzufügen",
"access-code-help": "Optionales Geheimnis, das nur dieser Unterzeichner kennt. Vor dem Anzeigen oder Unterschreiben des Dokuments wird die Eingabe verlangt. Geben Sie den Code dem Empfänger auf einem anderen Kanal weiter (Telefon, persönlich).",
"access-code-placeholder": "Mindestens 6 Ziffern",
"access-code-clear": "Löschen",
"access-code-must-be-numeric": "Der Zugangscode darf nur Ziffern 0-9 enthalten.",
"access-code-save": "Speichern",
"access-code-required": "Der Absender hat diesem Empfänger einen Zugangscode zugewiesen. Geben Sie ihn ein, um fortzufahren.",
"access-code-invalid": "Falscher Zugangscode. Bitte beim Absender nachfragen.",
"access-code-too-short": "Der Zugangscode muss mindestens 6 Zeichen lang sein.",
"access-code-verify": "Zugangscode überprüfen",
"access-code-verified": "Zugangscode verifiziert.",
"access-code-warning": "Dieser Zugangscode kann nicht geändert oder abgerufen werden, sobald das Dokument versendet wurde.",
"copy-access-code": "Zugangscode kopieren",
"access-code-upgrade-msg": "Zugangscodes sind nur in kostenpflichtigen Tarifen verfügbar. Aktualisieren Sie Ihren Tarif, um Zugangscodes für Empfänger hinzuzufügen.",
"show-access-code": "Zugangscode anzeigen",
"hide-access-code": "Zugangscode ausblenden",
"already-copied-to-clipboard": "Bereits in die Zwischenablage kopiert"
}
@@ -421,7 +421,10 @@
"image": "image",
"email": "email",
"number": "number",
"draw": "draw"
"draw": "draw",
"attachments": "attachments",
"attachments-add": "Click to add attachments",
"attachments-count": "{{count}} attachment(s)"
},
"fields": "Fields",
"recipients": "Recipients",
@@ -606,6 +609,8 @@
"pdf-download": "PDF download",
"generate-certificate-alert": " Your completion certificate is being generated. Please wait momentarily. If the download doesn't start shortly, click the button again.",
"generate-certificate-err": "We are currently experiencing an issue with certificate generation. Please try again later.",
"attachments-docx-upload-failed": "We are currently facing a problem. Please try again.",
"attachments-docx-size-limit": "Upload limit reached for large files. Please wait until {{time}} before retrying.",
"folder-already-exist": "Folder already exists!",
"folder-created-successfully": "Folder created successfully!",
"fill-folder-name": "Please fill folder name",
@@ -795,6 +800,7 @@
"quota-mail-info": "To maintain service quality and prevent spam, OpenSign allows up to 15 emails per month on the free plan. Upgrade now for unlimited email sending.",
"quota-mail-reset": "Your signature request email credits will be reset on",
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
"quota-mail-limit-reached": "You've reached your monthly limit of 15 email credits. Please upgrade your plan to continue sending emails directly.\nYour email credits will reset on {{resetDate}}",
"quota-mail-tip": "Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request link.",
"quota-mail-head": "Quota reached",
"unauthorized-modal": "You don't have permission to perform this action, please contact {{adminEmail}}.",
@@ -1361,7 +1367,7 @@
"export-all-formdata-error": "You can/t export form data because the template contains duplicate widget names. Please create a fresh template to be able to export form data.",
"export-formdata-error": "You can't export form data because the document contains duplicate widget names. Please create a fresh document without duplicate widget names to be able to export form data.",
"duplicate-widget-name-error": "This widget name is already assigned to another signer. Please choose a different name.",
"duplicate-template-widget-error": "You can't duplicate this template because it contains duplicate widget names.",
"duplicate-template-widget-error": "Unable to duplicate the template because some widget names are duplicated across different roles. Rename the duplicate widgets and try again.",
"credit-alert": "Credit Alert",
"document-form-data": "Document Form Data",
"view-formdata-paid-title-error": "The View Form Data feature lets you print and download the widget data filled in by signers while signing.",
@@ -1558,5 +1564,54 @@
"benefit-bulksend-api-team-year-help": "This plan comes with 500 premium credits per year that let you send documents via API, share public signing links, run bulk signature requests, use Kiosk mode for in-person signing, or collect signatures from documents embedded directly on your website. Each time you initiate or send a document, one credit is used — and that single document can include up to 10 signers.",
"failed-pdf-for-prefill-processing": "Failed to load PDF for prefill processing.",
"attach-all-role-to-signer": "Please attach all roles to the signer.",
"upgrade-to-edit": "Upgrade to edit"
}
"upgrade-to-edit": "Upgrade to edit",
"attachments-creator-hint": "Signers can upload files that will be appended to the end of the document.",
"attachments-drop-hint": "Drop files here or click to choose",
"attachments-limits": "PDF, DOCX, PNG, JPG. Up to {{maxFiles}} files, {{maxSizeMB}} MB each.",
"attachments-invalid-type": "Unsupported file type: {{name}}",
"attachments-max-files": "A maximum of {{maxFiles}} attachments are allowed.",
"attachments-remove-confirm": "Removing only deletes the metadata. Pages already appended will remain in the document. Continue?",
"conditional-logic": "Conditional logic",
"enabled": "Enabled",
"disabled": "Disabled",
"logic-no-sources": "No other fields on this signer can be used as a source.",
"logic-action": "Action",
"logic-when": "When",
"logic-all": "all",
"logic-any": "any",
"logic-of-following": "of the following are true",
"logic-add-rule": "Add rule",
"logic-pick-value": "Select a value",
"logic-value-placeholder": "Enter value",
"logic-action-show": "Show this field",
"logic-action-hide": "Hide this field",
"logic-action-require": "Make required",
"logic-action-optional": "Make optional",
"logic-op-equals": "equals",
"logic-op-notEquals": "does not equal",
"logic-op-checked": "is checked",
"logic-op-unchecked": "is not checked",
"widget-hidden-due-to-logic": "This widget is hidden due to conditional logic.",
"remove": "Remove",
"attachement-note":"Note: This widget allows signers to upload a file during the signing process. The uploaded file will be appended as additional pages at the end of the existing PDF",
"attachments-docx-not-supported":"We are currently experiencing an issue with DOCX file uploads. Please try uploading the document in a different supported format, or try again later.",
"access-code": "Access code",
"access-code-set": "Access code set",
"access-code-add": "Add access code",
"access-code-help": "Optional secret only this signer knows. They will be required to enter it before viewing or signing the document. Share it with the recipient out-of-band (phone, in person).",
"access-code-placeholder": "At least 6 digits",
"access-code-clear": "Clear",
"access-code-save": "Save",
"access-code-required": "This recipient was given an access code by the sender. Enter it to continue.",
"access-code-invalid": "Incorrect access code. Please check with the sender.",
"access-code-too-short": "Access code must be at least 6 digits.",
"access-code-must-be-numeric": "Access code must contain only digits 0-9.",
"access-code-verify": "Verify access code",
"access-code-verified": "Access code verified.",
"access-code-warning": "This access code cannot be changed or retrieved once the document is sent out.",
"copy-access-code": "Copy access code",
"access-code-upgrade-msg": "Access codes are available on paid plans only. Upgrade your plan to add access codes for recipients.",
"show-access-code": "Show access code",
"hide-access-code": "Hide access code",
"already-copied-to-clipboard": "Already copied to clipboard"
}
@@ -284,6 +284,8 @@
"yes": "Sí",
"copied": "Copiado",
"something-went-wrong-mssg": "Un problème est survenu, Actualiser cette page peut résoudre le problème.",
"attachments-docx-upload-failed": "Actualmente tenemos un problema. Inténtalo de nuevo.",
"attachments-docx-upload-limit": "Se alcanzó el límite de carga para archivos DOCX grandes. Espera 4 minutos antes de intentarlo de nuevo.",
"token-generated": "Token generado exitosamente.",
"webhook": "Webhook",
"update-webhook": "Actualizar webhook",
@@ -421,7 +423,10 @@
"image": "imagen",
"email": "correo",
"number": "número",
"draw": "Dibujar"
"draw": "Dibujar",
"attachments": "adjuntos",
"attachments-add": "Clic para subir archivos adjuntos",
"attachments-count": "{{count}} adjunto(s)"
},
"fields": "Campos",
"recipients": "Destinatarios",
@@ -795,6 +800,7 @@
"quota-mail-info": "Para mantener la calidad del servicio y prevenir el spam, OpenSign permite hasta 15 correos electrónicos por mes en el plan gratuito. Mejora tu plan ahora para envíos ilimitados de correos electrónicos.",
"quota-mail-reset": "Los créditos de tu correo electrónico de solicitud de firma se restablecerán el",
"quota-mail": "Has alcanzado tu límite de 15 solicitudes de firma por correo para este mes. Mejora ahora para continuar enviando correos.",
"quota-mail-limit-reached": "Has alcanzado tu límite mensual de 15 créditos de correo electrónico. Mejora tu plan para continuar enviando correos directamente.\nTus créditos de correo electrónico se restablecerán el {{resetDate}}.",
"quota-mail-tip": "Consejo: Aún puedes firmar <1>documentos ilimitados</1> compartiendo manualmente el enlace de solicitud de firma.",
"quota-mail-head": "Cuota alcanzada",
"unauthorized-modal": "No tienes permisos para realizar esta acción, por favor, contacta a {{adminEmail}}.",
@@ -1359,7 +1365,7 @@
"export-all-formdata-error": "No puede exportar los datos del formulario porque la plantilla contiene nombres de widgets duplicados. Cree una nueva plantilla para poder exportar los datos.",
"export-formdata-error": "No puede exportar los datos del formulario porque el documento contiene nombres de widgets duplicados. Cree un documento nuevo sin nombres duplicados para poder exportar los datos.",
"duplicate-widget-name-error": "Este nombre de widget ya está asignado a otro firmante. Por favor elija un nombre diferente.",
"duplicate-template-widget-error": "No puede duplicar esta plantilla porque contiene nombres de widgets duplicados.",
"duplicate-template-widget-error": "No se puede duplicar la plantilla porque algunos nombres de widgets están duplicados en diferentes roles. Cambie el nombre de los widgets duplicados e inténtelo de nuevo.",
"credit-alert": "Alerta de crédito",
"document-form-data": "Datos del formulario del documento",
"view-formdata-paid-title-error": "La función 'Ver datos del formulario' le permite imprimir y descargar los datos de los widgets completados por los firmantes durante la firma.",
@@ -1558,5 +1564,54 @@
"attach-all-role-to-signer": "Por favor, asigna todos los roles al firmante.",
"upgrade-to-edit": "Actualiza para editar",
"hide-live-chat-for-signers": "Ocultar chat en vivo para firmantes",
"hide-live-chat-for-signers-help": "Cuando está activado, los widgets de chat en vivo se ocultarán para los firmantes en las páginas de firma."
"hide-live-chat-for-signers-help": "Cuando está activado, los widgets de chat en vivo se ocultarán para los firmantes en las páginas de firma.",
"attachments-creator-hint": "Los firmantes pueden subir archivos que se añadirán al final del documento.",
"attachments-drop-hint": "Arrastra archivos aquí o haz clic para seleccionar",
"attachments-limits": "PDF, DOCX, PNG, JPG. Hasta {{maxFiles}} archivos, {{maxSizeMB}} MB cada uno.",
"attachments-invalid-type": "Tipo de archivo no admitido: {{name}}",
"attachments-max-files": "Se permiten hasta {{maxFiles}} adjuntos.",
"attachments-remove-confirm": "Eliminar solo borra los metadatos. Las páginas ya añadidas permanecerán en el documento. ¿Continuar?",
"conditional-logic": "Lógica condicional",
"enabled": "Habilitado",
"disabled": "Deshabilitado",
"logic-no-sources": "No hay otros campos de este firmante que puedan usarse como origen.",
"logic-action": "Acción",
"logic-when": "Cuando",
"logic-all": "todas",
"logic-any": "alguna",
"logic-of-following": "de las siguientes condiciones se cumplan",
"logic-add-rule": "Añadir regla",
"logic-pick-value": "Seleccionar un valor",
"logic-value-placeholder": "Introducir valor",
"logic-action-show": "Mostrar este campo",
"logic-action-hide": "Ocultar este campo",
"logic-action-require": "Hacer obligatorio",
"logic-action-optional": "Hacer opcional",
"logic-op-equals": "es igual a",
"logic-op-notEquals": "no es igual a",
"logic-op-checked": "está marcado",
"logic-op-unchecked": "no está marcado",
"widget-hidden-due-to-logic": "Este widget está oculto debido a la lógica condicional.",
"remove": "Eliminar",
"attachement-note": "Nota: Este widget permite a los firmantes cargar un archivo durante el proceso de firma. El archivo cargado se añadirá como páginas adicionales al final del PDF existente.",
"attachments-docx-not-supported": "Actualmente estamos experimentando un problema con las cargas de archivos DOCX. Intente cargar el documento en otro formato compatible o vuelva a intentarlo más tarde.",
"access-code": "Código de acceso",
"access-code-set": "Código de acceso establecido",
"access-code-add": "Añadir código de acceso",
"access-code-help": "Secreto opcional que solo conoce este firmante. Deberá introducirlo antes de ver o firmar el documento. Compártalo con el destinatario por otro canal (teléfono, en persona).",
"access-code-placeholder": "Al menos 6 dígitos",
"access-code-clear": "Borrar",
"access-code-must-be-numeric": "El código de acceso debe contener solo dígitos 0-9.",
"access-code-save": "Guardar",
"access-code-required": "El remitente asignó un código de acceso a este destinatario. Introdúzcalo para continuar.",
"access-code-invalid": "Código de acceso incorrecto. Consulte con el remitente.",
"access-code-too-short": "El código de acceso debe tener al menos 6 dígitos.",
"access-code-verify": "Verificar código de acceso",
"access-code-verified": "Código de acceso verificado.",
"access-code-warning": "Este código de acceso no se puede cambiar ni recuperar una vez que se envía el documento.",
"copy-access-code": "Copiar código de acceso",
"access-code-upgrade-msg": "Los códigos de acceso solo están disponibles en planes de pago. Actualice su plan para agregar códigos de acceso a los destinatarios.",
"show-access-code": "Mostrar código de acceso",
"hide-access-code": "Ocultar código de acceso",
"already-copied-to-clipboard": "Ya copiado al portapapeles"
}
@@ -421,7 +421,10 @@
"image": "image",
"email": "e-mail",
"number": "nombre",
"draw": "Dessiner"
"draw": "Dessiner",
"attachments": "pièces jointes",
"attachments-add": "Cliquer pour ajouter des pièces jointes",
"attachments-count": "{{count}} pièce(s) jointe(s)"
},
"fields": "Des champs",
"recipients": "Destinataires",
@@ -795,6 +798,7 @@
"quota-mail-info": "Pour maintenir la qualité du service et prévenir le spam, OpenSign permet jusqu'à 15 e-mails par mois avec le plan gratuit. Passez à l'offre supérieure pour un envoi illimité d'e-mails.",
"quota-mail-reset": "Les crédits de votre email de demande de signature seront réinitialisés le",
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
"quota-mail-limit-reached": "Vous avez atteint votre limite mensuelle de 15 crédits e-mail. Veuillez mettre à niveau votre forfait pour continuer à envoyer des e-mails directement.\nVos crédits e-mail seront réinitialisés le {{resetDate}}.",
"quota-mail-tip": "Astuce: Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement le lien de demande de signature.",
"quota-mail-head": "Quota atteint",
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
@@ -1359,7 +1363,7 @@
"export-all-formdata-error": "Vous ne pouvez pas exporter les données du formulaire car le modèle contient des noms de widgets en double. Veuillez créer un nouveau modèle pour pouvoir exporter les données.",
"export-formdata-error": "Vous ne pouvez pas exporter les données du formulaire car le document contient des noms de widgets en double. Veuillez créer un nouveau document sans doublons pour pouvoir exporter les données.",
"duplicate-widget-name-error": "Ce nom de widget est déjà attribué à un autre signataire. Veuillez choisir un autre nom.",
"duplicate-template-widget-error": "Vous ne pouvez pas dupliquer ce modèle car il contient des noms de widgets en double.",
"duplicate-template-widget-error": "Impossible de dupliquer le modèle, car certains noms de widgets sont dupliqués dans différents rôles. Renommez les widgets en double, puis réessayez.",
"credit-alert": "Alerte de crédit",
"document-form-data": "Données du formulaire du document",
"view-formdata-paid-title-error": "La fonction 'Afficher les données du formulaire' vous permet d'imprimer et de télécharger les données des widgets remplies par les signataires lors de la signature.",
@@ -1556,8 +1560,59 @@
"benefit-bulksend-api-team-month-help": "Ce forfait inclut 100 crédits premium par mois qui vous permettent d'envoyer des documents via l'API, de partager des liens publics de signature, de lancer des demandes de signature en masse, d'utiliser le mode Kiosque pour les signatures en personne ou de collecter des signatures à partir de documents intégrés directement à votre site web. À chaque fois que vous initiez ou envoyez un document, un crédit est utilisé — et ce document peut inclure jusqu'à 10 signataires.",
"benefit-bulksend-api-team-year-help": "Ce forfait inclut 500 crédits premium par an qui vous permettent d'envoyer des documents via l'API, de partager des liens publics de signature, de lancer des demandes de signature en masse, d'utiliser le mode Kiosque pour les signatures en personne ou de collecter des signatures à partir de documents intégrés directement à votre site web. À chaque fois que vous initiez ou envoyez un document, un crédit est utilisé — et ce document peut inclure jusqu'à 10 signataires.",
"failed-pdf-for-prefill-processing": "Impossible de charger le PDF pour le traitement du préremplissage.",
"attachments-docx-upload-failed": "Nous rencontrons actuellement un problème. Veuillez réessayer.",
"attachments-docx-upload-limit": "La limite de téléchargement pour les fichiers DOCX volumineux a été atteinte. Veuillez attendre 4 minutes avant de réessayer.",
"attach-all-role-to-signer": "Veuillez attribuer tous les rôles au signataire.",
"upgrade-to-edit": "Mettre à niveau pour modifier",
"hide-live-chat-for-signers": "Masquer le chat en direct pour les signataires",
"hide-live-chat-for-signers-help": "Lorsqu'activé, les widgets de chat en direct seront masqués pour les signataires sur les pages de signature."
"hide-live-chat-for-signers-help": "Lorsqu'activé, les widgets de chat en direct seront masqués pour les signataires sur les pages de signature.",
"attachments-creator-hint": "Les signataires peuvent téléverser des fichiers qui seront ajoutés à la fin du document.",
"attachments-drop-hint": "Déposez les fichiers ici ou cliquez pour choisir",
"attachments-limits": "PDF, DOCX, PNG, JPG. Jusqu'à {{maxFiles}} fichiers, {{maxSizeMB}} Mo chacun.",
"attachments-invalid-type": "Type de fichier non pris en charge : {{name}}",
"attachments-max-files": "Maximum {{maxFiles}} pièces jointes autorisées.",
"attachments-remove-confirm": "La suppression efface uniquement les métadonnées. Les pages déjà ajoutées resteront dans le document. Continuer ?",
"conditional-logic": "Logique conditionnelle",
"enabled": "Activé",
"disabled": "Désactivé",
"logic-no-sources": "Aucun autre champ de ce signataire ne peut servir de source.",
"logic-action": "Action",
"logic-when": "Lorsque",
"logic-all": "toutes",
"logic-any": "au moins une",
"logic-of-following": "des conditions suivantes sont vraies",
"logic-add-rule": "Ajouter une règle",
"logic-pick-value": "Choisir une valeur",
"logic-value-placeholder": "Saisir une valeur",
"logic-action-show": "Afficher ce champ",
"logic-action-hide": "Masquer ce champ",
"logic-action-require": "Rendre obligatoire",
"logic-action-optional": "Rendre facultatif",
"logic-op-equals": "est égal à",
"logic-op-notEquals": "n'est pas égal à",
"logic-op-checked": "est coché",
"logic-op-unchecked": "n'est pas coché",
"widget-hidden-due-to-logic": "Ce widget est masqué en raison de la logique conditionnelle.",
"remove": "Supprimer",
"attachement-note": "Remarque : Ce widget permet aux signataires de télécharger un fichier pendant le processus de signature. Le fichier téléchargé sera ajouté en tant que pages supplémentaires à la fin du PDF existant.",
"attachments-docx-not-supported": "Nous rencontrons actuellement un problème avec le téléchargement des fichiers DOCX. Veuillez essayer de télécharger le document dans un autre format pris en charge ou réessayer plus tard.",
"access-code": "Code d'accès",
"access-code-set": "Code d'accès défini",
"access-code-add": "Ajouter un code d'accès",
"access-code-help": "Secret optionnel connu uniquement de ce signataire. Il devra le saisir avant de voir ou de signer le document. Communiquez-le au destinataire par un autre canal (téléphone, en personne).",
"access-code-placeholder": "Au moins 6 chiffres",
"access-code-clear": "Effacer",
"access-code-must-be-numeric": "Le code d'accès ne doit contenir que des chiffres 0-9.",
"access-code-save": "Enregistrer",
"access-code-required": "L'expéditeur a attribué un code d'accès à ce destinataire. Saisissez-le pour continuer.",
"access-code-invalid": "Code d'accès incorrect. Veuillez vérifier auprès de l'expéditeur.",
"access-code-too-short": "Le code d'accès doit comporter au moins 6 chiffres.",
"access-code-verify": "Vérifier le code d'accès",
"access-code-verified": "Code d'accès vérifié.",
"access-code-warning": "Ce code d'accès ne peut pas être modifié ou récupéré une fois le document envoyé.",
"copy-access-code": "Copier le code d'accès",
"access-code-upgrade-msg": "Les codes d'accès sont disponibles uniquement dans les forfaits payants. Améliorez votre forfait pour ajouter des codes d'accès aux destinataires.",
"show-access-code": "Afficher le code daccès",
"hide-access-code": "Masquer le code daccès",
"already-copied-to-clipboard": "Déjà copié dans le presse-papiers"
}
@@ -284,6 +284,8 @@
"yes": "हाँ",
"copied": "कॉपी किया गया",
"something-went-wrong-mssg": "कुछ गलत हो गया, इस पृष्ठ को रीफ्रेश करने से यह समस्या हल हो सकती है।",
"attachments-docx-upload-failed": "फिलहाल एक समस्या आ रही है। कृपया पुनः प्रयास करें।",
"attachments-docx-upload-limit": "बड़ी DOCX फ़ाइलों के लिए अपलोड सीमा को पार कर लिया गया है। कृपया 4 मिनट तक प्रतीक्षा करें और फिर प्रयास करें।",
"token-generated": "टोकन सफलतापूर्वक उत्पन्न हुआ।",
"webhook": "वेबहूक",
"update-webhook": "वेबहूक अपडेट करें",
@@ -421,7 +423,10 @@
"image": "छवि",
"email": "ईमेल",
"number": "संख्या",
"draw": "आरेखित करें"
"draw": "आरेखित करें",
"attachments": "संलग्नक",
"attachments-add": "संलग्नक अपलोड करने के लिए क्लिक करें",
"attachments-count": "{{count}} संलग्नक"
},
"fields": "क्षेत्र",
"recipients": "प्राप्तकर्ता",
@@ -796,6 +801,7 @@
"quota-mail-reset": "आपके हस्ताक्षर अनुरोध ईमेल क्रेडिट इस पर रीसेट हो जाएंगे",
"quota-mail": "आपने इस महीने के लिए 15 हस्ताक्षर अनुरोध ईमेल की अपनी सीमा पूरी कर ली है। सीधे ईमेल भेजना जारी रखने के लिए अभी अपग्रेड करें।",
"quota-mail-tip": "सुझाव: आप अभी भी हस्ताक्षर अनुरोध लिंक को मैन्युअल रूप से साझा करके <1>असीमित दस्तावेज़ों</1> पर हस्ताक्षर कर सकते हैं।",
"quota-mail-limit-reached": "आपने 15 ईमेल क्रेडिट की अपनी मासिक सीमा पूरी कर ली है। सीधे ईमेल भेजना जारी रखने के लिए कृपया अपना प्लान अपग्रेड करें।\nआपके ईमेल क्रेडिट {{resetDate}} को रीसेट हो जाएंगे।",
"quota-mail-head": "कोटा पहुँच गया",
"unauthorized-modal": "आपको यह कार्रवाई करने की अनुमति नहीं है, कृपया {{adminEmail}} से संपर्क करें।",
"sent-this-month": "इस महीने भेजा गया",
@@ -1359,7 +1365,7 @@
"export-all-formdata-error": "आप फ़ॉर्म डेटा निर्यात नहीं कर सकते क्योंकि टेम्पलेट में डुप्लिकेट विजेट नाम हैं। कृपया नया टेम्पलेट बनाएं ताकि आप फ़ॉर्म डेटा निर्यात कर सकें।",
"export-formdata-error": "आप फ़ॉर्म डेटा निर्यात नहीं कर सकते क्योंकि दस्तावेज़ में डुप्लिकेट विजेट नाम हैं। कृपया नया दस्तावेज़ बनाएं जिसमें डुप्लिकेट विजेट नाम न हों।",
"duplicate-widget-name-error": "यह विजेट नाम पहले से ही किसी अन्य साइनर को सौंपा गया है। कृपया कोई दूसरा नाम चुनें।",
"duplicate-template-widget-error": "आप इस टेम्पलेट को डुप्लिकेट नहीं क सकत क्योंकि इसमें डुप्लिकेट विजेट नाम हैं।",
"duplicate-template-widget-error": "टेम्पलेट को डुप्लिकेट नहीं किया जा सकत क्योंकि कुछ विजेट नाम अलग-अलग भूमिकाओं में डुप्लिकेट हैं। डुप्लिकेट विजेट्स का नाम बदलें और फिर से प्रयास करें।",
"credit-alert": "क्रेडिट अलर्ट",
"document-form-data": "दस्तावेज़ फ़ॉर्म डेटा",
"view-formdata-paid-title-error": "'फ़ॉर्म डेटा देखें' सुविधा आपको हस्ताक्षर करते समय साइनरों द्वारा भरे गए विजेट डेटा को प्रिंट और डाउनलोड करने की सुविधा देती है.",
@@ -1558,5 +1564,54 @@
"attach-all-role-to-signer": "कृपया सभी भूमिकाएँ साइनर से जोड़ें।",
"upgrade-to-edit": "एडिट करने के लिए अपग्रेड करें",
"hide-live-chat-for-signers": "साइनर के लिए लाइव चैट छुपाएं",
"hide-live-chat-for-signers-help": "सक्षम होने पर, साइनिंग पेजों पर साइनर के लिए लाइव चैट विजेट छुपाए जाएंगे।"
"hide-live-chat-for-signers-help": "सक्षम होने पर, साइनिंग पेजों पर साइनर के लिए लाइव चैट विजेट छुपाए जाएंगे।",
"attachments-creator-hint": "हस्ताक्षरकर्ता ऐसी फ़ाइलें अपलोड कर सकते हैं जो दस्तावेज़ के अंत में जोड़ी जाएँगी।",
"attachments-drop-hint": "फ़ाइलें यहाँ छोड़ें या चुनने के लिए क्लिक करें",
"attachments-limits": "PDF, DOCX, PNG, JPG. अधिकतम {{maxFiles}} फ़ाइलें, प्रत्येक {{maxSizeMB}} MB।",
"attachments-invalid-type": "असमर्थित फ़ाइल प्रकार: {{name}}",
"attachments-max-files": "अधिकतम {{maxFiles}} संलग्नक अनुमत हैं।",
"attachments-remove-confirm": "हटाने से केवल मेटाडेटा हटेगा। पहले से जोड़े गए पृष्ठ दस्तावेज़ में बने रहेंगे। जारी रखें?",
"conditional-logic": "सशर्त तर्क",
"enabled": "सक्षम",
"disabled": "अक्षम",
"logic-no-sources": "इस हस्ताक्षरकर्ता का कोई अन्य फ़ील्ड स्रोत के रूप में उपयोग नहीं किया जा सकता।",
"logic-action": "कार्रवाई",
"logic-when": "जब",
"logic-all": "सभी",
"logic-any": "कोई भी",
"logic-of-following": "निम्न शर्तें सही हों",
"logic-add-rule": "नियम जोड़ें",
"logic-pick-value": "मान चुनें",
"logic-value-placeholder": "मान दर्ज करें",
"logic-action-show": "इस फ़ील्ड को दिखाएँ",
"logic-action-hide": "इस फ़ील्ड को छिपाएँ",
"logic-action-require": "अनिवार्य करें",
"logic-action-optional": "वैकल्पिक करें",
"logic-op-equals": "बराबर है",
"logic-op-notEquals": "बराबर नहीं है",
"logic-op-checked": "चेक किया गया है",
"logic-op-unchecked": "चेक नहीं किया गया है",
"widget-hidden-due-to-logic": "यह विजेट सशर्त तर्क के कारण छिपा हुआ है।",
"remove": "हटाएँ",
"attachement-note": "नोट: यह विजेट हस्ताक्षरकर्ताओं को साइनिंग प्रक्रिया के दौरान फ़ाइल अपलोड करने की अनुमति देता है। अपलोड की गई फ़ाइल मौजूदा PDF के अंत में अतिरिक्त पेज के रूप में जोड़ दी जाएगी।",
"attachments-docx-not-supported": "हम वर्तमान में DOCX फ़ाइल अपलोड में एक समस्या का सामना कर रहे हैं। कृपया दस्तावेज़ को किसी अन्य समर्थित प्रारूप में अपलोड करने का प्रयास करें, या बाद में पुनः प्रयास करें।",
"access-code": "एक्सेस कोड",
"access-code-set": "एक्सेस कोड सेट किया गया",
"access-code-add": "एक्सेस कोड जोड़ें",
"access-code-help": "वैकल्पिक गुप्त कोड जो केवल इस हस्ताक्षरकर्ता को पता है। दस्तावेज़ देखने या हस्ताक्षर करने से पहले उन्हें इसे दर्ज करना होगा। इसे प्राप्तकर्ता के साथ किसी अन्य माध्यम (फ़ोन, व्यक्तिगत रूप से) से साझा करें।",
"access-code-placeholder": "कम से कम 6 अंक",
"access-code-clear": "साफ़ करें",
"access-code-must-be-numeric": "एक्सेस कोड में केवल 0-9 अंक होने चाहिए।",
"access-code-save": "सहेजें",
"access-code-required": "प्रेषक ने इस प्राप्तकर्ता को एक एक्सेस कोड दिया है। जारी रखने के लिए इसे दर्ज करें।",
"access-code-invalid": "गलत एक्सेस कोड। कृपया प्रेषक से जांचें।",
"access-code-too-short": "एक्सेस कोड कम से कम 6 अंक का होना चाहिए।",
"access-code-verify": "एक्सेस कोड सत्यापित करें",
"access-code-verified": "एक्सेस कोड सत्यापित हो गया।",
"access-code-warning": "एक बार दस्तावेज़ भेज दिए जाने के बाद इस एक्सेस कोड को बदला या पुनः प्राप्त नहीं किया जा सकता।",
"copy-access-code": "एक्सेस कोड कॉपी करें",
"access-code-upgrade-msg": "एक्सेस कोड केवल भुगतान योजनाओं पर उपलब्ध हैं। प्राप्तकर्ताओं के लिए एक्सेस कोड जोड़ने के लिए अपनी योजना को अपग्रेड करें।",
"show-access-code": "एक्सेस कोड दिखाएं",
"hide-access-code": "एक्सेस कोड छिपाएं",
"already-copied-to-clipboard": "पहले से क्लिपबोर्ड में कॉपी किया गया है"
}
@@ -284,6 +284,8 @@
"yes": "Sì",
"copied": "Copiato",
"something-went-wrong-mssg": "Si è verificato un errore, L'aggiornamento della pagina potrebbe risolvere il problema.",
"attachments-docx-upload-failed": "Al momento si è verificato un problema. Riprova.",
"attachments-docx-upload-limit": "È stato raggiunto il limite di caricamento per file DOCX grandi. Attendi 4 minuti prima di riprovare.",
"token-generated": "Token generato con successo.",
"webhook": "Webhook",
"update-webhook": "Aggiorna Webhook",
@@ -421,7 +423,10 @@
"image": "immagine",
"email": "email",
"number": "numero",
"draw": "Disegno"
"draw": "Disegno",
"attachments": "allegati",
"attachments-add": "Clicca per caricare allegati",
"attachments-count": "{{count}} allegato/i"
},
"fields": "Campi",
"recipients": "Destinatari",
@@ -795,6 +800,7 @@
"quota-mail-info": "Per mantenere la qualità del servizio e prevenire lo spam, OpenSign consente fino a 15 email al mese con il piano gratuito. Esegui l'upgrade ora per l'invio illimitato di email.",
"quota-mail-reset": "I tuoi crediti email per richieste di firma saranno ripristinati il",
"quota-mail": "Hai raggiunto il limite di 15 email di richiesta di firma per questo mese. Passa al piano superiore per continuare a inviare email direttamente.",
"quota-mail-limit-reached": "Hai raggiunto il limite mensile di 15 crediti email. Effettua l'upgrade del tuo piano per continuare a inviare email direttamente.\nI tuoi crediti email verranno ripristinati il {{resetDate}}.",
"quota-mail-tip": "Suggerimento: Puoi ancora firmare un numero <1>illimitato de documenti</1> condividendo manualmente il link della richiesta di firma.",
"quota-mail-head": "Quota Raggiunta",
"unauthorized-modal": "Non hai l'autorizzazione per eseguire questa azione, contatta {{adminEmail}}.",
@@ -1359,7 +1365,7 @@
"export-all-formdata-error": "Non è possibile esportare i dati del modulo perché il modello contiene nomi di widget duplicati. Creare un nuovo modello per poter esportare i dati.",
"export-formdata-error": "Non è possibile esportare i dati del modulo perché il documento contiene nomi di widget duplicati. Creare un nuovo documento senza nomi duplicati per poter esportare i dati.",
"duplicate-widget-name-error": "Questo nome di widget è già assegnato a un altro firmatario. Scegli un nome diverso.",
"duplicate-template-widget-error": "Non è possibile duplicare questo modello perché contiene nomi di widget duplicati.",
"duplicate-template-widget-error": "Impossibile duplicare il modello perché alcuni nomi di widget sono duplicati in ruoli diversi. Rinomina i widget duplicati e riprova.",
"credit-alert": "Avviso di credito",
"document-form-data": "Dati del modulo del documento",
"view-formdata-paid-title-error": "La funzione 'Visualizza dati del modulo' consente di stampare e scaricare i dati dei widget compilati dai firmatari durante la firma.",
@@ -1558,5 +1564,54 @@
"attach-all-role-to-signer": "Per favore, assegna tutti i ruoli al firmatario.",
"upgrade-to-edit": "Esegui l'upgrade per modificare",
"hide-live-chat-for-signers": "Nascondi chat dal vivo per i firmatari",
"hide-live-chat-for-signers-help": "Se attivato, i widget di chat dal vivo saranno nascosti per i firmatari nelle pagine di firma."
"hide-live-chat-for-signers-help": "Se attivato, i widget di chat dal vivo saranno nascosti per i firmatari nelle pagine di firma.",
"attachments-creator-hint": "I firmatari possono caricare file che verranno aggiunti alla fine del documento.",
"attachments-drop-hint": "Trascina i file qui o clicca per selezionare",
"attachments-limits": "PDF, DOCX, PNG, JPG. Fino a {{maxFiles}} file, {{maxSizeMB}} MB ciascuno.",
"attachments-invalid-type": "Tipo di file non supportato: {{name}}",
"attachments-max-files": "Massimo {{maxFiles}} allegati consentiti.",
"attachments-remove-confirm": "La rimozione cancella solo i metadati. Le pagine già aggiunte resteranno nel documento. Continuare?",
"conditional-logic": "Logica condizionale",
"enabled": "Abilitato",
"disabled": "Disabilitato",
"logic-no-sources": "Nessun altro campo di questo firmatario può essere usato come origine.",
"logic-action": "Azione",
"logic-when": "Quando",
"logic-all": "tutte",
"logic-any": "una qualsiasi",
"logic-of-following": "delle seguenti condizioni sono vere",
"logic-add-rule": "Aggiungi regola",
"logic-pick-value": "Seleziona un valore",
"logic-value-placeholder": "Inserisci valore",
"logic-action-show": "Mostra questo campo",
"logic-action-hide": "Nascondi questo campo",
"logic-action-require": "Rendi obbligatorio",
"logic-action-optional": "Rendi facoltativo",
"logic-op-equals": "è uguale a",
"logic-op-notEquals": "non è uguale a",
"logic-op-checked": "è selezionato",
"logic-op-unchecked": "non è selezionato",
"widget-hidden-due-to-logic": "Questo widget è nascosto a causa della logica condizionale.",
"remove": "Rimuovi",
"attachement-note": "Nota: Questo widget consente ai firmatari di caricare un file durante il processo di firma. Il file caricato verrà aggiunto come pagine aggiuntive alla fine del PDF esistente.",
"attachments-docx-not-supported": "Al momento stiamo riscontrando un problema con il caricamento dei file DOCX. Prova a caricare il documento in un altro formato supportato oppure riprova più tardi.",
"access-code": "Codice di accesso",
"access-code-set": "Codice di accesso impostato",
"access-code-add": "Aggiungi codice di accesso",
"access-code-help": "Codice segreto facoltativo noto solo a questo firmatario. Dovrà inserirlo prima di visualizzare o firmare il documento. Comunicalo al destinatario tramite un canale alternativo (telefono, di persona).",
"access-code-placeholder": "Almeno 6 cifre",
"access-code-clear": "Cancella",
"access-code-must-be-numeric": "Il codice di accesso deve contenere solo cifre 0-9.",
"access-code-save": "Salva",
"access-code-required": "Il mittente ha assegnato un codice di accesso a questo destinatario. Inseriscilo per continuare.",
"access-code-invalid": "Codice di accesso errato. Verifica con il mittente.",
"access-code-too-short": "Il codice di accesso deve contenere almeno 6 cifre.",
"access-code-verify": "Verifica codice di accesso",
"access-code-verified": "Codice di accesso verificato.",
"access-code-warning": "Questo codice di accesso non può essere modificato o recuperato una volta inviato il documento.",
"copy-access-code": "Copia codice di accesso",
"access-code-upgrade-msg": "I codici di accesso sono disponibili solo nei piani a pagamento. Aggiorna il tuo piano per aggiungere codici di accesso per i destinatari.",
"show-access-code": "Mostra codice di accesso",
"hide-access-code": "Nascondi codice di accesso",
"already-copied-to-clipboard": "Già copiato negli appunti"
}
@@ -284,6 +284,8 @@
"yes": "네",
"copied": "복사됨",
"something-went-wrong-mssg": "문제가 발생했습니다. 새로고침 시 문제가 해결될 수 있습니다.",
"attachments-docx-upload-failed": "현재 문제가 발생했습니다. 다시 시도해 주세요.",
"attachments-docx-upload-limit": "큰 DOCX 파일에 대한 업로드 제한에 도달했습니다. 4분 후에 다시 시도해 주세요.",
"token-generated": "토큰이 성공적으로 생성되었습니다.",
"webhook": "웹훅",
"update-webhook": "웹훅 업데이트",
@@ -421,7 +423,10 @@
"image": "이미지",
"email": "이메일",
"number": "숫자",
"draw": "그리기"
"draw": "그리기",
"attachments": "첨부파일",
"attachments-add": "클릭하여 첨부파일 업로드",
"attachments-count": "{{count}}개 첨부파일"
},
"fields": "필드",
"recipients": "수신자",
@@ -795,6 +800,7 @@
"quota-mail-info": "서비스 품질을 유지하고 스팸을 방지하기 위해 OpenSign은 무료 플랜에서 월 최대 15개의 이메일을 허용합니다. 지금 업그레이드하여 무제한 이메일 전송을 이용하세요.",
"quota-mail-reset": "서명 요청 이메일 크레딧은 다음에 재설정됩니다.",
"quota-mail": "이번 달 서명 요청 이메일 15개 한도에 도달했습니다. 지금 업그레이드하여 이메일을 계속 직접 보내세요.",
"quota-mail-limit-reached": "월간 이메일 크레딧 15개 한도에 도달했습니다. 이메일을 계속 직접 보내려면 요금제를 업그레이드하세요.\n이메일 크레딧은 {{resetDate}}에 재설정됩니다.",
"quota-mail-tip": "팁: 서명 요청 링크를 수동으로 공유하여 <1>무제한 문서</1>에 계속 서명할 수 있습니다.",
"quota-mail-head": "할당량 도달",
"unauthorized-modal": "이 작업을 수행할 권한이 없습니다. {{adminEmail}}에게 문의하세요.",
@@ -1359,7 +1365,7 @@
"export-all-formdata-error": "템플릿에 중복된 위젯 이름이 포함되어 있어 양식 데이터를 내보낼 수 없습니다. 양식 데이터를 내보내려면 새 템플릿을 만드십시오.",
"export-formdata-error": "문서에 중복된 위젯 이름이 포함되어 있어 양식 데이터를 내보낼 수 없습니다. 양식 데이터를 내보내려면 중복된 위젯 이름이 없는 새 문서를 만드십시오.",
"duplicate-widget-name-error": "이 위젯 이름은 이미 다른 서명자에게 할당되었습니다. 다른 이름을 선택하십시오.",
"duplicate-template-widget-error": "이 템플릿에는 중복된 위젯 이름이 포함되어 있어 복제할 수 없습니다.",
"duplicate-template-widget-error": "일부 위젯 이름이 다른 역할에서 중복되어 템플릿을 복제할 수 없습니다. 중복된 위젯의 이름을 변경한 후 다시 시도하세요.",
"credit-alert": "크레딧 알림",
"document-form-data": "문서 양식 데이터",
"view-formdata-paid-title-error": "양식 데이터 보기 기능을 사용하면 서명자가 서명하는 동안 채운 위젯 데이터를 인쇄하고 다운로드할 수 있습니다.",
@@ -1558,5 +1564,54 @@
"attach-all-role-to-signer": "서명자에게 모든 역할을 연결해 주세요.",
"upgrade-to-edit": "편집하려면 업그레이드하세요",
"hide-live-chat-for-signers": "서명자에게 라이브 채팅 숨기기",
"hide-live-chat-for-signers-help": "활성화하면 서명 페이지에서 서명자에게 라이브 채팅 위젯이 숨겨집니다."
"hide-live-chat-for-signers-help": "활성화하면 서명 페이지에서 서명자에게 라이브 채팅 위젯이 숨겨집니다.",
"attachments-creator-hint": "서명자는 문서 끝에 추가될 파일을 업로드할 수 있습니다.",
"attachments-drop-hint": "파일을 여기에 놓거나 클릭하여 선택하세요",
"attachments-limits": "PDF, DOCX, PNG, JPG. 최대 {{maxFiles}}개 파일, 각 {{maxSizeMB}} MB.",
"attachments-invalid-type": "지원되지 않는 파일 형식: {{name}}",
"attachments-max-files": "최대 {{maxFiles}}개의 첨부파일이 허용됩니다.",
"attachments-remove-confirm": "제거하면 메타데이터만 삭제됩니다. 이미 추가된 페이지는 문서에 남아 있습니다. 계속하시겠습니까?",
"conditional-logic": "조건부 로직",
"enabled": "활성화됨",
"disabled": "비활성화됨",
"logic-no-sources": "이 서명자의 다른 필드를 소스로 사용할 수 없습니다.",
"logic-action": "동작",
"logic-when": "조건",
"logic-all": "모두",
"logic-any": "하나라도",
"logic-of-following": "다음 조건이 참일 때",
"logic-add-rule": "규칙 추가",
"logic-pick-value": "값 선택",
"logic-value-placeholder": "값 입력",
"logic-action-show": "이 필드 표시",
"logic-action-hide": "이 필드 숨기기",
"logic-action-require": "필수로 만들기",
"logic-action-optional": "선택 사항으로 만들기",
"logic-op-equals": "같음",
"logic-op-notEquals": "같지 않음",
"logic-op-checked": "체크됨",
"logic-op-unchecked": "체크되지 않음",
"widget-hidden-due-to-logic": "이 위젯은 조건부 논리로 인해 숨겨져 있습니다.",
"remove": "제거",
"attachement-note": "참고: 이 위젯을 사용하면 서명자가 서명 과정 중에 파일을 업로드할 수 있습니다. 업로드된 파일은 기존 PDF의 마지막에 추가 페이지로 첨부됩니다.",
"attachments-docx-not-supported": "현재 DOCX 파일 업로드에 문제가 발생하고 있습니다. 다른 지원되는 형식으로 문서를 업로드하시거나 잠시 후 다시 시도해 주세요.",
"access-code": "접속 코드",
"access-code-set": "접속 코드가 설정됨",
"access-code-add": "접속 코드 추가",
"access-code-help": "이 서명자만 아는 선택적 비밀번호입니다. 문서를 보거나 서명하기 전에 반드시 입력해야 합니다. 전화 또는 직접 만나서 등 별도의 경로로 수신자에게 전달하세요.",
"access-code-placeholder": "6자리 이상 숫자",
"access-code-clear": "지우기",
"access-code-must-be-numeric": "접속 코드는 0-9 숫자만 포함해야 합니다.",
"access-code-save": "저장",
"access-code-required": "발신자가 이 수신자에게 접속 코드를 지정했습니다. 계속하려면 입력하세요.",
"access-code-invalid": "잘못된 접속 코드입니다. 발신자에게 문의하세요.",
"access-code-too-short": "접속 코드는 6자리 이상 숫자여야 합니다.",
"access-code-verify": "접속 코드 확인",
"access-code-verified": "접속 코드가 확인되었습니다.",
"access-code-warning": "문서가 발송된 후에는 이 접속 코드를 변경하거나 검색할 수 없습니다.",
"copy-access-code": "접속 코드 복사",
"access-code-upgrade-msg": "접속 코드는 유료 요금제에서만 사용할 수 있습니다. 수신자에게 접속 코드를 추가하려면 요금제를 업그레이드하세요.",
"show-access-code": "액세스 코드 표시",
"hide-access-code": "액세스 코드 숨기기",
"already-copied-to-clipboard": "이미 클립보드에 복사됨"
}
@@ -38,7 +38,7 @@ const EXCLUDED_WIDGET_TYPES = new Set([
"image",
"signature",
"stamp",
"initials"
"initials",
]);
const ALL_EXCLUDED_TYPES = new Set([
...EXCLUDED_PREFILL_TYPES,
@@ -80,7 +80,14 @@ const BulkSendUi = (props) => {
const initializeWidgetsWithValues = async () => {
if (props?.Placeholders?.length > 0) {
const roles = props?.Placeholders?.filter((p) => !p.signerObjId) || [];
const emails = [];
const reservedEmails = (
props?.Placeholders?.filter((p) => !!p?.signerObjId) || []
)
.map(
(role) =>
normalizeKey(role?.email) || normalizeKey(role?.signerPtr?.Email)
)
.filter(Boolean);
const users = await Promise.all(
roles?.map(async (role) => {
@@ -146,10 +153,6 @@ const BulkSendUi = (props) => {
})
)
);
const signerEmail =
normalizeKey(role?.email) || role?.signerPtr?.Email;
if (signerEmail) emails.push(signerEmail);
return {
fieldId: role.Id,
email: "",
@@ -171,7 +174,7 @@ const BulkSendUi = (props) => {
setStepKey("recipients");
setStep(0);
}
setEmails(emails);
setEmails(reservedEmails);
// Build headers with required ordering:
// 1) all role emails first
@@ -290,24 +293,28 @@ const BulkSendUi = (props) => {
function validateEmails(data) {
for (const [rowIndex, item] of data.entries()) {
let email = "";
const rowEmails = new Set();
const rowNumber = rowIndex + 1;
const filterFields = item?.fields || [];
for (const field of filterFields) {
// skip prefill
if (field?.label === "prefill") continue;
const normalizedEmail = normalizeKey(field?.email);
const params = { email: field.email, row: rowNumber };
if (!field.email) {
if (!normalizedEmail) {
alert(t("email-not-found-in-row", { ...params }));
return false;
} else if (!emailRegex.test(field.email)) {
} else if (!emailRegex.test(normalizedEmail)) {
alert(t("invalid-email-found-in-row", { ...params }));
return false;
} else if (email === field.email || emails?.includes(field.email)) {
} else if (
rowEmails.has(normalizedEmail) ||
emails?.includes(normalizedEmail)
) {
alert(t("duplicate-email-found-in-row", { ...params }));
return false;
} else {
email = field.email;
rowEmails.add(normalizedEmail);
}
}
}
@@ -4,14 +4,14 @@ import { useTranslation } from "react-i18next";
const PrefillWidgets = ({ prefills = [], setPrefills, onNext }) => {
const { t } = useTranslation();
const handleWidgetDetails = (value, widgetIndex) => {
const handleWidgetDetails = (value, widgetLabel) => {
setPrefills((prev) => {
const widgets = [...(prev ?? [])];
const w = widgets[widgetIndex];
if (!w) return prev;
widgets[widgetIndex] = {
...w,
options: { ...w.options, response: value },
const index = widgets.findIndex((w) => w?.label === widgetLabel);
if (index === -1) return prev;
widgets[index] = {
...widgets[index],
options: { ...widgets[index].options, response: value },
response: value
};
return widgets;
@@ -37,16 +37,17 @@ const PrefillWidgets = ({ prefills = [], setPrefills, onNext }) => {
a.pageNumber !== b.pageNumber
? a.pageNumber - b.pageNumber
: (a.yPosition ?? 0) - (b.yPosition ?? 0)
).map((widget, index) => (
<RenderWidgets
key={widget.key}
showLabel
widget={widget}
handleWidgetDetails={(value) =>
handleWidgetDetails(value, index)
}
/>
))}
)
.map((widget) => (
<RenderWidgets
key={widget.key}
showLabel
widget={widget}
handleWidgetDetails={(value) =>
handleWidgetDetails(value, widget.label)
}
/>
))}
</div>
</div>
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from "react";
import React, { memo, useState, useEffect, useRef } from "react";
import "../../styles/opensigndrive.css";
import axios from "axios";
import { ContextMenu } from "radix-ui";
@@ -676,4 +676,4 @@ function DriveBody(props) {
);
}
export default DriveBody;
export default memo(DriveBody);
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
@@ -23,6 +23,7 @@ function DropdownWidgetOption(props) {
const layoutArr = ["vertical", "horizontal"];
const isPrefillExist = props?.roleName === "prefill";
const resetState = () => {
setDropdownOptionList(["Option-1", "Option-2"]);
setDropdownName(props.currWidgetsDetails?.options?.name || props.type);
@@ -150,7 +151,7 @@ function DropdownWidgetOption(props) {
status,
defaultData,
isHideLabel,
WidgetLayout
WidgetLayout,
);
resetState();
};
@@ -407,6 +408,7 @@ function DropdownWidgetOption(props) {
} w-full h-[1px] bg-[#9f9f9f]`}
></div>
<button
disabled={dropdownOptionList.length === 0 && true}
type="submit"
@@ -710,6 +710,7 @@ function Placeholder(props) {
});
};
return (
<>
{/* Check if a text widget (prefill type) exists. Once the user enters a value and clicks outside or the widget becomes non-selectable, it should appear as plain text (just like embedded text in a document). When the user clicks on the text again, it should become editable. */}
@@ -21,7 +21,6 @@ const RecipientList = (props) => {
const isWidgetExist = (Id) => {
return props.signerPos.some((x) => x.Id === Id && x.placeHolder);
};
//handle drag start
const handleDragStart = (e, id) => {
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
@@ -271,4 +270,4 @@ const RecipientList = (props) => {
);
};
export default RecipientList;
export default RecipientList;
@@ -1,3 +1,4 @@
import { useState } from "react";
import RecipientList from "./RecipientList";
// import { Tooltip } from "react-tooltip";
import { useTranslation } from "react-i18next";
@@ -9,6 +10,7 @@ function SignerListPlace(props) {
props?.setIsAddSigner(true);
props.setIsTour && props.setIsTour(false);
};
return (
<div>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
@@ -20,7 +22,9 @@ function SignerListPlace(props) {
</span>
</div>
<div className="overflow-auto hide-scrollbar max-h-[180px]">
<RecipientList {...props} />
<RecipientList
{...props}
/>
</div>
<div className="mx-1">
{props.handleAddSigner ? (
@@ -49,4 +53,4 @@ function SignerListPlace(props) {
);
}
export default SignerListPlace;
export default SignerListPlace;
@@ -36,7 +36,7 @@ function WidgetComponent(props) {
{ id: 14, text: "dropdown" },
{ id: 15, text: radioButtonWidget },
{ id: 16, text: "image" },
{ id: 17, text: drawWidget }
{ id: 17, text: drawWidget },
];
// Create all drag refs in one go
@@ -90,9 +90,12 @@ function WidgetComponent(props) {
);
const selfSignWidgets = widgetList.filter(
(data) =>
!["dropdown", radioButtonWidget, textInputWidget, drawWidget].includes(
data.type
)
![
"dropdown",
radioButtonWidget,
textInputWidget,
drawWidget,
].includes(data.type)
);
//if user select prefill role then allow only date,image,text,checkbox,radio,dropdownAdd commentMore actions
//dropdown widget should only be show in template flow
@@ -52,6 +52,7 @@ const WidgetNameModal = (props) => {
[type]
);
const handleHintPlaceholder = () => {
const type = props.defaultdata?.type;
@@ -146,7 +147,8 @@ const WidgetNameModal = (props) => {
return;
}
}
props.handleData(formdata);
let payload = formdata;
props.handleData(payload);
}
setFormdata({
isReadOnly: false,
@@ -386,9 +388,7 @@ const WidgetNameModal = (props) => {
)}
{isSignOrInitials && (
<div className="mb-[0.75rem]">
<label className="text-[14px] mb-[0.7rem]">
{t("rotation")}
</label>
<label className="text-[14px] mb-[0.7rem]">{t("rotation")}</label>
<div className="ml-[7px] flex items-center gap-[10px]">
<select
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs w-[120px]"
@@ -418,6 +418,7 @@ const WidgetNameModal = (props) => {
/>
</div>
)}
{showFontControls && (
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
<div className="flex items-center gap-2 ">
@@ -470,6 +471,7 @@ const WidgetNameModal = (props) => {
</div>
)}
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary">
{t("save")}
@@ -151,6 +151,9 @@ function WidgetsValueModal(props) {
const type = currWidgetsDetails?.type;
const widgetTypeTranslation = t(`widgets-name.${currWidgetsDetails?.type}`);
const getLogicMap = () => new Map();
const [widgetValue, setWidgetValue] = useState(() => {
if (currWidgetsDetails.type === "checkbox") {
return undefined;
@@ -1036,6 +1039,11 @@ function WidgetsValueModal(props) {
? e.target?.value?.trim()
: e.target.value;
setWidgetValue(value);
props.setCurrWidgetsDetails?.((prev) =>
prev && prev.key === currWidgetsDetails?.key
? { ...prev, options: { ...prev.options, response: value } }
: prev
);
onChangeInput(
value,
currWidgetsDetails,
@@ -1588,24 +1596,24 @@ function WidgetsValueModal(props) {
? String(data.name)
: "";
return (
<div key={ind} className="text-base-content select-none-cls">
<label
// htmlFor={`radio-${currWidgetsDetails?.key + ind}`}
className="cursor-pointer flex items-center text-sm gap-1"
>
<input
id={`radio-${currWidgetsDetails?.key + ind}`}
className={`op-radio op-radio-xs mt-1`}
type="radio"
value={label}
checked={handleRadioCheck(label?.trim())}
onChange={(e) => {
handleCheckRadio(e.target.value?.trim());
}}
/>
<span>{label}</span>
</label>
</div>
<div key={ind} className="text-base-content select-none-cls">
<label
// htmlFor={`radio-${currWidgetsDetails?.key + ind}`}
className="cursor-pointer flex items-center text-sm gap-1"
>
<input
id={`radio-${currWidgetsDetails?.key + ind}`}
className={`op-radio op-radio-xs mt-1`}
type="radio"
value={label}
checked={handleRadioCheck(label?.trim())}
onChange={(e) => {
handleCheckRadio(e.target.value?.trim());
}}
/>
<span>{label}</span>
</label>
</div>
);
})}
</div>
@@ -1650,6 +1658,10 @@ function WidgetsValueModal(props) {
//function is used to check current widget is required or optional
const handleCheckOptional = () => {
const effectiveStatus =
currWidgetsDetails?.options?.status ||
"required";
let isRequired = false;
const isCheckBox =
!currWidgetsDetails.options?.isReadOnly &&
@@ -1663,14 +1675,16 @@ function WidgetsValueModal(props) {
isRequired = true;
}
} else {
isRequired = currWidgetsDetails.options?.status === "required";
isRequired = effectiveStatus === "required";
}
if (isRequired) {
setIsOptional(false);
}
};
//function is used to show how many field left and how many total widget
const HandleRequiredField = () => {
let widgetsPosition = [];
if (uniqueId) {
const currSignerWidget = xyPosition?.find(
@@ -1680,28 +1694,25 @@ function WidgetsValueModal(props) {
} else {
widgetsPosition = xyPosition;
}
//generate all nested level in single level
const flatPlaceholder = widgetsPosition?.flatMap((page) =>
page.pos
.filter((widget) => !widget.options?.isReadOnly)
.map((widget) => ({
widget,
pageNumber: page.pageNumber
}))
.filter((widget) => {
if (widget.options?.isReadOnly) return false;
return true;
})
.map((widget) => ({ widget, pageNumber: page.pageNumber }))
);
let totalWidget = 0;
let alreadyValue = 0;
widgetsPosition?.forEach((page) => {
page.pos.forEach((field) => {
if (!field?.options?.isReadOnly) {
const isValueExist =
field.options?.response || field.options?.defaultValue;
totalWidget++;
if (isValueExist) {
alreadyValue++;
}
}
});
flatPlaceholder?.forEach(({ widget: field }) => {
const isValueExist =
field.options?.response || field.options?.defaultValue;
totalWidget++;
if (isValueExist) {
alreadyValue++;
}
});
const leftRequiredWidget = totalWidget - alreadyValue;
@@ -1826,10 +1837,13 @@ function WidgetsValueModal(props) {
const editableWidgets = (widgetsPosition?.placeHolder ?? []).flatMap(
({ pos = [], pageNumber }) =>
pos
.filter(
({ options }) =>
!options?.isReadOnly
)
.filter(({ options, key }) => {
if (
options?.isReadOnly
)
return false;
return true; // ← must be OUTSIDE the ee block
})
.map((widget) => ({ widget, pageNumber }))
);
//get current index of widget
@@ -1932,10 +1946,14 @@ function WidgetsValueModal(props) {
//'handleFinishButton' function is used to show finish button click on any widget if all required widgets have response
const handleFinishButton = () => {
const widgetsPosition = xyPosition?.find((data) => data.Id === uniqueId);
//using 'flatMap' create all nested array in one level
const editableWidgets = widgetsPosition?.placeHolder?.flatMap((page) =>
page.pos
.filter((widget) => !widget.options?.isReadOnly)
.filter((widget) => {
if (widget.options?.isReadOnly) return false;
return true; // ← outside ee block
})
.map((widget) => ({ ...widget, pageNumber: page.pageNumber }))
);
const getcurrentwidget = editableWidgets?.find(
@@ -1945,8 +1963,12 @@ function WidgetsValueModal(props) {
props?.setCurrWidgetsDetails(getcurrentwidget);
}
let isResponse = true;
//condition to check all required widgets have response or not then show finish buutton
//condition to check all required widgets have response or not then show finish button
for (const data of editableWidgets) {
// Use the dynamic required status (respects require/optional logic actions).
const effectiveStatus =
data.options?.status ||
"required";
if (data?.type === "checkbox") {
const minCount = data.options?.validation?.minRequiredCount;
const parseMin = minCount && parseInt(minCount);
@@ -1962,7 +1984,7 @@ function WidgetsValueModal(props) {
} else if (
!data.options.response &&
!data?.options?.defaultValue &&
data.options?.status === "required"
effectiveStatus === "required"
) {
isResponse = false;
break;
@@ -2002,7 +2024,7 @@ function WidgetsValueModal(props) {
<div className="p-1 mt-3">
<span className="text-base text-base-content">
{
t("finish-mssg")
t("finish-mssg")
}
</span>
</div>
@@ -2013,7 +2035,7 @@ function WidgetsValueModal(props) {
onClick={() => handleFinish()}
>
{
t("finish")
t("finish")
}
</button>
<button
+169 -58
View File
@@ -34,6 +34,86 @@ export const drawWidget = "draw";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
export const cellsWidget = "cells";
const duplicateAutoApplyWidgetTypes = [
"name",
"company",
"job title",
"email",
textInputWidget
];
const normalizeDuplicateWidgetType = (type) => {
if (typeof type !== "string") {
return type;
}
const normalizedType = type.trim().toLowerCase();
// Backward compatibility for legacy payloads.
if (normalizedType === "textbox") {
return textInputWidget;
}
return normalizedType;
};
const normalizeDuplicateWidgetName = (name) =>
typeof name === "string" ? name.trim().toLowerCase() : "";
const isDuplicateAutoApplyWidget = (widget) =>
duplicateAutoApplyWidgetTypes.includes(
normalizeDuplicateWidgetType(widget?.type)
);
const getDuplicateAutoApplyName = (widget) => {
if (!isDuplicateAutoApplyWidget(widget)) {
return "";
}
return normalizeDuplicateWidgetName(widget?.options?.name);
};
const getWidgetResponseValue = (widget) =>
widget?.options?.response ?? widget?.options?.defaultValue;
const getDuplicateResponseMap = (widgets = []) => {
const responseByName = new Map();
widgets.forEach((widget) => {
const widgetName = getDuplicateAutoApplyName(widget);
const response = getWidgetResponseValue(widget);
if (
widgetName &&
response !== undefined &&
response !== "" &&
!responseByName.has(widgetName)
) {
responseByName.set(widgetName, response);
}
});
return responseByName;
};
const applyDuplicateResponsesToWidgets = (
widgets = [],
responseByName = getDuplicateResponseMap(widgets)
) => {
if (responseByName.size === 0) {
return widgets;
}
return widgets.map((widget) => {
const widgetName = getDuplicateAutoApplyName(widget);
if (!widgetName || !responseByName.has(widgetName)) {
return widget;
}
return {
...widget,
options: {
...widget.options,
response: responseByName.get(widgetName),
defaultValue: ""
}
};
});
};
const applyDuplicateResponsesToPages = (pages = []) => {
const responseByName = getDuplicateResponseMap(
pages.flatMap((page) => page?.pos || [])
);
return pages.map((page) => ({
...page,
pos: applyDuplicateResponsesToWidgets(page?.pos || [], responseByName)
}));
};
export function getEnv() {
return window?.RUNTIME_ENV || {};
}
@@ -1120,6 +1200,28 @@ export const onChangeInput = (
dateDetails,
textWidgetHeight
) => {
const isDuplicateValueUpdate = !dateFormat && !textWidgetHeight;
const shouldAutoApplyDuplicateWidget = (position) => {
const currentName = getDuplicateAutoApplyName(currentPosition);
const positionName = getDuplicateAutoApplyName(position);
return currentName && positionName && currentName === positionName;
};
const applyDuplicateWidgetValue = (position) => {
if (
position?.options?.response === value &&
position?.options?.defaultValue === ""
) {
return position;
}
return {
...position,
options: {
...position.options,
response: value,
defaultValue: ""
}
};
};
const isSigners = xyPosition.some(
(data) => data.signerPtr || data.Role === "prefill"
);
@@ -1137,8 +1239,7 @@ export const onChangeInput = (
(data) => data.pageNumber === index
);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer[0].pos;
const addSignPos = getXYdata.map((position) => {
const updatePositionValue = (position) => {
if (position.key === currentPosition.key) {
if (dateFormat) {
return {
@@ -1177,40 +1278,39 @@ export const onChangeInput = (
) {
return { ...position, Height: textWidgetHeight };
} else {
return {
...position,
options: {
...position.options,
response: value,
defaultValue: ""
}
};
return applyDuplicateWidgetValue(position);
}
}
if (
isDuplicateValueUpdate &&
shouldAutoApplyDuplicateWidget(position)
) {
return applyDuplicateWidgetValue(position);
}
return position;
};
setXyPosition((prevPosition) => {
const sourcePosition = Array.isArray(prevPosition)
? prevPosition
: xyPosition;
return sourcePosition.map((obj) => {
if (obj.Id !== userId) {
return obj;
}
const updatedPlaceHolder = (obj.placeHolder || []).map((page) => ({
...page,
pos: page.pos.map(updatePositionValue)
}));
return {
...obj,
placeHolder: applyNumberFormulasToPages(updatedPlaceHolder)
};
});
});
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === index) {
return { ...obj, pos: addSignPos };
}
return obj;
});
const recalculatedPages = applyNumberFormulasToPages(newUpdateSignPos);
const newUpdateSigner = xyPosition.map((obj) => {
if (obj.Id === userId) {
return { ...obj, placeHolder: recalculatedPages };
}
return obj;
});
setXyPosition(newUpdateSigner);
}
}
} else {
let getXYdata = xyPosition[index].pos;
const updatePosition = getXYdata.map((positionData) => {
const updatePositionValue = (positionData) => {
if (positionData.key === currentPosition.key) {
if (dateFormat) {
return {
@@ -1233,26 +1333,28 @@ export const onChangeInput = (
) {
return { ...positionData, Height: textWidgetHeight };
} else {
return {
...positionData,
options: {
...positionData.options,
response: value,
defaultValue: ""
}
};
return applyDuplicateWidgetValue(positionData);
}
}
return positionData;
});
const updatePlaceholder = xyPosition.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: updatePosition };
if (
isDuplicateValueUpdate &&
shouldAutoApplyDuplicateWidget(positionData)
) {
return applyDuplicateWidgetValue(positionData);
}
return obj;
return positionData;
};
setXyPosition((prevPosition) => {
const sourcePosition = Array.isArray(prevPosition)
? prevPosition
: xyPosition;
const updatePlaceholder = sourcePosition.map((obj) => ({
...obj,
pos: obj.pos.map(updatePositionValue)
}));
return applyNumberFormulasToPages(updatePlaceholder);
});
setXyPosition(applyNumberFormulasToPages(updatePlaceholder));
}
};
//function to increase height of text area on press enter
@@ -4184,10 +4286,8 @@ export const updateDateWidgetsRes = (
: [];
const sortedPlaceHolder = placeHolder
.sort((a, b) => a.pageNumber - b.pageNumber)
.map((ph) => ({
...ph,
// Sort positions within each page
pos: [...ph.pos]
.map((ph) => {
const sortedWidgets = [...ph.pos]
.sort((a, b) => {
// Sort widgets by Y position (top to bottom) and X position (left to right)
// Treat widgets within 5px Y difference as belonging to the same row
@@ -4219,9 +4319,14 @@ export const updateDateWidgetsRes = (
};
}
return widget;
})
}));
return { ...item, placeHolder: sortedPlaceHolder };
});
let widgetsWithResponses = sortedWidgets;
return { ...ph, pos: widgetsWithResponses };
});
return {
...item,
placeHolder: applyDuplicateResponsesToPages(sortedPlaceHolder)
};
}
return item;
@@ -4319,8 +4424,11 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => {
if (checkboxExist) {
//get all required type checkbox
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
(position) =>
!position.options?.isReadOnly && position.type === "checkbox"
(position) => {
return (
!position.options?.isReadOnly && position.type === "checkbox"
);
}
);
//if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check
//also validate to minimum and maximum required checkbox
@@ -4372,10 +4480,13 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => {
else {
//get all required type widgets except checkbox and radio
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
(position) =>
position.type === "signature" ||
(position.options?.status === "required" &&
position.type !== "checkbox")
(position) => {
return (
position.type === "signature" ||
(position.options?.status === "required" &&
position.type !== "checkbox")
);
}
);
if (requiredWidgets && requiredWidgets?.length > 0) {
let checkSigned;
+11 -11
View File
@@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from "react";
import { useState, useRef, useEffect, useCallback } from "react";
import { PDFDocument } from "pdf-lib";
import "../styles/signature.css";
import Parse from "parse";
@@ -409,7 +409,7 @@ function PdfRequestFiles(
documentData?.[0]?.AuditTrail?.some(
(data) =>
data?.UserPtr?.objectId === currUserId &&
(data.Activity === "Signed"
(data.Activity === "Signed"
)
) || false;
if (checkAlreadySign) {
@@ -462,7 +462,7 @@ function PdfRequestFiles(
(data) => data.objectId === placeholder?.signerObjId
);
placeholdersOrSigners.push({
...getSignerData
...getSignerData,
});
} else {
placeholdersOrSigners.push(placeholder);
@@ -1137,11 +1137,11 @@ function PdfRequestFiles(
}
];
//function for get pdf page details
const pageDetails = async (pdf) => {
const pdfWHObj = await getOriginalWH(pdf);
setPdfOriginalWH(pdfWHObj);
setPdfLoad(true);
};
const pageDetails = async (pdf) => {
const pdfWHObj = await getOriginalWH(pdf);
setPdfOriginalWH(pdfWHObj);
setPdfLoad(true);
};
//function for change page
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
@@ -1817,7 +1817,7 @@ function PdfRequestFiles(
!alreadySign &&
!isCompleted?.isCertificate &&
!isDecline?.isDeclined &&
(
(
<AgreementSign
setIsAgree={setIsAgree}
showFirstWidget={showFirstWidget}
@@ -1841,7 +1841,7 @@ function PdfRequestFiles(
? "none"
: "auto"
}}
className={`${
className={`${
isGuestSignFlow
? "border-[0.5px] border-gray-300"
: "op-card"
@@ -2239,7 +2239,7 @@ function PdfRequestFiles(
{pdfDetails[0]?.AllowModifications &&
currentSigner &&
!alreadySign &&
(
(
<div data-tut="reactourFourth">
<WidgetComponent
pdfUrl={pdfUrl}
+6 -4
View File
@@ -1310,7 +1310,7 @@ function PlaceHolderSign() {
status,
defaultValue,
isHideLabel,
layout
layout,
) => {
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
const isPrefill = signerPos.some(
@@ -1355,7 +1355,7 @@ function PlaceHolderSign() {
isHideLabel: isHideLabel || false,
defaultValue: defaultValue,
fontSize: textSize || 12,
fontColor: textColor || "black"
fontColor: textColor || "black",
}
};
}
@@ -1395,7 +1395,7 @@ function PlaceHolderSign() {
isHideLabel: isHideLabel || false,
fontSize: textSize || 12,
fontColor: textColor || "black",
...(isPrefill ? { status } : {})
...(isPrefill ? { status } : {}),
}
};
}
@@ -1410,7 +1410,7 @@ function PlaceHolderSign() {
defaultValue: defaultValue,
fontSize: textSize || 12,
fontColor: textColor || "black",
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {}),
}
};
}
@@ -1851,6 +1851,8 @@ function PlaceHolderSign() {
Note: updateDocument?.[0]?.Note || "",
Description: updateDocument?.[0]?.Description || "",
SendinOrder: updateDocument?.[0]?.SendinOrder || false,
SendInOrderStrict:
updateDocument?.[0]?.SendInOrderStrict === true ? true : false,
AutomaticReminders: updateDocument?.[0]?.AutomaticReminders,
IsEnableOTP: updateDocument?.[0]?.IsEnableOTP === true ? true : false,
IsTourEnabled:
+2 -2
View File
@@ -1029,7 +1029,7 @@ function SignYourSelf() {
status,
defaultValue,
isHideLabel,
layout
layout,
) => {
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
const getPageNumer = xyPosition.filter(
@@ -1067,7 +1067,7 @@ function SignYourSelf() {
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor || currWidgetsDetails?.options?.fontColor || "black"
fontColor || currWidgetsDetails?.options?.fontColor || "black",
}
};
}
@@ -974,6 +974,7 @@ const TemplatePlaceholder = () => {
Note: pdfDetails[0]?.Note || "",
Description: pdfDetails[0]?.Description || "",
SendinOrder: pdfDetails[0]?.SendinOrder || false,
SendInOrderStrict: pdfDetails[0]?.SendInOrderStrict || false,
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
NextReminderDate: pdfDetails[0]?.NextReminderDate,
@@ -1388,6 +1389,8 @@ const TemplatePlaceholder = () => {
Note: updateTemplate?.[0]?.Note || "",
Description: updateTemplate?.[0]?.Description || "",
SendinOrder: updateTemplate?.[0]?.SendinOrder || false,
SendInOrderStrict:
updateTemplate?.[0]?.SendInOrderStrict === true ? true : false,
AutomaticReminders: updateTemplate?.[0]?.AutomaticReminders,
IsEnableOTP: updateTemplate?.[0]?.IsEnableOTP === true ? true : false,
IsTourEnabled:
@@ -1432,7 +1435,7 @@ const TemplatePlaceholder = () => {
status,
defaultValue,
isHideLabel,
layout
layout,
) => {
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
const isPrefill = signerPos.some(
@@ -1479,7 +1482,7 @@ const TemplatePlaceholder = () => {
isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false,
fontSize: textSize || 12,
fontColor: textColor || "black"
fontColor: textColor || "black",
}
};
}
@@ -1519,7 +1522,7 @@ const TemplatePlaceholder = () => {
isHideLabel: isHideLabel || false,
fontSize: textSize || 12,
fontColor: textColor || "black",
...(isPrefill ? { status } : {})
...(isPrefill ? { status } : {}),
}
};
}
@@ -1534,7 +1537,7 @@ const TemplatePlaceholder = () => {
defaultValue: defaultValue,
fontSize: textSize || 12,
fontColor: textColor || "black",
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {})
...(isReadOnly ? { isReadOnly: isReadOnly || false } : {}),
}
};
}
@@ -7,6 +7,59 @@ export default async function addUser(request) {
const currentUser = { __type: 'Pointer', className: '_User', objectId: request.user.id };
if (name && email && password && organization && team && role && tenantId) {
try {
// Derive the caller's tenant/organization/role from the server-side
// record rather than trusting client-supplied identifiers. This
// prevents an authenticated low-privileged user from creating an admin
// or assigning the new user to an arbitrary tenant/organization/team.
const callerQuery = new Parse.Query('contracts_Users');
callerQuery.equalTo('UserId', currentUser);
callerQuery.notEqualTo('IsDisabled', true);
const callerExtUser = await callerQuery.first({ useMasterKey: true });
if (!callerExtUser) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
const callerRole = callerExtUser.get('UserRole');
const isAdmin = callerRole === 'contracts_Admin';
const isOrgAdmin = callerRole === 'contracts_OrgAdmin';
if (!isAdmin && !isOrgAdmin) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const callerTenantId = callerExtUser.get('TenantId')?.id;
const callerOrgId = callerExtUser.get('OrganizationId')?.id;
// Enforce tenant-bound writes for all admins and require org scope for OrgAdmin callers.
if (!callerTenantId || tenantId !== callerTenantId || (isOrgAdmin && !callerOrgId)) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
// Only allow creating non-admin roles; never allow elevating to a
// tenant Admin through this endpoint.
const allowedRoles = ['OrgAdmin', 'Editor', 'User'];
if (!allowedRoles.includes(role)) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid role.');
}
// Resolve and authorize the target organization within the caller's tenant.
const targetOrgId = organization.objectId;
if (!targetOrgId) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all required fields.');
}
const orgQuery = new Parse.Query('contracts_Organizations');
const targetOrg = await orgQuery.get(targetOrgId, { useMasterKey: true });
if (targetOrg.get('TenantId')?.id !== callerTenantId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
// An OrgAdmin may only add users to their own organization.
if (isOrgAdmin && targetOrgId !== callerOrgId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
// Authorize the target team belongs to the target organization.
const teamQuery = new Parse.Query('contracts_Teams');
const targetTeam = await teamQuery.get(team, { useMasterKey: true });
if (targetTeam.get('OrganizationId')?.id !== targetOrgId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const extUser = new Parse.Object('contracts_Users');
extUser.set('Name', name);
if (phone) {
@@ -14,33 +67,27 @@ export default async function addUser(request) {
}
extUser.set('Email', email);
extUser.set('UserRole', `contracts_${role}`);
if (team) {
extUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: team,
},
]);
}
if (organization.objectId) {
extUser.set('OrganizationId', {
extUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: organization.objectId,
});
}
className: 'contracts_Teams',
objectId: team,
},
]);
extUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: targetOrgId,
});
if (organization.company) {
extUser.set('Company', organization.company);
}
if (tenantId) {
extUser.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
}
extUser.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: callerTenantId,
});
if (timezone) {
extUser.set('Timezone', timezone);
}
@@ -61,10 +108,10 @@ export default async function addUser(request) {
extUser.set('UserId', user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(request.user.id, true);
acl.setWriteAccess(request.user.id, true);
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
extUser.setACL(acl);
const extUserRes = await extUser.save();
@@ -82,10 +129,10 @@ export default async function addUser(request) {
extUser.set('CreatedBy', currentUser);
extUser.set('UserId', { __type: 'Pointer', className: '_User', objectId: userRes.id });
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(request.user.id, true);
acl.setWriteAccess(request.user.id, true);
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, true);
extUser.setACL(acl);
const res = await extUser.save();
@@ -116,13 +116,24 @@ async function sendMail(document, publicUrl) {
: senderEmail;
if (document.SendinOrder) {
signerMail = signerMail.slice();
signerMail.splice(1);
const getRole = signer => signer?.SignerRole || signer?.signer_role || signer?.role || 'signer';
const firstSignerIndex = signerMail.findIndex(signer => getRole(signer) === 'signer');
signerMail = signerMail.filter((signer, idx) => {
const role = getRole(signer);
return role === 'viewer' || idx === firstSignerIndex;
});
if (signerMail.length === 0 && document?.Placeholders?.length > 0) {
signerMail = document.Placeholders.filter(x => x?.Role !== 'prefill').slice(0, 1);
}
}
for (let i = 0; i < signerMail.length; i++) {
try {
let url = `${serverUrl}/functions/sendmailv3`;
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId };
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
};
const objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
let encodeBase64;
@@ -316,8 +327,8 @@ export default async function createBatchDocs(request) {
const Documents = JSON.parse(strDocuments);
const Ip = request?.headers?.['x-real-ip'] || '';
// Access the host from the headers
const publicUrl = request.headers.public_url;
// Use the browser origin so signing links keep the custom domain.
let publicUrl = request.headers.origin || request.headers.public_url;
const parseConfig = {
baseURL: serverUrl,
headers: {
@@ -29,7 +29,7 @@ export default async function createDocumentFromApp(request) {
docCls.set('URL', doc?.URL);
docCls.set('ExtUserPtr', doc.ExtUserPtr);
docCls.set('CreatedBy', doc.CreatedBy);
docCls.set('OriginIp', request?.headers?.['x-real-ip'] || '');
if (doc.Description) {
docCls.set('Description', doc.Description);
}
@@ -43,6 +43,8 @@ async function fetchDocumentsByName(
query.notEqualTo('IsArchive', true);
query.descending('updatedAt');
query.exclude('AuditTrail');
query.exclude('OriginalDocument');
query.exclude('SignedDocument');
query.notEqualTo('Type', 'Folder');
try {
return await query.find({ useMasterKey: true });
@@ -36,6 +36,8 @@ export default async function getDrive(request) {
query.skip(skip);
query.limit(limit);
query.exclude('AuditTrail');
query.exclude('OriginalDocument');
query.exclude('SignedDocument');
const res = await query.find({ useMasterKey: true });
return res;
} catch (err) {
@@ -9,6 +9,39 @@ export default async function getUserListByOrg(req) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
if (!OrganizationId) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide organizationId.');
}
// Authorize the requested organization against the caller's server-side
// tenant/organization. This prevents an authenticated user from
// enumerating users in an arbitrary organization or tenant.
const callerQuery = new Parse.Query('contracts_Users');
callerQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: req.user.id,
});
callerQuery.notEqualTo('IsDisabled', true);
const callerExtUser = await callerQuery.first({ useMasterKey: true });
if (!callerExtUser) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
const callerTenantId = callerExtUser.get('TenantId')?.id;
const callerOrgId = callerExtUser.get('OrganizationId')?.id;
const callerRole = callerExtUser.get('UserRole');
const isAdmin = callerRole === 'contracts_Admin' || callerRole === 'contracts_OrgAdmin';
const orgQuery = new Parse.Query('contracts_Organizations');
const targetOrg = await orgQuery.get(OrganizationId, { useMasterKey: true });
// Must belong to the caller's tenant.
if (!callerTenantId || targetOrg.get('TenantId')?.id !== callerTenantId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
// Non-admins may only list their own organization.
if (!isAdmin && OrganizationId !== callerOrgId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('OrganizationId', orgPtr);
extUser.include('TeamIds');
@@ -37,6 +37,18 @@ const getGmail = async (access_token, displayName) => {
return displayName;
}
};
export const encodeMimeAddress = address => {
const value = String(address || '');
const match = value.match(/^(.*?)\s*<([^<>]+)>$/);
const displayName = match ? match[1].trim() : value;
if (/^[\x00-\x7F]*$/.test(displayName)) return value;
const encodedName = Buffer.from(displayName, 'utf8').toString('base64');
return match ? `=?UTF-8?B?${encodedName}?= <${match[2]}>` : `=?UTF-8?B?${encodedName}?=`;
};
// Function to create a raw email message
const makeEmail = async (
to,
@@ -137,7 +149,7 @@ const makeEmail = async (
'Content-Type: multipart/mixed; boundary="' + boundary + '"\n',
'MIME-Version: 1.0\n',
`To: ${to}\n`,
`From: ${from}\n`,
`From: ${encodeMimeAddress(from)}\n`,
bccHeader,
ccHeader,
replyToHeader,
@@ -155,7 +167,7 @@ const makeEmail = async (
'Content-Type: multipart/mixed; boundary="' + boundary + '"\n',
'MIME-Version: 1.0\n',
`To: ${to}\n`,
`From: ${from}\n`,
`From: ${encodeMimeAddress(from)}\n`,
bccHeader,
ccHeader,
replyToHeader,
@@ -8,6 +8,30 @@ export default async function updateTenant(request) {
if (!request.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized');
}
// Derive the caller's tenant and role server-side. Never trust the
// client-supplied tenantId: an authenticated user may only update their own
// tenant and must hold an admin role to do so.
const callerQuery = new Parse.Query('contracts_Users');
callerQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
callerQuery.notEqualTo('IsDisabled', true);
const callerExtUser = await callerQuery.first({ useMasterKey: true });
if (!callerExtUser) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
const callerRole = callerExtUser.get('UserRole');
if (callerRole !== 'contracts_Admin' && callerRole !== 'contracts_OrgAdmin') {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const callerTenantId = callerExtUser.get('TenantId')?.id;
if (!callerTenantId || callerTenantId !== tenantId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const validKeys = ['CompletionBody', 'CompletionSubject', 'RequestBody', 'RequestSubject'];
try {
const tenant = new Parse.Object('partners_Tenant');
+2117 -2704
View File
File diff suppressed because it is too large Load Diff
+22 -9
View File
@@ -27,35 +27,35 @@
"@signpdf/placeholder-pdf-lib": "^3.3.0",
"@signpdf/signer-p12": "^3.3.0",
"@signpdf/signpdf": "^3.3.0",
"axios": "^1.15.2",
"axios": "^1.18.0",
"coherentpdf": "^2.5.5",
"cors": "^2.8.6",
"date-fns-tz": "^3.2.0",
"dotenv": "^17.4.2",
"express": "^5.2.1",
"form-data": "^4.0.5",
"form-data": "^4.0.6",
"generate-api-key": "^1.0.2",
"googleapis": "^171.4.0",
"libreoffice-convert": "^1.8.1",
"mailgun.js": "^13.0.0",
"moment": "^2.30.1",
"mongodb": "^7.2.0",
"multer": "^2.1.1",
"multer": "^2.2.0",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.3",
"nodemailer": "^8.0.7",
"nodemailer": "^9.0.1",
"p-limit": "^7.3.0",
"parse": "^8.1.0",
"parse": "^8.6.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^8.6.76",
"parse-server": "^8.6.84",
"parse-server-api-mail-adapter": "^5.0.5",
"pdf-lib": "^1.17.1",
"posthog-node": "^5.21.2",
"qrcode": "^1.5.4",
"rate-limiter-flexible": "^9.1.1",
"sharp": "^0.34.5",
"sharp": "^0.35.3",
"speakeasy": "^2.0.0",
"ws": "^8.20.0"
"ws": "^8.21.0"
},
"type": "module",
"devDependencies": {
@@ -68,10 +68,23 @@
"prettier": "^3.8.3"
},
"overrides": {
"brace-expansion": ">=2.1.2",
"ws": "$ws",
"parse": "$parse",
"form-data": "$form-data",
"@parse/push-adapter": "$@parse/push-adapter"
"@parse/push-adapter": "$@parse/push-adapter",
"@grpc/grpc-js": ">=1.14.4",
"fast-xml-builder": "1.2.0",
"protobufjs": ">=7.6.3",
"tmp": ">=0.2.7",
"decompress": "npm:@xhmikosr/decompress@^11.1.4",
"tar": "^7.5.19",
"websocket-driver": ">=0.7.5",
"js-yaml": "^3.15.0",
"lodash": "^4.18.0",
"undici": "^7.29.0",
"uuid": ">=11.1.1",
"body-parser": ">=2.3.0"
},
"engines": {
"node": "18 || 20 || 22"
+4 -3
View File
@@ -330,10 +330,11 @@
}
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},