From cbca22c308de4913eb465a5a4a5ed250e5a28852 Mon Sep 17 00:00:00 2001 From: nxglabs Date: Wed, 18 Jun 2025 14:03:05 +0000 Subject: [PATCH 1/3] Merge pull request #861 from nxglabs/staging fix: handle password protected pdf --- apps/OpenSign/docs/DARK_MODE_USAGE.md | 213 +++ .../examples/tailwind-usage-examples.js | 92 + .../examples/tailwind-usage-examples.jsx | 94 ++ apps/OpenSign/package-lock.json | 22 +- apps/OpenSign/package.json | 1 + .../public/locales/de/translation.json | 16 +- .../public/locales/en/translation.json | 22 +- .../public/locales/es/translation.json | 14 +- .../public/locales/fr/translation.json | 14 +- .../public/locales/hi/translation.json | 16 +- .../public/locales/it/translation.json | 16 +- .../static/js/assets/images/logo-dark.png | Bin 0 -> 97499 bytes apps/OpenSign/src/App.jsx | 2 +- apps/OpenSign/src/components/BulkSendUi.jsx | 2 +- apps/OpenSign/src/components/Header.jsx | 41 +- apps/OpenSign/src/components/ThemeToggle.jsx | 41 + .../components/dashboard/DashboardButton.jsx | 4 +- .../components/dashboard/DashboardReport.jsx | 82 +- .../src/components/pdf/AddRoleModal.jsx | 2 +- .../src/components/pdf/AgreementSign.jsx | 4 +- .../src/components/pdf/CellsSettingModal.jsx | 104 ++ .../src/components/pdf/CellsWidget.jsx | 139 ++ .../components/pdf/DropdownWidgetOption.jsx | 121 +- .../src/components/pdf/EditorToolbar.jsx | 13 +- .../src/components/pdf/EmailComponent.jsx | 17 +- .../src/components/pdf/PageReorderModal.jsx | 94 ++ .../OpenSign/src/components/pdf/PdfHeader.jsx | 77 +- apps/OpenSign/src/components/pdf/PdfZoom.jsx | 75 +- .../src/components/pdf/Placeholder.jsx | 205 ++- .../src/components/pdf/PlaceholderType.jsx | 223 +-- .../src/components/pdf/RecipientList.jsx | 2 +- .../src/components/pdf/RenderAllPdfPage.jsx | 44 +- .../OpenSign/src/components/pdf/RenderPdf.jsx | 653 +++----- .../src/components/pdf/SelectLanguage.jsx | 4 +- .../src/components/pdf/TextFontSetting.jsx | 2 +- .../src/components/pdf/WidgetComponent.jsx | 10 +- .../src/components/pdf/WidgetNameModal.jsx | 64 +- .../src/components/pdf/WidgetsValueModal.jsx | 464 +++-- .../components/shared/fields/SelectFolder.jsx | 2 +- .../shared/fields/SelectSigners.jsx | 2 +- .../components/shared/fields/SignersInput.jsx | 2 +- apps/OpenSign/src/constant/Utils.jsx | 575 +++++-- apps/OpenSign/src/constant/const.js | 5 + apps/OpenSign/src/hook/useElSize.ts | 38 + apps/OpenSign/src/hook/useScript.js | 3 +- apps/OpenSign/src/index.css | 160 ++ apps/OpenSign/src/index.jsx | 6 + apps/OpenSign/src/json/ReportJson.js | 8 + apps/OpenSign/src/pages/Form.jsx | 429 +++-- apps/OpenSign/src/pages/Managesign.jsx | 113 +- apps/OpenSign/src/pages/Opensigndrive.jsx | 26 +- apps/OpenSign/src/pages/PdfRequestFiles.jsx | 219 +-- apps/OpenSign/src/pages/PlaceHolderSign.jsx | 38 +- apps/OpenSign/src/pages/Preferences.jsx | 2 +- apps/OpenSign/src/pages/Report.jsx | 74 +- apps/OpenSign/src/pages/SignyourselfPdf.jsx | 134 +- .../src/pages/TemplatePlaceholder.jsx | 57 +- apps/OpenSign/src/pages/UserList.jsx | 2 +- apps/OpenSign/src/pages/UserProfile.jsx | 2 +- .../src/primitives/DownloadPdfZip.jsx | 2 +- .../src/primitives/GetReportDisplay.jsx | 168 +- .../src/primitives/PdfDeclineModal.jsx | 6 +- apps/OpenSign/src/styles/AddUser.css | 6 +- .../src/styles/dark-theme-improvements.css | 143 ++ apps/OpenSign/src/styles/managesign.css | 23 + apps/OpenSign/src/styles/opensigndrive.css | 14 + apps/OpenSign/src/styles/signature.css | 52 +- apps/OpenSign/tailwind.config.js | 101 +- apps/OpenSignServer/Dockerhubfile | 5 + .../cloud/customRoute/customApp.js | 5 +- .../cloud/customRoute/decryptpdf.js | 43 + .../cloud/customRoute/docxtopdf.js | 166 ++ .../cloud/parsefunction/createBatchDocs.js | 7 +- .../generateCertificatebydocId.js | 2 +- .../cloud/parsefunction/getReport.js | 31 +- .../cloud/parsefunction/recreateDocument.js | 5 +- .../cloud/parsefunction/reportsJson.js | 4 +- .../cloud/parsefunction/saveAsTemplate.js | 13 +- .../parsefunction/sendMailGmailProvider.js | 14 +- apps/OpenSignServer/package-lock.json | 1485 +++++++++-------- apps/OpenSignServer/package.json | 22 +- 81 files changed, 4912 insertions(+), 2311 deletions(-) create mode 100644 apps/OpenSign/docs/DARK_MODE_USAGE.md create mode 100644 apps/OpenSign/examples/tailwind-usage-examples.js create mode 100644 apps/OpenSign/examples/tailwind-usage-examples.jsx create mode 100644 apps/OpenSign/public/static/js/assets/images/logo-dark.png create mode 100644 apps/OpenSign/src/components/ThemeToggle.jsx create mode 100644 apps/OpenSign/src/components/pdf/CellsSettingModal.jsx create mode 100644 apps/OpenSign/src/components/pdf/CellsWidget.jsx create mode 100644 apps/OpenSign/src/components/pdf/PageReorderModal.jsx create mode 100644 apps/OpenSign/src/hook/useElSize.ts create mode 100644 apps/OpenSign/src/styles/dark-theme-improvements.css create mode 100644 apps/OpenSignServer/cloud/customRoute/decryptpdf.js create mode 100644 apps/OpenSignServer/cloud/customRoute/docxtopdf.js diff --git a/apps/OpenSign/docs/DARK_MODE_USAGE.md b/apps/OpenSign/docs/DARK_MODE_USAGE.md new file mode 100644 index 0000000000..0ebf8f7095 --- /dev/null +++ b/apps/OpenSign/docs/DARK_MODE_USAGE.md @@ -0,0 +1,213 @@ +# Tailwind Dark Mode Usage Guide + +## Overview +This guide shows how to use the new Tailwind utilities for better dark mode visibility in OpenSign. + +## Button Styling + +### VS Code-style Disabled Buttons +```jsx +// Option 1: Direct VS Code styling + + +// Option 2: Themed disabled styling + + +// Option 3: Conditional styling + +``` + +## Icon Styling + +### Theme-aware Icons +```jsx +// Better visibility in dark mode + + +// Muted but still visible + + +// Disabled state + +``` + +### CSS Variable Approach +```jsx +// Using CSS variables + + + +// Inline styles with CSS variables + +``` + +### Legacy JavaScript Function (Still Supported) +```jsx +// Existing approach - still works + +``` + +## Text Styling + +### Improved Gray Text +```jsx +// These automatically improve in dark mode +More visible in dark mode +Muted but readable +Clear text +``` + +## Complete Examples + +### Toolbar with Better Visibility +```jsx +const Toolbar = () => ( +
+ + + +
+); +``` + +### Form with Disabled States +```jsx +const Form = ({ isSubmitting }) => ( +
+ + +
+); +``` + +## React-Tour and Tooltip Dark Mode Support + +### React-Tour Modals +The react-tour modals now automatically support dark mode with VS Code-inspired styling: + +```jsx +// These components automatically get dark mode styling + +``` + +### ReactTooltip Components +All ReactTooltip instances now support dark mode: + +```jsx +// Automatically styled for dark mode + +
+

Tooltip content

+
+
+``` + +### HoverCard Balloon UI +The balloon tooltips in OpenSign Drive now properly support dark mode: + +```jsx +// These automatically get dark styling in dark mode + + + Document information + + +``` + +## Dark Mode Features Added + +### 1. **React-Tour Modal Styling** +- Background: `#1F2937` (VS Code modal background) +- Text: `#E5E7EB` (soft white for readability) +- Borders: `#374151` (subtle borders) +- Buttons: VS Code-style primary/secondary buttons + +### 2. **ReactTooltip Styling** +- Background: `#1F2937` with proper contrast +- Border: `#374151` for definition +- Box shadow: Enhanced for dark backgrounds +- Text: `#E5E7EB` for optimal readability + +### 3. **HoverCard Balloon UI** +- Background: `#1F2937` (matches VS Code) +- Text: `#E5E7EB` for readability +- Arrow: Automatically matches background color +- Enhanced shadows for dark backgrounds + +### 4. **React-Datepicker Support** +- Calendar background: `#1F2937` +- Selected dates: VS Code blue (`#007ACC`) +- Hover states: Proper contrast ratios +- Navigation arrows: Themed appropriately + +## CSS Classes Reference + +| Class | Purpose | Dark Mode Color | +|-------|---------|----------------| +| `icon-improved` | Better icon visibility | `#CCCCCC` | +| `icon-muted` | Muted but visible icons | `#999999` | +| `icon-disabled` | Disabled icon state | `#858585` | +| `op-btn-vscode-disabled` | VS Code disabled button | Background: `#3C3C3C` | +| `btn-themed-disabled` | Themed disabled button | Uses CSS variables | +| `icon-themed` | Variable-based icon color | `var(--icon-color)` | +| `.reactour__helper` | `#1F2937` background | +| `.react-tooltip` | `#1F2937` background | +| `.HoverCardContent` | `#1F2937` background | +| `.react-datepicker` | `#1F2937` background | + +## Migration Guide + +### From JavaScript Function to Tailwind +```jsx +// Before + + +// After + +``` + +### From Hardcoded Colors to Theme-aware +```jsx +// Before + + +// After (automatic improvement) + +// OR explicitly + +``` + +## Migration Notes + +All existing tooltip and tour components will automatically inherit the new dark mode styling when the theme is set to `opensigndark`. No code changes required for existing implementations. diff --git a/apps/OpenSign/examples/tailwind-usage-examples.js b/apps/OpenSign/examples/tailwind-usage-examples.js new file mode 100644 index 0000000000..97ac61ae64 --- /dev/null +++ b/apps/OpenSign/examples/tailwind-usage-examples.js @@ -0,0 +1,92 @@ +/** + * Tailwind Dark Mode Usage Examples for OpenSign + * + * This file demonstrates how to use the new Tailwind utilities + * for better dark mode visibility of buttons and icons. + */ + +// Example 1: VS Code-style disabled buttons +const DisabledButtonExamples = () => { + return ( +
+ {/* Option A: Using the VS Code disabled style */} + + + {/* Option B: Using themed disabled style */} + + + {/* Option C: Conditional styling */} + +
+ ); +}; + +// Example 2: Icon visibility improvements +const IconExamples = () => { + return ( +
+ {/* Theme-aware icons with better visibility */} + + + + + {/* Using CSS variables */} + + + + {/* Gray text that automatically improves in dark mode */} + + This text is now more visible in dark mode + + Muted but still readable +
+ ); +}; + +// Example 3: Using CSS variables in inline styles +const InlineStyleExamples = () => { + return ( +
+ {/* Using CSS variables directly */} + + + {/* Using the existing JavaScript function */} + +
+ ); +}; + +// Example 4: Toolbar with improved icons +const ToolbarExample = () => { + return ( +
+ + + +
+ ); +}; + +export { + DisabledButtonExamples, + IconExamples, + InlineStyleExamples, + ToolbarExample +}; diff --git a/apps/OpenSign/examples/tailwind-usage-examples.jsx b/apps/OpenSign/examples/tailwind-usage-examples.jsx new file mode 100644 index 0000000000..406a71985e --- /dev/null +++ b/apps/OpenSign/examples/tailwind-usage-examples.jsx @@ -0,0 +1,94 @@ +/** + * Tailwind Dark Mode Usage Examples for OpenSign + * + * This file demonstrates how to use the new Tailwind utilities + * for better dark mode visibility of buttons and icons. + */ + +// Example 1: VS Code-style disabled buttons +const DisabledButtonExamples = () => { + return ( +
+ {/* Option A: Using the VS Code disabled style */} + + + {/* Option B: Using themed disabled style */} + + + {/* Option C: Conditional styling */} + +
+ ); +}; + +// Example 2: Icon visibility improvements +const IconExamples = () => { + return ( +
+ {/* Theme-aware icons with better visibility */} + + + + + {/* Using CSS variables */} + + + + {/* Gray text that automatically improves in dark mode */} + This text is now more visible in dark mode + Muted but still readable +
+ ); +}; + +// Example 3: Using CSS variables in inline styles +const InlineStyleExamples = () => { + return ( +
+ {/* Using CSS variables directly */} + + + {/* Using the existing JavaScript function */} + +
+ ); +}; + +// Example 4: Toolbar with improved icons +const ToolbarExample = () => { + return ( +
+ + + +
+ ); +}; + +export { + DisabledButtonExamples, + IconExamples, + InlineStyleExamples, + ToolbarExample +}; diff --git a/apps/OpenSign/package-lock.json b/apps/OpenSign/package-lock.json index 0970efa566..3d4d02e6a0 100644 --- a/apps/OpenSign/package-lock.json +++ b/apps/OpenSign/package-lock.json @@ -28,6 +28,7 @@ "pkijs": "^3.0.8", "print-js": "^1.6.0", "prismjs": "^1.30.0", + "quill-html-edit-button": "^3.0.0", "radix-ui": "^1.4.2", "react": "^18.3.1", "react-bootstrap": "^2.10.10", @@ -5995,9 +5996,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -12462,6 +12463,15 @@ "node": ">= 12.0.0" } }, + "node_modules/quill-html-edit-button": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quill-html-edit-button/-/quill-html-edit-button-3.0.0.tgz", + "integrity": "sha512-ZUV3rYjEvXz+v/7f8N0Mkp2+0D8VGDj6yCYPfagSLmyshczeMawS7vZJzaJ18dJp35IfjiPnzqyVk3KCI8qN1w==", + "license": "MIT", + "peerDependencies": { + "quill": "^2.x" + } + }, "node_modules/radix-ui": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.4.2.tgz", @@ -15976,9 +15986,9 @@ } }, "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index e85d12a20c..bfd9af1e0d 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -23,6 +23,7 @@ "pkijs": "^3.0.8", "print-js": "^1.6.0", "prismjs": "^1.30.0", + "quill-html-edit-button": "^3.0.0", "radix-ui": "^1.4.2", "react": "^18.3.1", "react-bootstrap": "^2.10.10", diff --git a/apps/OpenSign/public/locales/de/translation.json b/apps/OpenSign/public/locales/de/translation.json index 32b2b682b0..6b3b1b3875 100644 --- a/apps/OpenSign/public/locales/de/translation.json +++ b/apps/OpenSign/public/locales/de/translation.json @@ -5,6 +5,7 @@ "create-account": "Konto erstellen", "login": "Anmelden", "language": "Sprache", + "dark-mode": "Dunkelmodus", "name": "Name", "phone": "Telefon", "phone-optional": "optional", @@ -361,6 +362,7 @@ "date": "Datum", "text": "Text", "text input": "Texteingabe", + "cells": "Zellen", "checkbox": "Checkbox", "dropdown": "Dropdown", "radio button": "Radiobutton", @@ -415,10 +417,12 @@ "options": "Optionen", "minimun-check": "Minimale Anzahl", "maximum-check": "Maximale Anzahl", + "cell-count": "Zellzahl", "default-value": "Standardwert", "select": "Auswählen", - "read-only": "Nur lesen", + "read-only": "Ist schreibgeschützt", "hide-labels": "Labels ausblenden", + "layout": "Layout", "checkbox": "Checkbox", "alert": "Warnung", "zoom-in": "Vergrößern", @@ -750,6 +754,7 @@ "delete-page": "Seite löschen", "merge-pdf": "PDFs zusammenführen", "add-pages": "Seiten hinzufügen", + "reorder-pages": "Seiten neu anordnen", "delete-alert": "Einzelne Seite kann nicht gelöscht werden.", "delete-alert-2": "Sind Sie sicher, dass Sie diese Seite löschen möchten?", "delete-note": "Hinweis: Sobald Sie diese Seite löschen, kann dies nicht rückgängig gemacht werden.", @@ -982,6 +987,7 @@ "review": "Überprüfen", "next-field": "Nächstes Feld", "required-mssg": "{{leftRequiredWidget}} von {{totalWidget}} Feldern übrig", + "verify-document": "Dokument verifizieren", "verify-document-signature": "Dokumentensignatur überprüfen", "select-pdf-document": "PDF-Dokument auswählen", "selected-file": "Ausgewählte Datei", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "Signaturinformationen konnten nicht analysiert werden", "not-calculated": "Nicht berechnet", "not-found-in-signature": "Nicht in Signatur gefunden", - "readonly-textinput-error": "Schreibgeschütztes Text-Widget muss einen Standardwert haben oder optional sein.", - "readonly-dropdown-error": "Schreibgeschütztes Dropdown-Widget muss einen Standardwert haben oder optional sein.", - "readonly-radiobtn-error": "Schreibgeschütztes Optionsfeld-Widget muss einen Standardwert haben oder optional sein." + "readonly-error": "Das schreibgeschützte {{widgetName}}-Widget muss einen Standardwert haben oder kann optional gemacht werden.", + "choose-one":"Wählen Sie eine aus", + "search-templates": "Vorlagen durchsuchen…", + "search-documents": "Dokumente suchen…", + "search-contacts": "Kontakte durchsuchen…" } diff --git a/apps/OpenSign/public/locales/en/translation.json b/apps/OpenSign/public/locales/en/translation.json index 39d3774054..6bc3784150 100644 --- a/apps/OpenSign/public/locales/en/translation.json +++ b/apps/OpenSign/public/locales/en/translation.json @@ -5,6 +5,7 @@ "create-account": "Create account", "login": "Login", "language": "Language", + "dark-mode": "Dark mode", "name": "Name", "phone": "Phone", "phone-optional": "optional", @@ -199,7 +200,7 @@ }, "file-type": "pdf, png, jpg, jpeg", "docx": "docx", - "file-selected": "file selected", + "file-selected": "file(s) selected", "template-title": "Template title", "document-title": "Document title", "description": "Description", @@ -361,6 +362,7 @@ "date": "date", "text": "text", "text input": "text input", + "cells": "cells", "checkbox": "checkbox", "dropdown": "dropdown", "radio button": "radio button", @@ -415,10 +417,12 @@ "options": "Options", "minimun-check": "Minimun check", "maximum-check": "Maximum check", + "cell-count": "Cell count", "default-value": "Default value", "select": "Select", - "read-only": "Is read only", + "read-only": "read only", "hide-labels": "Hide labels", + "layout": "Layout", "checkbox": "Checkbox", "alert": "Alert", "zoom-in": "Zoom in", @@ -526,7 +530,7 @@ "new-password": "New password", "confirm-password": "Confirm password", "file-alert-1": "The selected file size is too large. Please select a file less than", - "file-alert-2": "Please select file.", + "file-alert-2": "Please select file(s).", "file-alert-3": "Please wait while the document is being uploaded.", "enter-pdf-password": "Enter Pdf password", "correct-password": "Please provide correct password", @@ -750,6 +754,7 @@ "delete-page": "Delete page", "merge-pdf": "Merge pdf", "add-pages": "Add pages", + "reorder-pages": "Reorder pages", "delete-alert": "Can not delete single page", "delete-alert-2": "Are you sure you want to delete this page?", "delete-note": "Note: Once you delete this page, you cannot undo.", @@ -885,7 +890,7 @@ "you-will-receive-email-shortly": "✅ That's it! You'll receive a confirmation email shortly.", "please-provide-templateid": "Please provide templateid", "this-template-is-not-public": "This template is not public", - "invalid-templateid": "Invaldi templateid", + "invalid-templateid": "Invalid templateid", "contact-billing-at-opensign": "To add more seats, please contact OpenSign™ at <1>billing@opensignlabs.com for assistance", "title-length-alert": "Title must be at most 250 characters long.", "note-length-alert": "Note must be at most 200 characters long.", @@ -982,6 +987,7 @@ "review":"Review", "next-field":"Next Field", "required-mssg":"{{leftRequiredWidget}} of {{totalWidget}} fields left", + "verify-document": "Verify document", "verify-document-signature": "Verify Document Signature", "select-pdf-document": "Select PDF Document", "selected-file": "Selected file", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "Could not parse signer info", "not-calculated": "Not calculated", "not-found-in-signature": "Not found in signature", - "readonly-textinput-error": "Read-only text widget must have a default value or you can make it optional.", - "readonly-dropdown-error": "Read-only dropdown widget must have a default value or you can make it optional.", - "readonly-radiobtn-error": "Read-only radio button widget must have a default value or you can make it optional." + "readonly-error": "Read-only {{widgetName}} widget must have a default value or you can make it optional.", + "choose-one":"Choose One", + "search-templates": "Search templates…", + "search-documents": "Search documents…", + "search-contacts": "Search contacts…" } \ No newline at end of file diff --git a/apps/OpenSign/public/locales/es/translation.json b/apps/OpenSign/public/locales/es/translation.json index 551e3104d4..f4825aab35 100644 --- a/apps/OpenSign/public/locales/es/translation.json +++ b/apps/OpenSign/public/locales/es/translation.json @@ -5,6 +5,7 @@ "create-account": "Crear cuenta", "login": "Iniciar sesión", "language": "Idioma", + "dark-mode": "Modo oscuro", "name": "Nombre", "phone": "Teléfono", "phone-optional": "opcional", @@ -362,6 +363,7 @@ "date": "fecha", "text": "texto", "text input": "entrada de texto", + "cells": "células", "checkbox": "casilla", "dropdown": "desplegable", "radio button": "botón de radio", @@ -416,10 +418,12 @@ "options": "Opciones", "minimun-check": "Chequeo mínimo", "maximum-check": "Chequeo máximo", + "cell-count": "recuento de células", "default-value": "Valor por defecto", "select": "Seleccionar", "read-only": "Es de solo lectura", "hide-labels": "Esconder etiquetas", + "layout": "Diseño", "checkbox": "Casilla", "alert": "Alerta", "zoom-in": "Acercar", @@ -750,6 +754,7 @@ "delete-page": "eliminar página", "merge-pdf": "fusionar pdf", "add-pages": "Agregar páginas", + "reorder-pages": "Reordenar páginas", "delete-alert": "No se puede eliminar una sola página", "delete-alert-2": "¿Está seguro de que desea eliminar esta página?", "delete-note": "Nota: una vez que elimines esta página, no podrás deshacerla", @@ -982,6 +987,7 @@ "review": "Revisar", "next-field": "Siguiente campo", "required-mssg": "{{leftRequiredWidget}} de {{totalWidget}} campos restantes", + "verify-document": "Verificar documento", "verify-document-signature": "Verificar firma del documento", "select-pdf-document": "Seleccionar documento PDF", "selected-file": "Archivo seleccionado", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "No se pudo analizar la información del firmante", "not-calculated": "No calculado", "not-found-in-signature": "No encontrado en la firma", - "readonly-textinput-error": "El widget de texto de solo lectura debe tener un valor predeterminado o puede hacerse opcional.", - "readonly-dropdown-error": "El widget desplegable de solo lectura debe tener un valor predeterminado o puede hacerse opcional.", - "readonly-radiobtn-error": "El widget de botón de opción de solo lectura debe tener un valor predeterminado o puede hacerse opcional." + "readonly-error": "El widget de solo lectura {{widgetName}} debe tener un valor predeterminado o puede hacerlo opcional.", + "choose-one":"Elige uno", + "search-templates": "Buscar plantillas…", + "search-documents": "Buscar documentos…", + "search-contacts": "Buscar contactos…" } diff --git a/apps/OpenSign/public/locales/fr/translation.json b/apps/OpenSign/public/locales/fr/translation.json index b2d558179c..3880369a6b 100644 --- a/apps/OpenSign/public/locales/fr/translation.json +++ b/apps/OpenSign/public/locales/fr/translation.json @@ -5,6 +5,7 @@ "create-account": "Créer un compte", "login": "Se Connecter", "language": "Langue", + "dark-mode": "Mode sombre", "name": "Nom et Prénom", "phone": "Téléphone", "phone-optional": "(facultatif)", @@ -361,6 +362,7 @@ "date": "date", "text": "texte", "text input": "saisie de texte", + "cells": "cellules", "checkbox": "case à cocher", "dropdown": "dérouler", "radio button": "bouton radio", @@ -415,10 +417,12 @@ "options": "Possibilités", "minimun-check": "Vérification minimale", "maximum-check": "Contrôle maximum", + "cell-count": "numération cellulaire", "default-value": "Valeur par défaut", "select": "Sélectionner", "read-only": "Est en lecture seule", "hide-labels": "Masquer les étiquettes", + "layout": "Disposition", "checkbox": "Case à cocher", "alert": "Alerte", "zoom-in": "Agrandir", @@ -750,6 +754,7 @@ "delete-page": "supprimer la page", "merge-pdf": "Fusionner le pdf", "add-pages": "Ajouter des pages", + "reorder-pages": "Réorganiser les pages", "delete-alert": "Impossible de supprimer une seule page", "delete-alert-2": "Etes-vous sûr de vouloir supprimer cette page ?", "delete-note": "Remarque : une fois cette page supprimée, vous ne pouvez plus l'annuler", @@ -982,6 +987,7 @@ "review": "Revoir", "next-field": "Champ suivant", "required-mssg":"{{leftRequiredWidget}} champs sur {{totalWidget}} restants", + "verify-document": "Vérifier le document", "verify-document-signature": "Vérifier la signature du document", "select-pdf-document": "Sélectionner le document PDF", "selected-file": "Fichier sélectionné", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "Impossible d'analyser les informations sur le signataire", "not-calculated": "Non calculé", "not-found-in-signature": "Introuvable dans la signature", - "readonly-textinput-error": "Le widget de texte en lecture seule doit avoir une valeur par défaut ou être rendu optionnel.", - "readonly-dropdown-error": "Le widget déroulant en lecture seule doit avoir une valeur par défaut ou être rendu optionnel.", - "readonly-radiobtn-error": "Le widget bouton radio en lecture seule doit avoir une valeur par défaut ou être rendu optionnel." + "readonly-error": "Le widget en lecture seule {{widgetName}} doit avoir une valeur par défaut ou vous pouvez le rendre facultatif.", + "choose-one":"Choisissez-en un", + "search-templates": "Rechercher des modèles…", + "search-documents": "Rechercher des documents…", + "search-contacts": "Rechercher des contacts…" } diff --git a/apps/OpenSign/public/locales/hi/translation.json b/apps/OpenSign/public/locales/hi/translation.json index aefb718d3e..cd399fbf48 100644 --- a/apps/OpenSign/public/locales/hi/translation.json +++ b/apps/OpenSign/public/locales/hi/translation.json @@ -5,6 +5,7 @@ "create-account": "खाता बनाएं", "login": "लॉग इन करें", "language": "भाषा", + "dark-mode": "डार्क मोड", "name": "नाम", "phone": "फ़ोन", "phone-optional": "वैकल्पिक", @@ -361,6 +362,7 @@ "date": "दिनांक", "text": "पाठ", "text input": "पाठ इनपुट", + "cells": "सेल्स", "checkbox": "चेकबॉक्स", "dropdown": "ड्रॉपडाउन", "radio button": "रेडियो बटन", @@ -415,10 +417,12 @@ "options": "विकल्प", "minimun-check": "न्यूनतम जाँच", "maximum-check": "अधिकतम जाँच", + "cell-count": "सेल्स संख्या", "default-value": "डिफ़ॉल्ट मान", "select": "चुनें", - "read-only": "केवल पढ़ने के लिए है", + "read-only": "सिर्फ पढ़ने के लिए है", "hide-labels": "लेबल छिपाएँ", + "layout": "लेआउट", "checkbox": "चेकबॉक्स", "alert": "चेतावनी", "zoom-in": "ज़ूम इन करें", @@ -750,6 +754,7 @@ "delete-page": "पृष्ठ हटाएं", "merge-pdf": "पीडीएफ मर्ज करें", "add-pages": "पृष्ठ जोड़ें", + "reorder-pages": "पृष्ठ पुनः व्यवस्थित करें", "delete-alert": "एकल पृष्ठ नहीं हटाया जा सकता", "delete-alert-2": "क्या आप वाकई इस पृष्ठ को हटाना चाहते हैं?", "delete-note": "ध्यान दें: एक बार जब आप इस पृष्ठ को हटा देते हैं, तो आप इसे पूर्ववत नहीं कर सकते।", @@ -982,6 +987,7 @@ "review": "समीक्षा", "next-field": "अगला फ़ील्ड", "required-mssg": "{{totalWidget}} में से {{leftRequiredWidget}} फ़ील्ड शेष हैं", + "verify-document": "दस्तावेज़ सत्यापित करें", "verify-document-signature": "दस्तावेज़ हस्ताक्षर सत्यापित करें", "select-pdf-document": "पीडीएफ दस्तावेज़ चुनें", "selected-file": "चयनित फ़ाइल", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "हस्ताक्षरकर्ता जानकारी पार्स नहीं की जा सकी", "not-calculated": "गणना नहीं की गई", "not-found-in-signature": "हस्ताक्षर में नहीं मिला", - "readonly-textinput-error": "रीड-ओनली टेक्स्ट विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", - "readonly-dropdown-error": "रीड-ओनली ड्रॉपडाउन विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", - "readonly-radiobtn-error": "रीड-ओनली रेडियो बटन विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।" + "readonly-error": "रीड-ओनली {{widgetName}} विजेट में एक डिफ़ॉल्ट मान होना चाहिए या आप इसे वैकल्पिक बना सकते हैं।", + "choose-one":"एक का चयन", + "search-templates": "टेम्पलेट खोजें…", + "search-documents": "दस्तावेज़ खोजें…", + "search-contacts": "संपर्क खोजें…" } diff --git a/apps/OpenSign/public/locales/it/translation.json b/apps/OpenSign/public/locales/it/translation.json index b164e80fee..f2b52e6676 100644 --- a/apps/OpenSign/public/locales/it/translation.json +++ b/apps/OpenSign/public/locales/it/translation.json @@ -5,6 +5,7 @@ "create-account": "Crea Account", "login": "Accedi", "language": "Lingua", + "dark-mode": "Modalità scura", "name": "Nome", "phone": "Telefono", "phone-optional": "facoltativo", @@ -361,6 +362,7 @@ "date": "data", "text": "testo", "text input": "campo di testo", + "cells": "cellule", "checkbox": "casella di controllo", "dropdown": "menu a tendina", "radio button": "pulsante di opzione", @@ -415,10 +417,12 @@ "options": "Opzioni", "minimun-check": "Controllo minimo", "maximum-check": "Controllo massimo", + "cell-count": "conteggio delle cellule", "default-value": "Valore predefinita", "select": "Seleziona", - "read-only": "È solo lettura", + "read-only": "È di sola lettura", "hide-labels": "Nascondi etichette", + "layout": "Layout", "checkbox": "Casella di controllo", "alert": "Avviso", "zoom-in": "Ingrandisci", @@ -750,6 +754,7 @@ "delete-page": "Elimina pagina", "merge-pdf": "Unisci PDF", "add-pages": "Aggiungi pagine", + "reorder-pages": "Riordina pagine", "delete-alert": "Non è possibile eliminare una singola pagina", "delete-alert-2": "Sei sicuro di voler eliminare questa pagina?", "delete-note": "Nota: Una volta eliminata questa pagina, non potrai annullare l'operazione.", @@ -982,6 +987,7 @@ "review": "Rivedere", "next-field": "Campo successivo", "required-mssg":"{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti", + "verify-document": "Verifica documento", "verify-document-signature": "Verifica firma documento", "select-pdf-document": "Seleziona documento PDF", "selected-file": "File selezionato", @@ -1023,7 +1029,9 @@ "could-not-parse-signer-info": "Impossibile analizzare le informazioni del firmatario", "not-calculated": "Non calcolato", "not-found-in-signature": "Non trovato nella firma", - "readonly-textinput-error": "Il widget di testo di sola lettura deve avere un valore predefinito oppure può essere reso opzionale.", - "readonly-dropdown-error": "Il widget a discesa di sola lettura deve avere un valore predefinito oppure può essere reso opzionale.", - "readonly-radiobtn-error": "Il widget pulsante di opzione di sola lettura deve avere un valore predefinito oppure può essere reso opzionale." + "readonly-error": "Il widget {{widgetName}} di sola lettura deve avere un valore predefinito oppure può essere reso facoltativo.", + "choose-one":"Scegline uno", + "search-templates": "Cerca modelli…", + "search-documents": "Cerca documenti…", + "search-contacts": "Cerca contatti…" } diff --git a/apps/OpenSign/public/static/js/assets/images/logo-dark.png b/apps/OpenSign/public/static/js/assets/images/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..33a512e4c207e0b51235c3139f19d44c1ef78291 GIT binary patch literal 97499 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 literal 0 HcmV?d00001 diff --git a/apps/OpenSign/src/App.jsx b/apps/OpenSign/src/App.jsx index 5a69c1da8b..342964fde2 100644 --- a/apps/OpenSign/src/App.jsx +++ b/apps/OpenSign/src/App.jsx @@ -142,7 +142,7 @@ function App() { /> {/* signyouself route with no rowlevel data using docId from url */} } /> - {/* draft document route to handle and navigate route page accordiing to document status */} + {/* draft document route to handle and navigate route page according to document status */} } /> {/* recipient placeholder set route with no rowlevel data using docId from url*/} { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - //function to check atleast one signature field exist + //function to check at least one signature field exist const signatureExist = async () => { setIsDisableBulkSend(false); const getPlaceholder = props?.Placeholders; diff --git a/apps/OpenSign/src/components/Header.jsx b/apps/OpenSign/src/components/Header.jsx index 595b9c8550..7fee264bea 100644 --- a/apps/OpenSign/src/components/Header.jsx +++ b/apps/OpenSign/src/components/Header.jsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "react"; import dp from "../assets/images/dp.png"; import FullScreenButton from "./FullScreenButton"; +import ThemeToggle from "./ThemeToggle"; import { useNavigate } from "react-router"; import Parse from "parse"; import { useWindowSize } from "../hook/useWindowSize"; @@ -85,6 +86,34 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => { }; }, [isOpen]); + + useEffect(() => { + const updateLogoForTheme = () => { + const isDarkMode = + document.documentElement.getAttribute("data-theme") === "opensigndark"; + const logo = isDarkMode + ? "/static/js/assets/images/logo-dark.png" // Path to the dark mode logo + : appInfo.applogo; // Use current logo for light mode + if (applogo !== logo) { + setAppLogo(logo); + } + }; + + // Set the logo immediately based on the current theme + updateLogoForTheme(); + + const observer = new MutationObserver(() => { + updateLogoForTheme(); + }); + + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"] + }); + + return () => observer.disconnect(); + }, [applogo]); + return (
@@ -147,7 +176,7 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => {
    @@ -194,6 +223,16 @@ const Header = ({ showSidebar, setIsMenu, isConsole }) => { {t("verify-document")} +
  • + + + {t("dark-mode")} + + BETA + + + +
  • )}
  • diff --git a/apps/OpenSign/src/components/ThemeToggle.jsx b/apps/OpenSign/src/components/ThemeToggle.jsx new file mode 100644 index 0000000000..4cb7207bcc --- /dev/null +++ b/apps/OpenSign/src/components/ThemeToggle.jsx @@ -0,0 +1,41 @@ +import { useEffect, useState } from "react"; + +const ThemeToggle = () => { + const [isDark, setIsDark] = useState(false); + + useEffect(() => { + const storedTheme = localStorage.getItem("theme"); + if (storedTheme === "dark") { + setIsDark(true); + document.documentElement.setAttribute("data-theme", "opensigndark"); + } else { + document.documentElement.setAttribute("data-theme", "opensigncss"); + } + }, []); + + const handleChange = () => { + const newTheme = !isDark; + setIsDark(newTheme); + if (newTheme) { + document.documentElement.setAttribute("data-theme", "opensigndark"); + localStorage.setItem("theme", "dark"); + } else { + document.documentElement.setAttribute("data-theme", "opensigncss"); + localStorage.setItem("theme", "light"); + } + }; + + return ( + <> + + + ); +}; + +export default ThemeToggle; diff --git a/apps/OpenSign/src/components/dashboard/DashboardButton.jsx b/apps/OpenSign/src/components/dashboard/DashboardButton.jsx index e1c7e3f8c4..b19cbe8f6e 100644 --- a/apps/OpenSign/src/components/dashboard/DashboardButton.jsx +++ b/apps/OpenSign/src/components/dashboard/DashboardButton.jsx @@ -29,7 +29,7 @@ const DashboardButton = (props) => { : "cursor-default" } w-full shadow-md px-3 py-2 op-card bg-base-100`} > -
    +
    { >
    -
    +
    {t(`sidebar.${props.Label}`)} {props.Label === "Sign yourself" && (
    diff --git a/apps/OpenSign/src/components/dashboard/DashboardReport.jsx b/apps/OpenSign/src/components/dashboard/DashboardReport.jsx index b6706ebb51..25f6f0263d 100644 --- a/apps/OpenSign/src/components/dashboard/DashboardReport.jsx +++ b/apps/OpenSign/src/components/dashboard/DashboardReport.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import Parse from "parse"; import ReportTable from "../../primitives/GetReportDisplay"; import reportJson from "../../json/ReportJson"; @@ -17,10 +17,16 @@ function DashboardReport(props) { const [isMoreDocs, setIsMoreDocs] = useState(true); const abortController = new AbortController(); const docPerPage = 5; + const [searchTerm, setSearchTerm] = useState(""); + const [mobileSearchOpen, setMobileSearchOpen] = useState(false); + const [isSearchResult, setIsSearchResult] = useState(false); + const debounceTimer = useRef(null); useEffect(() => { setReportName(""); - getReportData(props.Record.reportId); + setSearchTerm(""); + setMobileSearchOpen(false); + getReportData(props.Record.reportId, 0, 20, ""); // Function returned from useEffect is called on unmount return () => { @@ -36,12 +42,69 @@ function DashboardReport(props) { // below useEffect call when isNextRecord state is true and fetch next record useEffect(() => { if (isNextRecord) { - getReportData(props.Record.reportId, List.length, 20); + getReportData(props.Record.reportId, List.length, 20, searchTerm); } // eslint-disable-next-line }, [isNextRecord]); - const getReportData = async (id, skipUserRecord = 0, limit = 20) => { + const handleSearchChange = async (e) => { + const term = e.target.value.toLowerCase(); + setSearchTerm(term); + if (debounceTimer.current) { + clearTimeout(debounceTimer.current); + } + debounceTimer.current = setTimeout(async () => { + try { + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + sessiontoken: localStorage.getItem("accesstoken") + }; + const url = `${localStorage.getItem("baseUrl")}functions/getReport`; + const res = await axios.post( + url, + { + reportId: props.Record.reportId, + searchTerm: term, + skip: 0, + limit: docPerPage + }, + { headers } + ); + const data = res.data?.result || []; + if (!data.error) { + setList(data); + setIsMoreDocs(data.length >= docPerPage); + setIsNextRecord(false); + setIsSearchResult(true); + } + } catch (err) { + console.error("Search error:", err); + } + }, 300); + setIsSearchResult(false); + }; + + const handleSearchPaste = (e) => { + setTimeout(() => { + handleSearchChange({ target: { value: e.target.value } }); + }, 0); + }; + + useEffect(() => { + return () => { + if (debounceTimer.current) { + clearTimeout(debounceTimer.current); + } + }; + }, []); + + const getReportData = async ( + id, + skipUserRecord = 0, + limit = 20, + term = searchTerm + ) => { setIsLoader(true); const json = reportJson(id); if (json) { @@ -59,6 +122,9 @@ function DashboardReport(props) { const skipRecord = id === "5Go51Q7T8r" ? 0 : skipUserRecord; const limitRecord = id === "5Go51Q7T8r" ? 200 : limit; const params = { reportId: id, skip: skipRecord, limit: limitRecord }; + if (term) { + params.searchTerm = term; + } const url = `${localStorage.getItem("baseUrl")}functions/getReport`; const res = await axios.post(url, params, { headers: headers, @@ -141,11 +207,17 @@ function DashboardReport(props) { setIsNextRecord={setIsNextRecord} isMoreDocs={isMoreDocs} docPerPage={docPerPage} + mobileSearchOpen={mobileSearchOpen} + setMobileSearchOpen={setMobileSearchOpen} + searchTerm={searchTerm} + handleSearchChange={handleSearchChange} + handleSearchPaste={handleSearchPaste} + isSearchResult={isSearchResult} /> ) : (
    -

    {t("report-not-found")}

    +

    {t("report-not-found")}

    )} diff --git a/apps/OpenSign/src/components/pdf/AddRoleModal.jsx b/apps/OpenSign/src/components/pdf/AddRoleModal.jsx index 6696306dd7..f052a74a39 100644 --- a/apps/OpenSign/src/components/pdf/AddRoleModal.jsx +++ b/apps/OpenSign/src/components/pdf/AddRoleModal.jsx @@ -10,7 +10,7 @@ const AddRoleModal = (props) => { isOpen={props.isModalRole} handleClose={props.handleCloseRoleModal} > -
    +
    -
    +
    {t("agree-p1")}
    -
    +
    {t("agreement-note")}
    diff --git a/apps/OpenSign/src/components/pdf/CellsSettingModal.jsx b/apps/OpenSign/src/components/pdf/CellsSettingModal.jsx new file mode 100644 index 0000000000..50a3e59b56 --- /dev/null +++ b/apps/OpenSign/src/components/pdf/CellsSettingModal.jsx @@ -0,0 +1,104 @@ +import React, { useState, useEffect } from "react"; +import ModalUi from "../../primitives/ModalUi"; +import { fontsizeArr, fontColorArr } from "../../constant/Utils"; +import { useTranslation } from "react-i18next"; + +export default function CellsSettingModal({ + isOpen, + handleClose, + defaultData, + handleSave +}) { + const { t } = useTranslation(); + const [name, setName] = useState(""); + const [cellCount, setCellCount] = useState(5); + const [fontSize, setFontSize] = useState(12); + const [fontColor, setFontColor] = useState("black"); + + useEffect(() => { + if (defaultData) { + setName(defaultData.options?.name || "Cells"); + setCellCount(defaultData.options?.cellCount || 5); + setFontSize(defaultData.options?.fontSize || 12); + setFontColor(defaultData.options?.fontColor || "black"); + } + }, [defaultData]); + + const onSubmit = (e) => { + e.preventDefault(); + handleSave && + handleSave({ + name, + cellCount: parseInt(cellCount, 10), + fontSize, + fontColor + }); + }; + + return ( + + +
    + + setName(e.target.value)} + /> +
    +
    + + setCellCount(e.target.value)} + /> +
    +
    +
    + {t("font-size")}: + +
    +
    + {t("color")}: + + +
    +
    +
    + + +
    + ); +} diff --git a/apps/OpenSign/src/components/pdf/CellsWidget.jsx b/apps/OpenSign/src/components/pdf/CellsWidget.jsx new file mode 100644 index 0000000000..ea6ff5b7aa --- /dev/null +++ b/apps/OpenSign/src/components/pdf/CellsWidget.jsx @@ -0,0 +1,139 @@ +import { useState, useEffect, useRef } from "react"; + +const Cell = ({ + count, + h, + value, + editable, + onChange, + onKeyDown, + onBlur, + inputRef, + index, + fontSize, + fontColor, + hint +}) => ( +
    + onChange && onChange(e, index) : undefined} + onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined} + // trigger validation when leaving a cell + onBlur={editable ? (e) => onBlur && onBlur(e, index) : undefined} + className="w-full text-center focus:outline-none bg-transparent placeholder-gray-300" + placeholder={hint} + style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }} + /> +
    +); + +export default function CellsWidget({ + count = 8, + height = 40, + value = "", + editable = false, + onChange, + onKeyDown, + onBlur, + onCellCountChange, + inputRefs, + resizable = false, + fontSize = "12px", + fontColor = "black", + hint = "" +}) { + const [cellCount, setCellCount] = useState(count); + + // keep internal state in sync with prop updates + useEffect(() => setCellCount(count), [count]); + + const startX = useRef(0); + const startCount = useRef(cellCount); + + const capture = (downEv, onMove, onUp = () => {}) => { + const id = downEv.pointerId; + const move = (ev) => id === ev.pointerId && onMove(ev); + const up = (ev) => { + if (id !== ev.pointerId) return; + onUp(ev); + downEv.target.releasePointerCapture(id); + window.removeEventListener("pointermove", move); + window.removeEventListener("pointerup", up); + }; + window.addEventListener("pointermove", move); + window.addEventListener("pointerup", up); + downEv.target.setPointerCapture(id); + }; + + const onTopHandlePointerDown = (ev) => { + // Prevent triggering the widget drag logic + ev.stopPropagation(); + ev.preventDefault(); + startX.current = ev.clientX; + startCount.current = cellCount; + capture(ev, (moveEv) => { + const dx = moveEv.clientX - startX.current; + const delta = Math.floor(-dx / 5); + let newCount = Math.max(1, startCount.current + delta); + if (newCount !== cellCount) { + setCellCount(newCount); + onCellCountChange?.(newCount); + } + }); + }; + + const cells = Array.from({ length: cellCount }).map((_, i) => value[i] || ""); + const hints = Array.from({ length: cellCount }).map((_, i) => hint[i] || ""); + + return ( +
    + {resizable && ( +
    + +
    + )} + {cells.map((val, i) => ( + (inputRefs.current[i] = el) : undefined} + index={i} + fontSize={fontSize} + fontColor={fontColor} + hint={hints[i]} + /> + ))} +
    + ); +} diff --git a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx index 7a7800f303..75837a3828 100644 --- a/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx +++ b/apps/OpenSign/src/components/pdf/DropdownWidgetOption.jsx @@ -7,8 +7,8 @@ import { fontColorArr, fontsizeArr } from "../../constant/Utils"; function DropdownWidgetOption(props) { const { t } = useTranslation(); const [dropdownOptionList, setDropdownOptionList] = useState([ - "option-1", - "option-2" + "Option-1", + "Option-2" ]); const [minCount, setMinCount] = useState(0); const [maxCount, setMaxCount] = useState(0); @@ -17,11 +17,13 @@ function DropdownWidgetOption(props) { const [isHideLabel, setIsHideLabel] = useState(false); const [status, setStatus] = useState("required"); const [defaultValue, setDefaultValue] = useState(""); - const statusArr = ["required", "optional"]; const [defaultCheckbox, setDefaultCheckbox] = useState([]); + const [layout, setLayout] = useState("vertical"); + const statusArr = ["required", "optional"]; + const layoutArr = ["vertical", "horizontal"]; const resetState = () => { - setDropdownOptionList(["option-1", "option-2"]); + setDropdownOptionList(["Option-1", "Option-2"]); setDropdownName(props.currWidgetsDetails?.options?.name || props.type); setIsReadOnly(false); setIsHideLabel(false); @@ -29,6 +31,7 @@ function DropdownWidgetOption(props) { setMaxCount(0); setDefaultCheckbox([]); setDefaultValue(""); + setLayout("vertical"); }; useEffect(() => { if ( @@ -48,6 +51,7 @@ function DropdownWidgetOption(props) { setStatus(props.currWidgetsDetails?.options?.status || "required"); setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || ""); setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []); + setLayout(props.currWidgetsDetails?.options?.layout || "vertical"); } else { setStatus("required"); resetState(); @@ -107,14 +111,22 @@ function DropdownWidgetOption(props) { props?.type === "dropdown" || props?.type === radioButtonWidget; const readOnlyWithoutValue = isReadOnly && !defaultValue && status !== "optional"; + const isCheckbox = props?.type === "checkbox"; + const WidgetLayout = ["checkbox", radioButtonWidget].includes(props.type) + ? layout + : null; // If it’s a dropdown and it’s read-only without a value (nor marked optional), stop here. if (isDropdownOrRadio && readOnlyWithoutValue) { - alert( - props?.type === "dropdown" - ? t("readonly-dropdown-error") - : t("readonly-radiobtn-error") - ); + alert(t("readonly-error", { widgetName: props?.type })); + return; + } else if ( + isCheckbox && + isReadOnly && + minCount > 0 && + defaultCheckbox?.length === 0 + ) { + alert(t("readonly-error", { widgetName: props?.type })); return; } @@ -129,7 +141,8 @@ function DropdownWidgetOption(props) { null, status, defaultData, - isHideLabel + isHideLabel, + WidgetLayout ); resetState(); }; @@ -153,17 +166,18 @@ function DropdownWidgetOption(props) { }} >
    -
    + + ); +} diff --git a/apps/OpenSign/src/components/pdf/PdfHeader.jsx b/apps/OpenSign/src/components/pdf/PdfHeader.jsx index db801803cf..32569674f7 100644 --- a/apps/OpenSign/src/components/pdf/PdfHeader.jsx +++ b/apps/OpenSign/src/components/pdf/PdfHeader.jsx @@ -2,16 +2,21 @@ import React, { useRef, useState } from "react"; import PrevNext from "./PrevNext"; import { base64ToArrayBuffer, + decryptPdf, deletePdfPage, + flattenPdf, + getFileAsArrayBuffer, handleDownloadCertificate, handleDownloadPdf, handleRemoveWidgets, - handleToPrint + handleToPrint, + reorderPdfPages } from "../../constant/Utils"; import "../../styles/signature.css"; import { DropdownMenu } from "radix-ui"; import ModalUi from "../../primitives/ModalUi"; import Loader from "../../primitives/Loader"; +import PageReorderModal from "./PageReorderModal"; import { useTranslation } from "react-i18next"; import { PDFDocument } from "pdf-lib"; import { maxFileSize } from "../../constant/const"; @@ -24,6 +29,7 @@ function Header(props) { const isMobile = window.innerWidth < 767; const [isDownloading, setIsDownloading] = useState(""); const [isDeletePage, setIsDeletePage] = useState(false); + const [isReorderModal, setIsReorderModal] = useState(false); const mergePdfInputRef = useRef(null); const enabledBackBtn = props?.disabledBackBtn === true ? false : true; //function for show decline alert @@ -80,7 +86,42 @@ function Header(props) { return; } try { - const uploadedPdfBytes = await file.arrayBuffer(); + let uploadedPdfBytes = await file.arrayBuffer(); + try { + uploadedPdfBytes = await flattenPdf(uploadedPdfBytes); + } catch (err) { + if (err?.message?.includes("is encrypted")) { + try { + const pdfFile = await decryptPdf(file, ""); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + } catch (err) { + if (err?.response?.status === 401) { + const password = prompt( + `PDF "${file.name}" is password-protected. Enter password:` + ); + if (password) { + try { + const pdfFile = await decryptPdf(file, password); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + // Upload the file to Parse Server + } catch (err) { + console.error("Incorrect password or decryption failed", err); + alert("Incorrect password or decryption failed."); + } + } else { + alert("Please provided Password."); + } + } else { + console.log("Err ", err); + alert("error while uploading pdf."); + } + } + } else { + alert("error while uploading pdf."); + } + } const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { ignoreEncryption: true }); @@ -106,6 +147,21 @@ function Header(props) { console.error("Error merging PDF:", error); } }; + + const handleReorderSave = async (order) => { + try { + const pdfupdatedData = await reorderPdfPages(props.pdfArrayBuffer, order); + if (pdfupdatedData) { + props.setPdfArrayBuffer(pdfupdatedData.arrayBuffer); + props.setPdfBase64Url(pdfupdatedData.base64); + props.setAllPages(pdfupdatedData.totalPages); + props.setPageNumber(1); + } + } catch (e) { + console.log("error in reorder pdf pages", e); + } + setIsReorderModal(false); + }; return (
    {isMobile && props?.isShowHeader ? ( @@ -334,6 +390,17 @@ function Header(props) {
    + setIsReorderModal(true)} + > +
    + + + {t("reorder-pages")} + +
    +
    + setIsReorderModal(false)} + totalPages={props.allPages} + onSave={handleReorderSave} + />
    ); } diff --git a/apps/OpenSign/src/components/pdf/PdfZoom.jsx b/apps/OpenSign/src/components/pdf/PdfZoom.jsx index dc74ffd0a4..caff5f9e99 100644 --- a/apps/OpenSign/src/components/pdf/PdfZoom.jsx +++ b/apps/OpenSign/src/components/pdf/PdfZoom.jsx @@ -1,18 +1,24 @@ -import React, { useRef, useState } from "react"; +import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { base64ToArrayBuffer, + decryptPdf, deletePdfPage, - handleRemoveWidgets + flattenPdf, + getFileAsArrayBuffer, + handleRemoveWidgets, + reorderPdfPages } from "../../constant/Utils"; import ModalUi from "../../primitives/ModalUi"; import { PDFDocument } from "pdf-lib"; import { maxFileSize } from "../../constant/const"; +import PageReorderModal from "./PageReorderModal"; function PdfZoom(props) { const { t } = useTranslation(); const mergePdfInputRef = useRef(null); const [isDeletePage, setIsDeletePage] = useState(false); + const [isReorderModal, setIsReorderModal] = useState(false); const handleDetelePage = async () => { props.setIsUploadPdf && props.setIsUploadPdf(true); try { @@ -67,7 +73,42 @@ function PdfZoom(props) { return; } try { - const uploadedPdfBytes = await file.arrayBuffer(); + let uploadedPdfBytes = await file.arrayBuffer(); + try { + uploadedPdfBytes = await flattenPdf(uploadedPdfBytes); + } catch (err) { + if (err?.message?.includes("is encrypted")) { + try { + const pdfFile = await decryptPdf(file, ""); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + } catch (err) { + if (err?.response?.status === 401) { + const password = prompt( + `PDF "${file.name}" is password-protected. Enter password:` + ); + if (password) { + try { + const pdfFile = await decryptPdf(file, password); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + // Upload the file to Parse Server + } catch (err) { + console.error("Incorrect password or decryption failed", err); + alert("Incorrect password or decryption failed."); + } + } else { + alert("Please provided Password."); + } + } else { + console.log("Err ", err); + alert("error while uploading pdf."); + } + } + } else { + alert("error while uploading pdf."); + } + } const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { ignoreEncryption: true }); @@ -94,6 +135,21 @@ function PdfZoom(props) { } }; + const handleReorderSave = async (order) => { + try { + const pdfupdatedData = await reorderPdfPages(props.pdfArrayBuffer, order); + if (pdfupdatedData) { + props.setPdfArrayBuffer(pdfupdatedData.arrayBuffer); + props.setPdfBase64Url(pdfupdatedData.base64); + props.setAllPages(pdfupdatedData.totalPages); + props.setPageNumber(1); + } + } catch (e) { + console.log("error in reorder pdf pages", e); + } + setIsReorderModal(false); + }; + return ( <> @@ -120,6 +176,13 @@ function PdfZoom(props) { > + setIsReorderModal(true)} + title={t("reorder-pages")} + > + + )}
    + setIsReorderModal(false)} + totalPages={props.allPages} + onSave={handleReorderSave} + /> ); } diff --git a/apps/OpenSign/src/components/pdf/Placeholder.jsx b/apps/OpenSign/src/components/pdf/Placeholder.jsx index 7b34cc9d08..49c109b218 100644 --- a/apps/OpenSign/src/components/pdf/Placeholder.jsx +++ b/apps/OpenSign/src/components/pdf/Placeholder.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect } from "react"; import BorderResize from "./BorderResize"; import { Rnd } from "react-rnd"; import { @@ -13,6 +13,7 @@ import { onChangeInput, radioButtonWidget, textInputWidget, + cellsWidget, textWidget } from "../../constant/Utils"; import PlaceholderType from "./PlaceholderType"; @@ -80,11 +81,12 @@ function Placeholder(props) { props.pos?.options?.defaultValue || props.pos?.options?.response; const [isDateModal, setIsDateModal] = useState(false); const [containerScale, setContainerScale] = useState(); - const holdTimeout = useRef(null); - const startTime = useRef(null); // Track when the user starts holdings const [selectDate, setSelectDate] = useState({}); const [dateFormat, setDateFormat] = useState([]); const [clickonWidget, setClickonWidget] = useState({}); + const [isDateReadOnly, setIsDateReadOnly] = useState( + props?.pos?.options?.isReadOnly || false + ); const startDate = props?.pos?.options?.response ? getDefaultDate( props?.pos?.options?.response, @@ -303,6 +305,13 @@ function Placeholder(props) { } else if (props.pos.type === "checkbox") { props?.setIsCheckbox(true); } + // cells widget settings in sign yourself flow + else if ( + props.pos.type === cellsWidget && + (props.isSignYourself || props.isSelfSign) + ) { + props.handleCellSettingModal && props.handleCellSettingModal(); + } //condition to handle setting icon for signyour-self flow for all type text widgets else if ( [ @@ -406,11 +415,54 @@ function Placeholder(props) { false, data?.format, props.fontSize || props.pos?.options?.fontSize || 12, - props.fontColor || props.pos?.options?.fontColor || "black" + props.fontColor || props.pos?.options?.fontColor || "black", + isDateReadOnly || false ); setSelectDate({ date: date, format: data?.format }); }; + + const setCellCount = (key, newCount) => { + props.setXyPosition((prev) => { + const isSignerList = prev.some((d) => d.signerPtr); + if (isSignerList) { + const signerId = props.data?.Id || props.uniqueId; + const filterSignerPos = prev.filter((d) => d.Id === signerId); + if (filterSignerPos.length > 0) { + const getPlaceHolder = filterSignerPos[0].placeHolder; + const updatedPlaceHolder = getPlaceHolder.map((ph) => { + if (ph.pageNumber !== props.pageNumber) return ph; + const newPos = ph.pos.map((p) => + p.key === key + ? { ...p, options: { ...p.options, cellCount: newCount } } + : p + ); + return { ...ph, pos: newPos }; + }); + return prev.map((obj) => + obj.Id === signerId + ? { ...obj, placeHolder: updatedPlaceHolder } + : obj + ); + } + } else { + const updatePos = prev[props.index].pos.map((p) => + p.key === key + ? { ...p, options: { ...p.options, cellCount: newCount } } + : p + ); + return prev.map((obj, ind) => + ind === props.index ? { ...obj, pos: updatePos } : obj + ); + } + return prev; + }); + }; const PlaceholderIcon = () => { + const isSettingForCells = + props?.isAlllowModify && !props?.assignedWidgetId.includes(props.pos.key) + ? [] + : [cellsWidget]; + // 1- If props.isShowBorder is true, display border's icon for all widgets. OR // 2- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show border's icon: // 1- When isAlllowModify is true, show border's icon. @@ -434,10 +486,15 @@ function Placeholder(props) { "name", "company", "job title", - "email" + "email", + ...isSettingForCells ].includes(props.pos.type) && (props.isSignYourself || props.isSelfSign) ? ( { + e.stopPropagation(); + handleOnClickSettingIcon(); + }} onClick={(e) => { e.stopPropagation(); handleOnClickSettingIcon(); @@ -447,7 +504,14 @@ function Placeholder(props) { handleOnClickSettingIcon(); }} className="fa-light fa-gear icon" - style={{ color: "#188ae2", right: "29px", top: "-19px" }} + style={{ + color: "#188ae2", + right: "29px", + top: "-19px", + cursor: "pointer", + zIndex: 99, + pointerEvents: "auto" + }} > ) : ( /* condition to add setting icon for placeholder & template flow for all widgets except signature and date */ @@ -457,6 +521,10 @@ function Placeholder(props) { !props.isSignYourself && !props.isSelfSign)) && ( { + e.stopPropagation(); + handleOnClickSettingIcon(); + }} onClick={(e) => { e.stopPropagation(); handleOnClickSettingIcon(); @@ -469,7 +537,10 @@ function Placeholder(props) { style={{ color: "#188ae2", right: props?.pos?.type === textWidget ? "32px" : "51px", - top: "-19px" + top: "-19px", + cursor: "pointer", + zIndex: 99, + pointerEvents: "auto" }} > ) @@ -507,6 +578,7 @@ function Placeholder(props) { {/* setting icon only for date widgets */} {props.pos.type === "date" && selectDate && ( e.stopPropagation()} onClick={(e) => { props.setCurrWidgetsDetails(props.pos); setIsDateModal(!isDateModal); @@ -539,7 +611,9 @@ function Placeholder(props) { top: "-18px", right: props.isPlaceholder ? "50px" : "30px", color: "#188ae2", - fontSize: "14px" + fontSize: "14px", + cursor: "pointer", + pointerEvents: "auto" }} className="fa-light fa-gear icon" > @@ -686,21 +760,6 @@ function Placeholder(props) { return "rgba(203, 233, 237, 0.69)"; } }; - const handleTouchEnd = () => { - if (!props.isNeedSign || props.isAlllowModify) { - const holdDuration = Date.now() - startTime.current; // Calculate hold time - clearTimeout(holdTimeout.current); // Cancel timeout if touch ended early - - if (holdDuration < 1000) { - try { - navigator.vibrate([]); // Cancel any ongoing vibration - } catch (e) { - console.log("error in navigator.vibrate", e); - } - } - } - if (!props.isNeedSign || props.isAlllowModify) handleOnClickPlaceholder(); - }; const fontSize = calculateFont(props.pos.options?.fontSize); const fontColor = props.pos.options?.fontColor || "black"; @@ -751,20 +810,16 @@ function Placeholder(props) { id={props.pos.key} data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""} key={props.pos.key} + cancel=".cell-size-handle, .icon" lockAspectRatio={ - !props.isFreeResize && - ![ - textWidget, - "email", - "name", - "company", - "job title", - textInputWidget - ].includes(props.pos.type) && - (props.pos.Width - ? props.pos.Width / props.pos.Height - : defaultWidthHeight(props.pos.type).width / - defaultWidthHeight(props.pos.type).height) + props?.isAlllowModify && + !props?.assignedWidgetId.includes(props.pos.key) + ? false + : !props.isFreeResize && + (props.pos.Width + ? props.pos.Width / props.pos.Height + : defaultWidthHeight(props.pos.type).width / + defaultWidthHeight(props.pos.type).height) } enableResizing={{ top: false, @@ -820,8 +875,10 @@ function Placeholder(props) { : props.posHeight(props.pos, props.isSignYourself) }} minHeight={ - props.pos.type !== "checkbox" && - calculateFont(props.pos.options?.fontSize, true) + props.pos.type === cellsWidget + ? calculateFont(props.pos.options?.fontSize, true) + : props.pos.type !== "checkbox" && + calculateFont(props.pos.options?.fontSize, true) } maxHeight="auto" onResizeStart={() => { @@ -938,18 +995,19 @@ function Placeholder(props) { handleSaveDate={handleSaveDate} xPos={props.xPos} calculateFont={calculateFont} + setCellCount={setCellCount} />
    )} -
    -
    - {t("format")} : -
    +
    +
    +
    + {t("format")} : props.setFontSize(parseInt(e.target.value))} - > - {fontsizeArr.map((size, ind) => { - return ( +
    +
    + {t("font-size")} : + -
    - {t("color")}: + ))} + +
    +
    + {t("color")} :
    - + {props?.isPlaceholder && ( +
    + setIsDateReadOnly(!isDateReadOnly)} + /> + +
    + )}
    ); case "checkbox": + const checkBoxLayout = props.pos.options?.layout || "vertical"; + const isMultipleCheckbox = + props.pos.options?.values?.length > 0 ? true : false; + const checkBoxWrapperClass = `flex items-start ${ + checkBoxLayout === "horizontal" + ? `flex-row flex-wrap ${isMultipleCheckbox ? "gap-x-2" : ""}` + : `flex-col ${isMultipleCheckbox ? "gap-y-[5px]" : ""}` + }`; // Using gap-y-1 for consistency, adjust if needed + return ( -
    - {props.pos.options?.values?.map((data, ind) => { - return ( -
    - -
    - ); - })} +
    + {props.pos.options?.values?.map((data, ind) => ( +
    + +
    + ))}
    ); case textInputWidget: @@ -192,24 +197,15 @@ function PlaceholderType(props) { placeholder={hint || t("widgets-name.text")} rows={1} value={widgetValue} - className={`${ - props.pos.options?.isReadOnly || - props.data?.signerObjId !== props.signerObjId - ? "select-none" - : textWidgetCls - }`} + className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`} style={{ fontSize: fontSize, color: fontColor, - background: props.data?.blockColor, + background: isReadOnly ? props.data?.blockColor : "white", pointerEvents: "none" }} readOnly - disabled={ - props.isNeedSign && - (props.pos.options?.isReadOnly || - props.data?.signerObjId !== props.signerObjId) - } + disabled={props.isNeedSign && isReadOnly} cols="50" /> ) : ( @@ -217,13 +213,38 @@ function PlaceholderType(props) { {hint || widgetTypeTranslation}
    ); + case cellsWidget: { + const count = props.pos.options?.cellCount || 5; + const cells = (widgetValue || "").split(""); + const height = "100%"; + const fontSize = props.calculateFont(props.pos.options?.fontSize); + const fontColor = props.pos.options?.fontColor || "black"; + const handleCellResize = (newCount) => { + if (props.setCellCount) props.setCellCount(props.pos.key, newCount); + }; + const isEditable = + props.isPlaceholder || props.isSignYourself || props.isSelfSign; + return ( + + ); + } case "dropdown": return (
    - {widgetData || hint || widgetTypeTranslation} + {widgetData || t("choose-one")}
    ); @@ -259,13 +280,15 @@ function PlaceholderType(props) { placeholder={hint || widgetTypeTranslation} rows={1} value={widgetValue} - className={textWidgetCls} + className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`} style={{ fontSize: fontSize, color: fontColor, + background: isReadOnly ? props.data?.blockColor : "white", pointerEvents: "none" }} cols="50" + disabled={props.isNeedSign && isReadOnly} /> ) : (
    @@ -280,13 +303,15 @@ function PlaceholderType(props) { placeholder={hint || widgetTypeTranslation} rows={1} value={widgetValue} - className={textWidgetCls} + className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`} style={{ fontSize: fontSize, color: fontColor, + background: isReadOnly ? props.data?.blockColor : "white", pointerEvents: "none" }} cols="50" + disabled={props.isNeedSign && isReadOnly} /> ) : (
    @@ -301,13 +326,15 @@ function PlaceholderType(props) { placeholder={hint || widgetTypeTranslation} rows={1} value={widgetValue} - className={textWidgetCls} + className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`} style={{ fontSize: fontSize, color: fontColor, + background: isReadOnly ? props.data?.blockColor : "white", pointerEvents: "none" }} cols="50" + disabled={props.isNeedSign && isReadOnly} /> ) : (
    @@ -405,15 +432,15 @@ function PlaceholderType(props) { placeholder={hint || widgetTypeTranslation} rows={1} value={widgetValue} - className={textWidgetCls} + className={`${textWidgetCls} ${isReadOnly ? "select-none" : ""}`} style={{ fontSize: fontSize, color: fontColor, - fontFamily: "Arial, sans-serif", + background: isReadOnly ? props.data?.blockColor : "white", pointerEvents: "none" }} - disabled cols="1" + disabled={props.isNeedSign && isReadOnly} /> ) : (
    @@ -421,46 +448,39 @@ function PlaceholderType(props) {
    ); case radioButtonWidget: + const radioLayout = props.pos.options?.layout || "vertical"; + const isOnlyOneBtn = props.pos.options?.values?.length > 0 ? true : false; + const radioWrapperClass = `flex items-start ${ + radioLayout === "horizontal" + ? `flex-row flex-wrap ${isOnlyOneBtn ? "gap-x-2" : ""}` + : `flex-col ${isOnlyOneBtn ? "gap-y-[5px]" : ""}` + }`; // Using gap-y-1 for consistency, adjust if needed return ( -
    - {props.pos.options?.values.map((data, ind) => { - return ( -
    - -
    - ); - })} +
    + {props.pos.options?.values.map((data, ind) => ( +
    + +
    + ))}
    ); case textWidget: @@ -474,7 +494,8 @@ function PlaceholderType(props) { style={{ fontFamily: "Arial, sans-serif", fontSize: fontSize, - color: fontColor + color: fontColor, + background: "white" }} cols="50" /> diff --git a/apps/OpenSign/src/components/pdf/RecipientList.jsx b/apps/OpenSign/src/components/pdf/RecipientList.jsx index 26b41c1e61..eae63acdf4 100644 --- a/apps/OpenSign/src/components/pdf/RecipientList.jsx +++ b/apps/OpenSign/src/components/pdf/RecipientList.jsx @@ -31,7 +31,7 @@ const RecipientList = (props) => { e.preventDefault(); }; - //handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list + //handle draggable element drop and also used in mobile view on up and down key to change sequence of recipient's list const handleChangeSequence = (e, ind, isUp, isDown, obj) => { e.preventDefault(); let draggedItemId; diff --git a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.jsx b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.jsx index 58372a396d..be42c23948 100644 --- a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.jsx +++ b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.jsx @@ -3,7 +3,12 @@ import { useTranslation } from "react-i18next"; import { Document, Page } from "react-pdf"; import { useSelector } from "react-redux"; import { PDFDocument } from "pdf-lib"; -import { base64ToArrayBuffer } from "../../constant/Utils"; +import { + base64ToArrayBuffer, + decryptPdf, + flattenPdf, + getFileAsArrayBuffer +} from "../../constant/Utils"; import { maxFileSize } from "../../constant/const"; function RenderAllPdfPage(props) { @@ -87,7 +92,42 @@ function RenderAllPdfPage(props) { return; } try { - const uploadedPdfBytes = await file.arrayBuffer(); + let uploadedPdfBytes = await file.arrayBuffer(); + try { + uploadedPdfBytes = await flattenPdf(uploadedPdfBytes); + } catch (err) { + if (err?.message?.includes("is encrypted")) { + try { + const pdfFile = await decryptPdf(file, ""); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + } catch (err) { + if (err?.response?.status === 401) { + const password = prompt( + `PDF "${file.name}" is password-protected. Enter password:` + ); + if (password) { + try { + const pdfFile = await decryptPdf(file, password); + const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile); + uploadedPdfBytes = await flattenPdf(pdfArrayBuffer); + // Upload the file to Parse Server + } catch (err) { + console.error("Incorrect password or decryption failed", err); + alert("Incorrect password or decryption failed."); + } + } else { + alert("Please provided Password."); + } + } else { + console.log("Err ", err); + alert("error while uploading pdf."); + } + } + } else { + alert("error while uploading pdf."); + } + } const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { ignoreEncryption: true }); diff --git a/apps/OpenSign/src/components/pdf/RenderPdf.jsx b/apps/OpenSign/src/components/pdf/RenderPdf.jsx index 8d69ab6bf9..46f78ca08c 100644 --- a/apps/OpenSign/src/components/pdf/RenderPdf.jsx +++ b/apps/OpenSign/src/components/pdf/RenderPdf.jsx @@ -79,7 +79,7 @@ function RenderPdf(props) { } }; - //function for render placeholder block over pdf document + // function for render placeholder block over pdf document (all signing flow) const checkSignedSigners = (data) => { let checkSign = []; //condition to handle quick send flow and using normal request sign flow @@ -91,73 +91,72 @@ function RenderPdf(props) { : []; return ( checkSign.length === 0 && - data?.placeHolder?.map((placeData, key) => { - return ( - - {placeData.pageNumber === props.pageNumber && - placeData.pos.map((pos, ind) => { - return ( - pos && ( - - - - ) - ); - })} - - ); - }) + data?.placeHolder?.map((placeData, key) => ( + + {placeData.pageNumber === props.pageNumber && + placeData.pos.map( + (pos, ind) => + pos && ( + + + + ) + )} + + )) ); }; @@ -172,148 +171,141 @@ function RenderPdf(props) { } }; const pdfDataBase64 = `data:application/pdf;base64,${props.pdfBase64Url}`; - //calculate render height of pdf in mobile view + // calculate render height of pdf in mobile view const handlePageLoadSuccess = (page) => { - const containerWidth = props.divRef.current.offsetWidth; // Get container width - const viewport = page.getViewport({ scale: 1 }); - const scale = containerWidth / viewport.width; // Scale to fit container width - const scaleHeight = viewport.height * scale; - setScaledHeight(scaleHeight); + if (isMobile) { + const containerWidth = props.divRef.current.offsetWidth; // Get container width + const viewport = page.getViewport({ scale: 1 }); + const scale = containerWidth / viewport.width; // Scale to fit container width + const scaleHeight = viewport.height * scale; + setScaledHeight(scaleHeight); + } }; return ( <> {props.successEmail && ( {t("success-email-alert")} )} - {isMobile ? ( - +
    -
    - {props.containerWH?.width && - props.pdfOriginalWH.length > 0 && - (props.pdfRequest || props.isSelfSign - ? props.signerPos?.map((data, key) => { - return ( + {props.pdfLoad !== false && + props.containerWH?.width && + props.pdfOriginalWH.length > 0 && ( + <> + {props.pdfRequest || props.isSelfSign + ? // request sign, guest sign, + props.signerPos?.map((data, key) => ( {checkSignedSigners(data)} - ); - }) - : props.placeholder // placeholder mobile - ? props.signerPos?.map((data, ind) => { - return ( + )) + : props.placeholder // placeholdersign document, draft document, create template, draft template + ? props.signerPos?.map((data, ind) => ( {data?.placeHolder && - data?.placeHolder.map((placeData, index) => { - return ( - - {placeData.pageNumber === props.pageNumber && - placeData.pos.map((pos) => { - return ( - - - - ); - })} - - ); - })} + data?.placeHolder.map((placeData, index) => ( + + {placeData.pageNumber === props.pageNumber && + placeData.pos.map((pos) => ( + + + + ))} + + ))} - ); - }) - : !props.pdfDetails?.[0]?.IsCompleted && - props.xyPosition?.map((data, ind) => { - return ( + )) + : !props.pdfDetails?.[0]?.IsCompleted && // signyourself flow + props.xyPosition?.map((data, ind) => ( {data.pageNumber === props.pageNumber && - data.pos.map((pos, id) => { - return ( + data.pos.map( + (pos, id) => pos && ( ) - ); - })} + )} - ); - }))} - {/* Mobile */} - {t("failed-to-load-refresh-page")}

    } - onLoadError={() => props.setPdfLoad(false)} - loading={t("loading-doc")} - onLoadSuccess={props.pageDetails} - onClick={() => - props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({}) - } - file={pdfDataBase64} - > - { - console.log("annotation error", error); - }} - className="select-none touch-callout-none" - /> -
    -
    - - ) : ( - -
    + )} + {t("failed-to-load-refresh-page")}

    } + onLoadError={(e) => { + console.log("PDF load error", e); + props.setPdfLoad(false); }} - ref={props.drop} - id="container" + loading={t("loading-doc")} + onLoadSuccess={(pdf) => { + props.setPdfLoad(true); + props.pageDetails(pdf); + }} + onClick={() => + props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({}) + } + file={pdfDataBase64} > - {props.pdfLoad && - props.containerWH?.width && - props.pdfOriginalWH.length > 0 && - (props.pdfRequest || props.isSelfSign //pdf request sign flow - ? props.signerPos?.map((data, key) => { - return ( - - {checkSignedSigners(data)} - - ); - }) - : props.placeholder //placeholder and template flow - ? props.signerPos.map((data, ind) => { - return ( - - {data?.placeHolder && - data?.placeHolder.map((placeData, index) => { - return ( - - {placeData.pageNumber === props.pageNumber && - placeData.pos.map((pos) => { - return ( - - - - ); - })} - - ); - })} - - ); - }) - : !props.pdfDetails?.[0]?.IsCompleted && - props.xyPosition?.map((data, ind) => { - // signyourself flow - return ( - - {data.pageNumber === props.pageNumber && - data.pos.map((pos) => { - return ( - - - props.handleStop( - event, - dragElement, - pos.type - ) - } - handleSignYourselfImageResize={ - handleSignYourselfImageResize - } - index={props.index} - xyPosition={props.xyPosition} - setXyPosition={props.setXyPosition} - isShowBorder={true} - isSignYourself={true} - posWidth={posWidth} - posHeight={posHeight} - pdfDetails={props.pdfDetails[0]} - isDragging={props.isDragging} - setIsCheckbox={props.setIsCheckbox} - setCurrWidgetsDetails={ - props.setCurrWidgetsDetails - } - handleTextSettingModal={ - props.handleTextSettingModal - } - scale={props.scale} - containerWH={props.containerWH} - pdfOriginalWH={props.pdfOriginalWH} - pageNumber={props.pageNumber} - fontSize={props.fontSize} - setFontSize={props.setFontSize} - fontColor={props.fontColor} - setFontColor={props.setFontColor} - isResize={props.isResize} - setIsResize={props.setIsResize} - isFreeResize={false} - isOpenSignPad={true} - calculateFontsize={calculateFontsize} - currWidgetsDetails={ - props?.currWidgetsDetails - } - /> - - ); - })} - - ); - }))} - {/* large device */} - {/* this component for render pdf document is in middle of the component */} - {t("failed-to-load-refresh-page")}

    } - onLoadError={() => props.setPdfLoad(false)} - loading={t("loading-doc")} - onLoadSuccess={props.pageDetails} - onClick={() => - props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({}) - } - file={pdfDataBase64} - > - { - console.log("annotation error", error); - }} - /> -
    -
    -
    - )} + { + console.log("annotation error", error); + }} + /> + +
    +
    ); } diff --git a/apps/OpenSign/src/components/pdf/SelectLanguage.jsx b/apps/OpenSign/src/components/pdf/SelectLanguage.jsx index 06aad4be31..eecf75747b 100644 --- a/apps/OpenSign/src/components/pdf/SelectLanguage.jsx +++ b/apps/OpenSign/src/components/pdf/SelectLanguage.jsx @@ -24,14 +24,14 @@ function SelectLanguage(props) {
    handleChange(e)} + required + /> +
    + )} + {[textInputWidget, cellsWidget].includes(props.defaultdata?.type) && ( <>
    - {[textInputWidget].includes(props.defaultdata?.type) && ( + {[textInputWidget, cellsWidget].includes( + props.defaultdata?.type + ) && (
    { })) } /> -
    @@ -317,6 +354,7 @@ const WidgetNameModal = (props) => { {[ textInputWidget, textWidget, + cellsWidget, "name", "company", "job title", diff --git a/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx b/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx index 44d78d2d72..c2a962fb25 100644 --- a/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx +++ b/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx @@ -15,9 +15,11 @@ import { radioButtonWidget, selectCheckbox, textInputWidget, + cellsWidget, textWidget, years } from "../../constant/Utils"; +import CellsWidget from "./CellsWidget"; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import SignatureCanvas from "react-signature-canvas"; @@ -44,9 +46,9 @@ import RegexParser from "regex-parser"; //function to get default format const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy"; -//function to convert formated date to new Date() format +//function to convert formatted date to new Date() format const getDefaultDate = (dateStr, format) => { - //get valid date format for moment to convert formated date to new Date() format + //get valid date format for moment to convert formatted date to new Date() format const formats = changeDateToMomentFormat(format); const parsedDate = moment(dateStr, formats); let date; @@ -85,7 +87,9 @@ function WidgetsValueModal(props) { isSave, setUniqueId, tempSignerId, - signatureTypes + signatureTypes, + setCellCount, + allowCellResize = true } = props; const [penColor, setPenColor] = useState("blue"); const [isOptional, setIsOptional] = useState(true); @@ -93,7 +97,6 @@ function WidgetsValueModal(props) { const [isTab, setIsTab] = useState(""); const [textWidth, setTextWidth] = useState(0); const [textHeight, setTextHeight] = useState(0); - const [signatureType, setSignatureType] = useState(""); const [isSignTypes, setIsSignTypes] = useState(true); const [typedSignature, setTypedSignature] = useState(""); const [selectDate, setSelectDate] = useState({}); @@ -119,13 +122,42 @@ function WidgetsValueModal(props) { const currentUserName = jsonSender && jsonSender?.name; const widgetTypeTranslation = t(`widgets-name.${currWidgetsDetails?.type}`); const [widgetValue, setWidgetValue] = useState( - currWidgetsDetails?.options?.response || - currWidgetsDetails?.options?.defaultValue + currWidgetsDetails.type !== "checkbox" && + (currWidgetsDetails?.options?.response || + currWidgetsDetails?.options?.defaultValue) ); - const [selectedCheckbox, setSelectedCheckbox] = useState( - currWidgetsDetails?.options?.response || + const [cellsValue, setCellsValue] = useState(() => { + const count = currWidgetsDetails?.options?.cellCount || 5; + const val = + currWidgetsDetails?.options?.response || + currWidgetsDetails?.options?.defaultValue || + ""; + return Array.from({ length: count }, (_, i) => val[i] || ""); + }); + const cellRefs = useRef([]); + // keep track of the first empty cell to automatically focus it after updates + useEffect(() => { + const index = cellsValue.findIndex((v) => !v); + if (index !== -1) { + setTimeout(() => { + cellRefs.current[index]?.focus(); + }, 0); + } + }, [cellsValue]); + + useEffect(() => { + const count = currWidgetsDetails?.options?.cellCount || 5; + const val = + currWidgetsDetails?.options?.response || currWidgetsDetails?.options?.defaultValue || - [] + ""; + setCellsValue(Array.from({ length: count }, (_, i) => val[i] || "")); + }, [currWidgetsDetails?.key, currWidgetsDetails?.options?.cellCount]); + const [selectedCheckbox, setSelectedCheckbox] = useState( + currWidgetsDetails.type === "checkbox" && + (currWidgetsDetails?.options?.response || + currWidgetsDetails?.options?.defaultValue || + []) ); const [startDate, setStartDate] = useState( currWidgetsDetails?.options?.response @@ -137,7 +169,7 @@ function WidgetsValueModal(props) { ); const allColor = ["blue", "red", "black"]; const textInputcls = - "op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"; + "op-input op-input-bordered op-input-sm focus:outline-none text-base-content hover:border-base-content w-full text-xs"; const isTabCls = "bg-[#002864] text-white rounded-[15px] px-[10px] py-[4px]"; useEffect(() => { if ( @@ -156,34 +188,6 @@ function WidgetsValueModal(props) { setHint(currWidgetsDetails?.type); } } - //set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets - if (currWidgetsDetails && canvasRef.current) { - const isWidgetType = currWidgetsDetails?.type; - const signatureType = currWidgetsDetails?.signatureType; - const url = currWidgetsDetails?.SignUrl; - //checking widget type and draw type signature url - if (currWidgetsDetails?.type === "initials") { - if (isWidgetType === "initials" && signatureType === "draw" && url) { - canvasRef.current.fromDataURL(url); - } - } else if ( - isWidgetType === "signature" && - signatureType === "draw" && - url - ) { - canvasRef.current.fromDataURL(url); - } - - const trimmedName = currentUserName && currentUserName?.trim(); - const firstCharacter = trimmedName?.charAt(0); - const userName = - currWidgetsDetails?.type === "initials" - ? firstCharacter - : currentUserName; - const signatureValue = currWidgetsDetails?.typeSignature; - setTypedSignature(signatureValue || userName || ""); - setFontSelect("Fasthand"); - } // eslint-disable-next-line react-hooks/exhaustive-deps }, [currWidgetsDetails]); // Added currWidgetsDetails to dependency array for reset logic @@ -193,7 +197,7 @@ function WidgetsValueModal(props) { setRemoveBgEnabled(false); }, [currWidgetsDetails?.key]); - //function to save date and format after seleted new date in response field and after finish document it should be emebed new selected date instead of current date + //function to save date and format after selected new date in response field and after finish document it should embed the new selected date instead of current date useEffect(() => { if (currWidgetsDetails?.type === "date") { const isDateChange = true; @@ -403,7 +407,6 @@ function WidgetsValueModal(props) { const tab = signatureTypes?.[getIndex].name; if (tab === "draw") { setIsTab("draw"); - setSignatureType("draw"); } else if (tab === "upload") { setIsImageSelect(true); setIsTab("uploadImage"); @@ -449,7 +452,32 @@ function WidgetsValueModal(props) { setTypedSignature(""); } } else { - setWidgetValue(""); + if (currWidgetsDetails?.type === cellsWidget) { + const count = + currWidgetsDetails?.options?.cellCount || cellsValue.length || 1; + const cleared = Array.from({ length: count }, () => ""); + setCellsValue(cleared); + const combined = cleared.join(""); + setWidgetValue(combined); + onChangeInput( + combined, + currWidgetsDetails?.key, + xyPosition, + props.index, + setXyPosition, + uniqueId + ); + } else { + setWidgetValue(""); + onChangeInput( + "", + currWidgetsDetails?.key, + xyPosition, + props.index, + setXyPosition, + uniqueId + ); + } } }; //function for set signature url @@ -583,7 +611,7 @@ function WidgetsValueModal(props) { if (isTab === "mysignature") { setSignature(""); if (currWidgetsDetails?.type === "initials") { - handleSaveSignature(signatureType, "initials"); + handleSaveSignature(isTab, "initials"); } else { handleSaveSignature(null, "default"); } @@ -604,13 +632,13 @@ function WidgetsValueModal(props) { } else { setSignature(""); canvasRef?.current?.clear(); - handleSaveSignature(signatureType); + handleSaveSignature(isTab); } } setPenColor("blue"); } else { setSignature(""); - handleSaveImage(signatureType); + handleSaveImage(); } setIsImageSelect(false); setIsDefaultSign(false); @@ -649,24 +677,21 @@ function WidgetsValueModal(props) { }, [fontSelect]); useEffect(() => { - if ( - ["signature", "initials"].includes(currWidgetsDetails?.type) && - widgetValue - ) { - if (isTab === "draw" && currWidgetsDetails?.signatureType === "draw") { - setSignature(widgetValue); - } else if (isTab === "uploadImage" && currWidgetsDetails?.ImageType) { - setImage({ imgType: currWidgetsDetails?.ImageType, src: widgetValue }); + if (currWidgetsDetails?.options?.response) { + const url = currWidgetsDetails?.options?.response; + if (["signature", "initials"].includes(currWidgetsDetails?.type)) { + if (isTab === "draw" && currWidgetsDetails?.signatureType === "draw") { + setSignature(url); + // Load the default signature after the component mounts + if (canvasRef.current) { + canvasRef.current.fromDataURL(url); + } + } else if (isTab === "uploadImage" && currWidgetsDetails?.ImageType) { + setImage({ imgType: currWidgetsDetails?.ImageType, src: url }); + } + } else if (["image", "stamp"].includes(currWidgetsDetails?.type)) { + setImage({ imgType: currWidgetsDetails?.ImageType, src: url }); } - } else if ( - ["image", "stamp"].includes(currWidgetsDetails?.type) && - widgetValue - ) { - setImage({ imgType: currWidgetsDetails?.ImageType, src: widgetValue }); - } - // Load the default signature after the component mounts - if (canvasRef.current) { - canvasRef.current.fromDataURL(signature); } if (isTab === "type") { const trimmedName = typedSignature @@ -746,7 +771,7 @@ function WidgetsValueModal(props) { const PenColorComponent = (props) => { return (
    - Options + Options {allColor.map((data, key) => { return ( { + // hide any prior validation error while typing setIsShowValidation(false); setWidgetValue(e.target.value); onChangeInput( @@ -953,6 +979,55 @@ function WidgetsValueModal(props) { uniqueId ); }; + const updateCells = (updated) => { + setCellsValue(updated); + const combined = updated.join(""); + setWidgetValue(combined); + props.setCurrWidgetsDetails?.((prev) => + prev && prev.key === currWidgetsDetails?.key + ? { ...prev, options: { ...prev.options, response: combined } } + : prev + ); + onChangeInput( + combined, + currWidgetsDetails?.key, + xyPosition, + props.index, + setXyPosition, + uniqueId + ); + }; + + const handleCellsInput = (e, idx) => { + setIsShowValidation(false); + const val = e.target.value.slice(0, 1); + const updated = [...cellsValue]; + updated[idx] = val; + updateCells(updated); + }; + const handleCellsKeyDown = (e, idx) => { + if (e.key === "Backspace" && !cellsValue[idx] && idx > 0) { + e.preventDefault(); + cellRefs.current[idx - 1]?.focus(); + } + }; + + const handleCellResize = (newCount) => { + let updated = [...cellsValue]; + if (newCount > updated.length) { + updated = [...updated, ...Array(newCount - updated.length).fill("")]; + } else if (newCount < updated.length) { + updated = updated.slice(0, newCount); + } + cellRefs.current = cellRefs.current.slice(0, newCount); + updateCells(updated); + setCellCount?.(currWidgetsDetails?.key, newCount); + }; + + // when focus leaves the cells widget, validate the input + const handleCellsBlur = (e, idx) => { + handleInputBlur(); + }; //function is used to show widgets on modal according to selected widget type checkbox/date/radio/drodown/textbox/signature/image const getWidgetType = (type) => { switch (type) { @@ -971,7 +1046,7 @@ function WidgetsValueModal(props) { <> {currWidgetsDetails?.type !== "stamp" && currWidgetsDetails?.type !== "image" && ( -
    +
    <> {currWidgetsDetails?.type !== "initials" && @@ -983,7 +1058,6 @@ function WidgetsValueModal(props) { setIsDefaultSign(true); setIsImageSelect(true); setIsTab("mysignature"); - setSignatureType(""); setImage(); }} className={`${ @@ -1003,7 +1077,6 @@ function WidgetsValueModal(props) { setIsDefaultSign(true); setIsImageSelect(true); setIsTab("mysignature"); - setSignatureType(""); setImage(); }} className={`${ @@ -1040,7 +1113,6 @@ function WidgetsValueModal(props) { setIsDefaultSign(false); setIsImageSelect(true); setIsTab("uploadImage"); - setSignatureType(""); }} className={`${ isTab === "uploadImage" && `${isTabCls}` @@ -1057,7 +1129,6 @@ function WidgetsValueModal(props) { setIsDefaultSign(false); setIsImageSelect(false); setIsTab("type"); - setSignatureType(""); setImage(); }} className={`${ @@ -1272,7 +1343,7 @@ function WidgetsValueModal(props) {
    -
    +
    {/* Standalone autoSignAll for "Type" tab if conditions met */} {setIsAutoSign && uniqueId && (
    @@ -1308,7 +1379,7 @@ function WidgetsValueModal(props) {
    -
    +
    {/* Standalone autoSignAll for "Draw" tab if conditions met */} {setIsAutoSign && uniqueId && (
    @@ -1326,69 +1397,66 @@ function WidgetsValueModal(props) {
    ) : ( -
    -
    -
    - {t("signature")} -
    -
    - × -
    -
    -
    -

    {t("at-least-one-signature-type")}

    -
    +
    +

    {t("at-least-one-signature-type")}

    )}
    ); case "checkbox": + const checkBoxLayout = + currWidgetsDetails?.options?.layout || "vertical"; + const isMultipleCheckbox = + currWidgetsDetails?.options?.values?.length > 0 ? true : false; + const checkBoxWrapperClass = `flex items-start ${ + checkBoxLayout === "horizontal" + ? `flex-row flex-wrap ${isMultipleCheckbox ? "gap-x-2" : ""}` + : `flex-col ${isMultipleCheckbox ? "gap-y-[5px]" : ""}` + }`; // Using gap-y-1 for consistency, adjust if needed + return ( -
    - {currWidgetsDetails?.options?.values?.map((data, ind) => { - return ( -
    -
    ); case textInputWidget: @@ -1401,6 +1469,22 @@ function WidgetsValueModal(props) { className={textInputcls} /> ); + case cellsWidget: + return ( + + ); case "dropdown": return ( ); case "name": @@ -1468,7 +1545,7 @@ function WidgetsValueModal(props) { case "date": return (
    ( @@ -1529,30 +1606,38 @@ function WidgetsValueModal(props) { ); case radioButtonWidget: + const radioLayout = currWidgetsDetails.options?.layout || "vertical"; + const isOnlyOneBtn = + currWidgetsDetails.options?.values?.length > 0 ? true : false; + const radioWrapperClass = `flex items-start ${ + radioLayout === "horizontal" + ? `flex-row flex-wrap ${isOnlyOneBtn ? "gap-x-2" : ""}` + : `flex-col ${isOnlyOneBtn ? "gap-y-[5px]" : ""}` + }`; // Using gap-y-1 for consistency, adjust if needed return ( -
    - {currWidgetsDetails?.options?.values.map((data, ind) => { - return ( -
    - -
    - ); - })} +
    + {currWidgetsDetails?.options?.values.map((data, ind) => ( +
    + +
    + ))}
    ); case textWidget: @@ -1590,15 +1675,9 @@ function WidgetsValueModal(props) { const minCount = currWidgetsDetails?.options?.validation?.minRequiredCount; const parseMin = minCount && parseInt(minCount); - //get maximum required count if exist - const maxCount = - currWidgetsDetails?.options?.validation?.maxRequiredCount; - const parseMax = maxCount && parseInt(maxCount); - if (parseMin > 0 && parseMax > 0) { + if (parseMin > 0) { isRequired = true; } - } else if (isRadio) { - isRequired = true; } else { isRequired = currWidgetsDetails.options?.status === "required"; } @@ -1670,7 +1749,7 @@ function WidgetsValueModal(props) { } } }; - //funtion is used when user enter value any textbox then check validation + //function is used when user enter value in any textbox then check validation const handleInputBlur = () => { const validateType = currWidgetsDetails?.options?.validation?.type; let regexValidation; @@ -1694,7 +1773,7 @@ function WidgetsValueModal(props) { break; } }; - //funtion too uuse on click on next/finish button then update modal ui according to cuurent widgets + //function too use on click on next/finish button then update modal UI according to current widgets const handleClickOnNext = (isFinishDoc) => { if ( ["signature", "stamp", "image", "initials"].includes( @@ -1799,11 +1878,60 @@ function WidgetsValueModal(props) { setUniqueId(tempSignerId); } }; + //function is used to execute the finish button functionality const handleFinish = () => { props?.finishDocument(); dispatch(setIsShowModal({})); }; + useEffect(() => { + if (!isSave) { + handleFinishButton(); + } + }, [isSave]); + //'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) + .map((widget) => widget) + ); + const getcurrentwidget = editableWidgets?.find( + (data) => data?.key === currWidgetsDetails?.key + ); + if (getcurrentwidget?.options?.response) { + props?.setCurrWidgetsDetails(getcurrentwidget); + } + let isResponse = true; + //condition to check all required widgets have response or not then show finish buutton + for (const data of editableWidgets) { + if (data?.type === "checkbox") { + const minCount = data.options?.validation?.minRequiredCount; + const parseMin = minCount && parseInt(minCount); + const hasNoResponse = + (!Array.isArray(data?.options?.response) || + data.options.response.length === 0) && + (!Array.isArray(data?.options?.defaultValue) || + data.options.defaultValue.length === 0); + if (parseMin > 0 && hasNoResponse) { + isResponse = false; + break; + } + } else if ( + !data.options.response && + !data?.options?.defaultValue && + data.options?.status === "required" + ) { + isResponse = false; + break; + } + } + if (isResponse) { + setIsLastWidget(true); + } + }; return ( <>
    - + {currWidgetsDetails?.options?.name || widgetTypeTranslation} {!isOptional && ( @@ -1866,7 +1994,7 @@ function WidgetsValueModal(props) { ) ? ( @@ -681,7 +688,7 @@ function Opensigndrive() {
    @@ -793,7 +800,7 @@ function Opensigndrive() { >
    @@ -809,7 +816,7 @@ function Opensigndrive() {
    diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.jsx b/apps/OpenSign/src/pages/PdfRequestFiles.jsx index f6b09d32e0..7bd022c023 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.jsx +++ b/apps/OpenSign/src/pages/PdfRequestFiles.jsx @@ -23,7 +23,6 @@ import { pdfNewWidthFun, signPdfFun, addDefaultSignatureImg, - radioButtonWidget, replaceMailVaribles, convertPdfArrayBuffer, contractUsers, @@ -47,7 +46,8 @@ import { mailTemplate, updateDateWidgetsRes, widgetDataValue, - getOriginalWH + getOriginalWH, + handleCheckResponse, } from "../constant/Utils"; import Header from "../components/pdf/PdfHeader"; import RenderPdf from "../components/pdf/RenderPdf"; @@ -277,6 +277,15 @@ function PdfRequestFiles( let currUserId; //getting document details const documentData = await contractDocument(docId); + // Filter out 'prefill' roles from the Placeholder array + const filteredPlaceholder = documentData[0].Placeholders.filter( + (data) => data.Role !== "prefill" + ); + // Reassign the updated Placeholder back to the documentData array + documentData[0] = { + ...documentData[0], + Placeholders: filteredPlaceholder + }; if (documentData && documentData.length > 0) { const userSignatureType = documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes; @@ -624,6 +633,7 @@ function PdfRequestFiles( } catch (err) { console.log("err in get email verification ", err); setHandleError(t("something-went-wrong-mssg")); + setIsUiLoading(false); } } //check if isEmailVerified then go on next step @@ -633,153 +643,13 @@ function PdfRequestFiles( (data) => data.signerObjId === signerObjectId ); if (checkUser && checkUser.length > 0) { - let checkboxExist, - requiredRadio, - showAlert = false, - widgetKey, - radioExist, - requiredCheckbox, - TourPageNumber; // `pageNumber` is used to check on which page user did not fill widget's data then change current pageNumber and show tour message on that page - - for (let i = 0; i < checkUser[0].placeHolder.length; i++) { - for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) { - //get current page - const updatePage = checkUser[0].placeHolder[i]?.pageNumber; - //checking checbox type widget - checkboxExist = - checkUser[0].placeHolder[i].pos[j].type === "checkbox"; - //checking radio button type widget - radioExist = - checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget; - //condition to check checkbox widget exist or not - if (checkboxExist) { - //get all required type checkbox - requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( - (position) => - !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 - if (requiredCheckbox && requiredCheckbox.length > 0) { - for (let i = 0; i < requiredCheckbox.length; i++) { - //get minimum required count if exit - const minCount = - requiredCheckbox[i].options?.validation?.minRequiredCount; - const parseMin = minCount && parseInt(minCount); - //get maximum required count if exit - const maxCount = - requiredCheckbox[i].options?.validation?.maxRequiredCount; - const parseMax = maxCount && parseInt(maxCount); - //in `response` variable is used to get how many checkbox checked by user - const response = - requiredCheckbox[i].options?.response?.length; - //in `defaultValue` variable is used to get how many checkbox checked by default - const defaultValue = - requiredCheckbox[i].options?.defaultValue?.length; - //condition to check parseMin and parseMax greater than 0 then consider it as a required check box - if ( - parseMin > 0 && - parseMax > 0 && - !response && - !defaultValue && - !showAlert - ) { - showAlert = true; - widgetKey = requiredCheckbox[i].key; - TourPageNumber = updatePage; - setminRequiredCount(parseMin); - } - //else condition to validate minimum required checkbox - else if ( - parseMin > 0 && - (parseMin > response || !response) - ) { - if (!showAlert) { - showAlert = true; - widgetKey = requiredCheckbox[i].key; - TourPageNumber = updatePage; - setminRequiredCount(parseMin); - } - } - } - } - } - //condition to check radio widget exist or not - else if (radioExist) { - //get all required type radio button - requiredRadio = checkUser[0].placeHolder[i].pos.filter( - (position) => - !position.options?.isReadOnly && - position.type === radioButtonWidget - ); - //if required type radio data exit then check user checked all radio button or some radio remain to check - if (requiredRadio && requiredRadio?.length > 0) { - let checkSigned; - for (let i = 0; i < requiredRadio?.length; i++) { - checkSigned = requiredRadio[i]?.options?.response; - if (!checkSigned) { - let checkDefaultSigned = - requiredRadio[i]?.options?.defaultValue; - if (!checkDefaultSigned && !showAlert) { - showAlert = true; - widgetKey = requiredRadio[i].key; - TourPageNumber = updatePage; - setminRequiredCount(null); - } - } - } - } - } - //else condition to check all type widget data fill or not except checkbox and radio button - else { - //get all required type widgets except checkbox and radio - const requiredWidgets = checkUser[0].placeHolder[i].pos.filter( - (position) => - position.options?.status === "required" && - position.type !== radioButtonWidget && - position.type !== "checkbox" - ); - if (requiredWidgets && requiredWidgets?.length > 0) { - let checkSigned; - for (let i = 0; i < requiredWidgets?.length; i++) { - checkSigned = requiredWidgets[i]?.options?.response; - if (!checkSigned) { - const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl; - if (!checkSignUrl) { - let checkDefaultSigned = - requiredWidgets[i]?.options?.defaultValue; - if (!checkDefaultSigned && !showAlert) { - showAlert = true; - widgetKey = requiredWidgets[i].key; - TourPageNumber = updatePage; - setminRequiredCount(null); - } - } - } - } - } - } - } - //when showAlert is true then break the loop and show alert to fill required data in widgets - if (showAlert) { - break; - } - } - if (checkboxExist && requiredCheckbox && showAlert) { - setUnSignedWidgetId(widgetKey); - setPageNumber(TourPageNumber); - setWidgetsTour(true); - } else if (radioExist && showAlert) { - setUnSignedWidgetId(widgetKey); - setPageNumber(TourPageNumber); - setWidgetsTour(true); - } else if (showAlert) { - setUnSignedWidgetId(widgetKey); - setPageNumber(TourPageNumber); + const status = handleCheckResponse(checkUser,setminRequiredCount) + if (status?.showAlert) { + setUnSignedWidgetId(status?.widgetKey); + setPageNumber(status?.tourPageNumber); setWidgetsTour(true); + setIsUiLoading(false); } else { - setIsUiLoading(true); // `widgets` is Used to return widgets details with page number of current user const widgets = checkUser?.[0]?.placeHolder; let pdfArrBuffer; @@ -829,7 +699,6 @@ function PdfRequestFiles( isSignYourSelfFlow, scale ); - // console.log("pdfte", pdfBytes); //get ExistUserPtr object id of user class to get tenantDetails if (!pdfBytes?.error) { const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; @@ -855,16 +724,19 @@ function PdfRequestFiles( isSuccessRoute, contactId ); - const index = pdfDetails?.[0]?.Signers.findIndex( - (x) => x.objectId === signerObjectId - ); + const index = + updatedDoc.updatedPdfDetails?.[0]?.Signers.findIndex( + (x) => x.objectId === contactId + ); const newIndex = index + 1; const usermail = { - Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || "" + Email: + updatedDoc.updatedPdfDetails?.[0]?.Placeholders[newIndex] + ?.email || "" }; const user = usermail?.Email ? usermail - : pdfDetails?.[0]?.Signers[newIndex]; + : updatedDoc.updatedPdfDetails?.[0]?.Signers[newIndex]; if ( sendmail !== "false" && sendInOrder @@ -1023,6 +895,7 @@ function PdfRequestFiles( isShow: true, alertMessage: t("something-went-wrong-mssg") }); + setIsUiLoading(false); } } catch (err) { console.log("err in embedsign", err); @@ -1034,8 +907,8 @@ function PdfRequestFiles( } } } - const handleSignPdf = async () => { + setIsUiLoading(true); await embedWidgetsData(); }; @@ -1059,15 +932,15 @@ function PdfRequestFiles( let filterSignerPos = []; if (signerObjId) { //get current signerObjId placeholder details - filterSignerPos = updateSignPos.filter( + filterSignerPos = updateSignPos?.filter( (data) => data.Id === signerObjId ); } if (filterSignerPos.length > 0) { - const getPlaceHolder = filterSignerPos[0].placeHolder; + const getPlaceHolder = filterSignerPos[0]?.placeHolder; //get position of current pagenumber - const getPageNumer = getPlaceHolder.filter( + const getPageNumer = getPlaceHolder?.filter( (data) => data.pageNumber === pageNumber ); if (getPageNumer.length > 0) { @@ -1111,9 +984,9 @@ function PdfRequestFiles( setIsTextSetting(value); }; const handleSaveFontSize = () => { - const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); + const filterSignerPos = signerPos?.filter((data) => data.Id === uniqueId); if (filterSignerPos) { - const placehoder = filterSignerPos[0].placeHolder; + const placehoder = filterSignerPos[0]?.placeHolder; const getPageNumer = placehoder.filter( (data) => data.pageNumber === pageNumber ); @@ -1229,7 +1102,7 @@ function PdfRequestFiles( const addDefaultSignature = () => { const type = defaultSignAlert?.type; //get current signers placeholder position data - const currentSignerPosition = signerPos.filter( + const currentSignerPosition = signerPos?.filter( (data) => data.signerObjId === signerObjectId ); const defaultSign = type === "signature" ? defaultSignImg : myInitial; @@ -1254,7 +1127,8 @@ function PdfRequestFiles( setRequestSignTour(true); if (isDontShow) { const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false; - if (!isEnableOTP) { + const sessionToken = localStorage.getItem("accesstoken"); + if (!isEnableOTP && !sessionToken) { try { await axios.post( `${localStorage.getItem("baseUrl")}functions/updatecontacttour`, @@ -1619,11 +1493,11 @@ function PdfRequestFiles( } if (uniqueId) { let filterSignerPos, currentPagePosition; - filterSignerPos = signerPos.find((data) => data.Id === uniqueId); + filterSignerPos = signerPos?.find((data) => data.Id === uniqueId); const getPlaceHolder = filterSignerPos?.placeHolder; if (getPlaceHolder) { //checking exist placeholder on same page - currentPagePosition = getPlaceHolder.find( + currentPagePosition = getPlaceHolder?.find( (data) => data.pageNumber === pageNumber ); } @@ -1651,12 +1525,6 @@ function PdfRequestFiles( ); setSignerPos(updatesignerPos); } - - // if (dragTypeValue === "dropdown") { - // setShowDropdown(true); - // } else if (dragTypeValue === "checkbox") { - // setIsCheckbox(true); - // } else if ( [textWidget, "name", "company", "job title", "email"].includes( dragTypeValue @@ -1672,7 +1540,7 @@ function PdfRequestFiles( //function for delete signature block const handleDeleteSign = (key, Id) => { const updateData = []; - const filterSignerPos = signerPos.filter((data) => data.Id === Id); + const filterSignerPos = signerPos?.filter((data) => data.Id === Id); if (filterSignerPos.length > 0) { const getPlaceHolder = filterSignerPos[0].placeHolder; const getPageNumer = getPlaceHolder.filter( @@ -1700,7 +1568,7 @@ function PdfRequestFiles( }); setSignerPos(newUpdateSigner); } else { - const getRemainPage = filterSignerPos[0].placeHolder.filter( + const getRemainPage = filterSignerPos[0]?.placeHolder?.filter( (data) => data.pageNumber !== pageNumber ); //condition to check placeholder length is greater than 1 do not need to remove whole placeholder @@ -1713,11 +1581,11 @@ function PdfRequestFiles( return obj; }); let signerupdate = []; - signerupdate = signerPos.filter((data) => data.Id !== Id); + signerupdate = signerPos?.filter((data) => data.Id !== Id); signerupdate.push(newUpdatePos[0]); setSignerPos(signerupdate); } else { - const updatedData = signerPos.map((item) => { + const updatedData = signerPos?.map((item) => { if (item.Id === Id) { // Create a copy of the item object and delete the placeHolder field const updatedItem = { ...item }; @@ -1735,7 +1603,7 @@ function PdfRequestFiles( //function to get first widget and page number to assign currect signer and tour message const showFirstWidget = () => { if (!requestSignTour) { - const getCurrentUserPlaceholder = signerPos.find( + const getCurrentUserPlaceholder = signerPos?.find( (x) => x.Id === uniqueId ); const placeholder = getCurrentUserPlaceholder.placeHolder; @@ -1756,7 +1624,6 @@ function PdfRequestFiles( setShowSignPagenumber(sortedPagenumber); } }; - return ( )} <DownloadPdfZip diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.jsx b/apps/OpenSign/src/pages/PlaceHolderSign.jsx index e215d3d816..faedb49cda 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.jsx +++ b/apps/OpenSign/src/pages/PlaceHolderSign.jsx @@ -27,6 +27,7 @@ import { multiSignEmbed, addWidgetOptions, textInputWidget, + cellsWidget, textWidget, radioButtonWidget, color, @@ -1417,7 +1418,8 @@ function PlaceHolderSign() { deleteOption, status, defaultValue, - isHideLabel + isHideLabel, + layout ) => { const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); if (filterSignerPos.length > 0) { @@ -1453,6 +1455,7 @@ function PlaceHolderSign() { name: dropdownName, values: dropdownOptions, status: status, + layout: layout, isReadOnly: isReadOnly || false, isHideLabel: isHideLabel || false, defaultValue: defaultValue, @@ -1492,6 +1495,7 @@ function PlaceHolderSign() { maxRequiredCount: maxCount }, defaultValue: defaultValue, + layout: layout, isReadOnly: isReadOnly || false, isHideLabel: isHideLabel || false, fontSize: @@ -1590,6 +1594,36 @@ function PlaceHolderSign() { isReadOnly: defaultdata?.isReadOnly || false } }; + } else if (position.type === cellsWidget) { + return { + ...position, + options: { + ...position.options, + name: defaultdata?.name || "Cells", + status: defaultdata?.status || "required", + hint: defaultdata?.hint || "", + cellCount: parseInt(defaultdata?.cellCount || 5), + defaultValue: (defaultdata?.defaultValue || "").slice( + 0, + parseInt(defaultdata?.cellCount || 5) + ), + validation: + isSubscribe && inputype + ? { + type: inputype, + pattern: + inputype === "regex" ? defaultdata.textvalidate : "" + } + : {}, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || 12, + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black", + isReadOnly: defaultdata?.isReadOnly || false + } + }; } else if (["signature"].includes(position.type)) { return { ...position, @@ -2150,7 +2184,7 @@ function PlaceHolderSign() { navigate("/report/1MwEuxLEkF"); }} > - <div className="h-[100%] p-[20px]"> + <div className="h-[100%] p-[20px] text-base-content"> {mailStatus === "success" ? ( <div className="text-center mb-[10px]"> <LottieWithLoader /> diff --git a/apps/OpenSign/src/pages/Preferences.jsx b/apps/OpenSign/src/pages/Preferences.jsx index 85179c40c6..0e6d170e30 100644 --- a/apps/OpenSign/src/pages/Preferences.jsx +++ b/apps/OpenSign/src/pages/Preferences.jsx @@ -301,7 +301,7 @@ const Preferences = () => { JSON.parse(localStorage.getItem("Extand_Class"))?.[0]; if (extUser && extUser?.objectId) { extUser.TenantId.RequestBody = updateRes?.RequestBody; - extUser.TenantId.RequestBody = updateRes?.RequestSubject; + extUser.TenantId.RequestSubject = updateRes?.RequestSubject; const _extUser = JSON.parse(JSON.stringify(extUser)); localStorage.setItem("Extand_Class", JSON.stringify([_extUser])); } diff --git a/apps/OpenSign/src/pages/Report.jsx b/apps/OpenSign/src/pages/Report.jsx index 6b96481f8c..868e2a9bb0 100644 --- a/apps/OpenSign/src/pages/Report.jsx +++ b/apps/OpenSign/src/pages/Report.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import ReportTable from "../primitives/GetReportDisplay"; import Parse from "parse"; import axios from "axios"; @@ -27,12 +27,18 @@ const Report = () => { const [isImport, setIsImport] = useState(false); const abortController = new AbortController(); const docPerPage = 10; + const [searchTerm, setSearchTerm] = useState(""); + const [mobileSearchOpen, setMobileSearchOpen] = useState(false); + const [isSearchResult, setIsSearchResult] = useState(false); + const debounceTimer = useRef(null); // below useEffect is call when id param change useEffect(() => { setReportName(""); setList([]); - getReportData(); + setSearchTerm(""); + setMobileSearchOpen(false); + getReportData(0, docPerPage, ""); // Function returned from useEffect is called on unmount return () => { @@ -48,7 +54,7 @@ const Report = () => { // below useEffect call when isNextRecord state is true and fetch next record useEffect(() => { if (isNextRecord) { - getReportData(List.length, 20); + getReportData(List.length, 20, searchTerm); } // eslint-disable-next-line }, [isNextRecord]); @@ -56,7 +62,58 @@ const Report = () => { const handleDontShow = (isChecked) => { setIsDontShow(isChecked); }; - const getReportData = async (skipUserRecord = 0, limit = 20) => { + + const handleSearchChange = async (e) => { + const term = e.target.value.toLowerCase(); + setSearchTerm(term); + if (debounceTimer.current) { + clearTimeout(debounceTimer.current); + } + debounceTimer.current = setTimeout(async () => { + try { + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + sessiontoken: localStorage.getItem("accesstoken") + }; + const url = `${localStorage.getItem("baseUrl")}functions/getReport`; + const res = await axios.post( + url, + { reportId: id, searchTerm: term, skip: 0, limit: docPerPage }, + { headers } + ); + const data = res.data?.result || []; + if (!data.error) { + setList(data); + setIsMoreDocs(data.length >= docPerPage); + setIsNextRecord(false); + setIsSearchResult(true); + } + } catch (err) { + console.error("Search error:", err); + } + }, 300); + setIsSearchResult(false); + }; + + const handleSearchPaste = (e) => { + setTimeout(() => { + handleSearchChange({ target: { value: e.target.value } }); + }, 0); + }; + + useEffect(() => { + return () => { + if (debounceTimer.current) { + clearTimeout(debounceTimer.current); + } + }; + }, []); + const getReportData = async ( + skipUserRecord = 0, + limit = 20, + term = searchTerm + ) => { // setIsLoader(true); const json = reportJson(id); if (json) { @@ -77,6 +134,9 @@ const Report = () => { const skipRecord = id === "4Hhwbp482K" ? 0 : skipUserRecord; const limitRecord = id === "4Hhwbp482K" ? 200 : limit; const params = { reportId: id, skip: skipRecord, limit: limitRecord }; + if (term) { + params.searchTerm = term; + } const url = `${localStorage.getItem("baseUrl")}functions/getReport`; const res = await axios.post(url, params, { headers: headers, @@ -197,6 +257,12 @@ const Report = () => { report_help={reporthelp} tourData={tourData} isDontShow={isDontShow} + mobileSearchOpen={mobileSearchOpen} + setMobileSearchOpen={setMobileSearchOpen} + searchTerm={searchTerm} + handleSearchChange={handleSearchChange} + handleSearchPaste={handleSearchPaste} + isSearchResult={isSearchResult} isImport={isImport} /> ) : ( diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.jsx b/apps/OpenSign/src/pages/SignyourselfPdf.jsx index 3b7a3bae39..93f5a79f1f 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.jsx +++ b/apps/OpenSign/src/pages/SignyourselfPdf.jsx @@ -22,6 +22,7 @@ import { randomId, getDate, textWidget, + cellsWidget, convertPdfArrayBuffer, textInputWidget, fetchImageBase64, @@ -68,6 +69,8 @@ import { resetWidgetState } from "../redux/reducers/widgetSlice.js"; import WidgetsValueModal from "../components/pdf/WidgetsValueModal"; +import WidgetNameModal from "../components/pdf/WidgetNameModal"; +import CellsSettingModal from "../components/pdf/CellsSettingModal"; //For signYourself inProgress section signer can add sign and complete doc sign. function SignYourSelf() { const { t } = useTranslation(); @@ -118,6 +121,10 @@ function SignYourSelf() { const [isTextSetting, setIsTextSetting] = useState(false); const [currWidgetsDetails, setCurrWidgetsDetails] = useState({}); const [isCheckbox, setIsCheckbox] = useState(false); + const [isNameModal, setIsNameModal] = useState(false); + const [isCellsSetting, setIsCellsSetting] = useState(false); + const openNameModal = () => setIsNameModal(true); + const openCellsSettingModal = () => setIsCellsSetting(true); const [pdfLoad, setPdfLoad] = useState(false); const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" }); const [isDontShow, setIsDontShow] = useState(false); @@ -356,9 +363,13 @@ function SignYourSelf() { ); const dragTypeValue = item?.text ? item.text : monitor.type; const widgetValue = getWidgetValue(dragTypeValue); - const widgetTypeExist = ["name", "company", "job title", "email"].includes( - dragTypeValue - ); + const widgetTypeExist = [ + "name", + "company", + "job title", + "email", + cellsWidget + ].includes(dragTypeValue); const containerScale = getContainerScale( pdfOriginalWH, pageNumber, @@ -439,6 +450,7 @@ function SignYourSelf() { [ textInputWidget, textWidget, + cellsWidget, "name", "company", "job title", @@ -931,7 +943,8 @@ function SignYourSelf() { deleteOption, status, defaultValue, - isHideLabel + isHideLabel, + layout ) => { const getPageNumer = xyPosition.filter( (data) => data.pageNumber === pageNumber @@ -939,6 +952,8 @@ function SignYourSelf() { if (getPageNumer.length > 0) { const getXYdata = getPageNumer[0].pos; const getPosData = getXYdata; + const widgetLayout = + currWidgetsDetails?.type === "checkbox" ? { layout: layout } : {}; const addSignPos = getPosData.map((position) => { if (position.key === currWidgetsDetails?.key) { if (addOption) { @@ -962,6 +977,7 @@ function SignYourSelf() { ...position.options, name: dropdownName, values: dropdownOptions, + ...widgetLayout, isReadOnly: isReadOnly, isHideLabel: isHideLabel || false, fontSize: @@ -1028,6 +1044,92 @@ function SignYourSelf() { handleTextSettingModal(false); } }; + + const setCellCount = (key, newCount) => { + setXyPosition((prev) => { + const getPageNumer = prev.filter((data) => data.pageNumber === pageNumber); + if (getPageNumer.length > 0) { + const updatePos = getPageNumer[0].pos.map((p) => + p.key === key ? { ...p, options: { ...p.options, cellCount: newCount } } : p + ); + return prev.map((obj, ind) => (ind === index ? { ...obj, pos: updatePos } : obj)); + } + return prev; + }); + }; + + const handleWidgetdefaultdata = (defaultdata) => { + const newFontSize = + defaultdata?.fontSize !== undefined ? defaultdata.fontSize : fontSize; + const newFontColor = + defaultdata?.fontColor !== undefined ? defaultdata.fontColor : fontColor; + + const getPageNumer = xyPosition.filter( + (data) => data.pageNumber === pageNumber + ); + if (getPageNumer.length > 0) { + const updatePos = getPageNumer[0].pos.map((position) => { + if (position.key === currWidgetsDetails?.key) { + if (position.type === cellsWidget) { + const count = parseInt( + defaultdata?.cellCount ?? position.options?.cellCount ?? 5, + 10 + ); + return { + ...position, + options: { + ...position.options, + name: defaultdata?.name || position.options?.name || "Cells", + cellCount: count, + defaultValue: (defaultdata?.defaultValue || "").slice(0, count), + fontSize: newFontSize || position.options?.fontSize || 12, + fontColor: newFontColor || position.options?.fontColor || "black" + } + }; + } else { + return { + ...position, + options: { + ...position.options, + name: defaultdata?.name || position.options?.name, + fontSize: newFontSize || position.options?.fontSize || 12, + fontColor: + newFontColor || position.options?.fontColor || "black" + } + }; + } + } + return position; + }); + const updateXYposition = xyPosition.map((obj, ind) => + ind === index ? { ...obj, pos: updatePos } : obj + ); + setXyPosition(updateXYposition); + } + setFontSize(); + setFontColor(); + setCurrWidgetsDetails({}); + setIsNameModal(false); + }; + + const handleNameModal = () => { + setIsNameModal(false); + setCurrWidgetsDetails({}); + setIsCheckbox(false); + }; + + const handleCellsSettingSave = (data) => { + // ensure font and color are updated before applying widget changes + if (data?.fontSize !== undefined) setFontSize(data.fontSize); + if (data?.fontColor !== undefined) setFontColor(data.fontColor); + handleWidgetdefaultdata(data); + setIsCellsSetting(false); + }; + + const handleCellsSettingClose = () => { + setIsCellsSetting(false); + setCurrWidgetsDetails({}); + }; const clickOnZoomIn = () => { onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent); }; @@ -1183,9 +1285,8 @@ function SignYourSelf() { title={t("document-signed")} handleClose={() => setShowAlreadySignDoc({ status: false })} > - <div className="p-[20px] h-full"> + <div className="p-[20px] h-full text-base-content"> <p>{showAlreadySignDoc.mssg}</p> - <div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div> <button className="op-btn op-btn-ghost shadow-md" @@ -1283,12 +1384,15 @@ function SignYourSelf() { setIsPageCopy={setIsPageCopy} setIsCheckbox={setIsCheckbox} setCurrWidgetsDetails={setCurrWidgetsDetails} + handleNameModal={openNameModal} + handleCellSettingModal={openCellsSettingModal} handleTextSettingModal={handleTextSettingModal} setScale={setScale} scale={scale} pdfBase64Url={pdfBase64Url} fontSize={fontSize} setFontSize={setFontSize} + setCellCount={setCellCount} fontColor={fontColor} setFontColor={setFontColor} isResize={isResize} @@ -1330,6 +1434,7 @@ function SignYourSelf() { xyPosition={xyPosition} //placeholder details pageNumber={pageNumber} //current page number setXyPosition={setXyPosition} //placeholder details state + setCellCount={setCellCount} setPageNumber={setPageNumber} setCurrWidgetsDetails={setCurrWidgetsDetails} currWidgetsDetails={currWidgetsDetails} @@ -1338,6 +1443,23 @@ function SignYourSelf() { signatureTypes={signatureTypes} /> )} + <WidgetNameModal + widgetName={currWidgetsDetails?.options?.name} + defaultdata={currWidgetsDetails} + isOpen={isNameModal} + handleClose={handleNameModal} + handleData={handleWidgetdefaultdata} + fontSize={fontSize} + setFontSize={setFontSize} + fontColor={fontColor} + setFontColor={setFontColor} + /> + <CellsSettingModal + isOpen={isCellsSetting} + defaultData={currWidgetsDetails} + handleClose={handleCellsSettingClose} + handleSave={handleCellsSettingSave} + /> <RotateAlert showRotateAlert={showRotateAlert.status} setShowRotateAlert={setShowRotateAlert} diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.jsx b/apps/OpenSign/src/pages/TemplatePlaceholder.jsx index 85b4e249f9..4cb2d3bd68 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.jsx +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useRef } from "react"; +import { useEffect, useState, useRef } from "react"; import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import { useParams, useNavigate } from "react-router"; import axios from "axios"; @@ -22,6 +22,7 @@ import { defaultWidthHeight, addWidgetOptions, textInputWidget, + cellsWidget, radioButtonWidget, getContainerScale, convertBase64ToFile, @@ -1274,7 +1275,8 @@ const TemplatePlaceholder = () => { deleteOption, status, defaultValue, - isHideLabel + isHideLabel, + layout ) => { const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId); if (filterSignerPos.length > 0) { @@ -1313,6 +1315,7 @@ const TemplatePlaceholder = () => { name: dropdownName, values: dropdownOptions, status: status, + layout: layout, defaultValue: defaultValue, isReadOnly: isReadOnly || false, isHideLabel: isHideLabel || false, @@ -1351,6 +1354,7 @@ const TemplatePlaceholder = () => { minRequiredCount: minCount, maxRequiredCount: maxCount }, + layout: layout, isReadOnly: isReadOnly || false, defaultValue: defaultValue, isHideLabel: isHideLabel || false, @@ -1451,6 +1455,36 @@ const TemplatePlaceholder = () => { "black" } }; + } else if (position.type === cellsWidget) { + return { + ...position, + options: { + ...position.options, + name: defaultdata?.name || "Cells", + status: defaultdata?.status || "required", + hint: defaultdata?.hint || "", + cellCount: parseInt(defaultdata?.cellCount || 5), + defaultValue: (defaultdata?.defaultValue || "").slice( + 0, + parseInt(defaultdata?.cellCount || 5) + ), + validation: + isSubscribe && inputype + ? { + type: inputype, + pattern: + inputype === "regex" ? defaultdata.textvalidate : "" + } + : {}, + isReadOnly: defaultdata?.isReadOnly || false, + fontSize: + fontSize || currWidgetsDetails?.options?.fontSize || 12, + fontColor: + fontColor || + currWidgetsDetails?.options?.fontColor || + "black" + } + }; } else if (["signature"].includes(position.type)) { return { ...position, @@ -1510,6 +1544,22 @@ const TemplatePlaceholder = () => { setIsRadio(false); setIsCheckbox(false); }; + const setCellCount = (key, newCount) => { + const updated = signerPos.map((signer) => { + if (signer.Id !== uniqueId) return signer; + const placeHolder = signer.placeHolder.map((ph) => { + if (ph.pageNumber !== pageNumber) return ph; + const pos = ph.pos.map((p) => + p.key === key + ? { ...p, options: { ...p.options, cellCount: newCount } } + : p + ); + return { ...ph, pos }; + }); + return { ...signer, placeHolder }; + }); + setSignerPos(updated); + }; const clickOnZoomIn = () => { onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent); @@ -1671,7 +1721,7 @@ const TemplatePlaceholder = () => { navigate("/report/6TeaPr321t"); }} > - <div className="h-full p-[20px] mb-2"> + <div className="h-full p-[20px] mb-2 text-base-content"> <p>{t("template-created-alert")}</p> <div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div> <div className="flex gap-1 flex-col md:flex-row"> @@ -1828,6 +1878,7 @@ const TemplatePlaceholder = () => { pdfBase64Url={pdfBase64Url} fontSize={fontSize} setFontSize={setFontSize} + setCellCount={setCellCount} fontColor={fontColor} setFontColor={setFontColor} isResize={isResize} diff --git a/apps/OpenSign/src/pages/UserList.jsx b/apps/OpenSign/src/pages/UserList.jsx index bdf38ee2f8..52d2d20e69 100644 --- a/apps/OpenSign/src/pages/UserList.jsx +++ b/apps/OpenSign/src/pages/UserList.jsx @@ -284,7 +284,7 @@ const UserList = () => { handleClose={handleClose} > <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> + <div className="text-lg font-normal text-base-content"> {t("are-you-sure")}{" "} {item?.IsDisabled ? t("activate") diff --git a/apps/OpenSign/src/pages/UserProfile.jsx b/apps/OpenSign/src/pages/UserProfile.jsx index 8c6f02c2ca..17f286a3f1 100644 --- a/apps/OpenSign/src/pages/UserProfile.jsx +++ b/apps/OpenSign/src/pages/UserProfile.jsx @@ -286,7 +286,7 @@ function UserProfile() { style={{ width: `${percentage}%` }} ></div> </div> - <span className="text-black text-sm">{percentage}%</span> + <span className="text-base-contentk text-sm">{percentage}%</span> </div> )} <div className="text-base font-semibold pt-4"> diff --git a/apps/OpenSign/src/primitives/DownloadPdfZip.jsx b/apps/OpenSign/src/primitives/DownloadPdfZip.jsx index 2c454316e2..f6e59d21e7 100644 --- a/apps/OpenSign/src/primitives/DownloadPdfZip.jsx +++ b/apps/OpenSign/src/primitives/DownloadPdfZip.jsx @@ -91,7 +91,7 @@ function DownloadPdfZip(props) { title={t("download-files")} handleClose={() => props.setIsDownloadModal(false)} > - <div className="p-[20px] h-full"> + <div className="p-[20px] h-full text-base-content"> {downloadType.map((data, ind) => { return ( <label diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.jsx b/apps/OpenSign/src/primitives/GetReportDisplay.jsx index 8c7b15ee96..302f1a02ae 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.jsx +++ b/apps/OpenSign/src/primitives/GetReportDisplay.jsx @@ -7,6 +7,7 @@ import ModalUi from "./ModalUi"; import AddSigner from "../components/AddSigner"; import { emailRegex, + iconColor, } from "../constant/const"; import Alert from "./Alert"; import Tooltip from "./Tooltip"; @@ -38,9 +39,12 @@ import { useTranslation } from "react-i18next"; import DownloadPdfZip from "./DownloadPdfZip"; import * as XLSX from "xlsx"; import EditContactForm from "../components/EditContactForm"; +import { useElSize } from "../hook/useElSize"; const ReportTable = (props) => { const copyUrlRef = useRef(null); + const titleRef = useRef(null); + const titleElement = useElSize(titleRef); const appName = "OpenSign™"; const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; @@ -92,6 +96,12 @@ const ReportTable = (props) => { const startIndex = (currentPage - 1) * props.docPerPage; const { isMoreDocs, setIsNextRecord } = props; + useEffect(() => { + if (props.isSearchResult) { + setCurrentPage(1); + } + }, [props.isSearchResult]); + const getPaginationRange = () => { const totalPageNumbers = 7; // Adjust this value to show more/less page numbers const pages = []; @@ -907,7 +917,6 @@ const ReportTable = (props) => { setActLoader({}); } }; - const handleUpdateExpiry = async (e, item) => { e.preventDefault(); e.stopPropagation(); @@ -999,7 +1008,7 @@ const ReportTable = (props) => { ? "op-border-primary op-text-primary" : x.Activity === "VIEWED" ? "border-green-400 text-green-400" - : "border-black text-black" + : "border-base-content text-base-content" } focus:outline-none border-2 w-[60px] h-[30px] text-[11px] rounded-full`} > {x?.Activity?.toUpperCase() || "-"} @@ -1314,7 +1323,6 @@ const ReportTable = (props) => { try { const params = { docId: doc?.objectId }; const templateRes = await Parse.Cloud.run("saveastemplate", params); - // console.log("templateRes ", templateRes); setTemplateId(templateRes?.id); setIsSuccess({ [doc.objectId]: true }); } catch (err) { @@ -1394,6 +1402,12 @@ const ReportTable = (props) => { setActLoader({}); } }; + + const restrictBtn = (item, act) => { + return item.IsSignyourself && act.action === "recreatedocument" + ? true + : false; + }; return ( <div className="relative"> {Object.keys(actLoader)?.length > 0 && ( @@ -1416,7 +1430,10 @@ const ReportTable = (props) => { /> </> )} - <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> + <div + ref={titleRef} + className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]" + > <div className="font-light"> {t(`report-name.${props.ReportName}`)}{" "} {props.report_help && ( @@ -1428,26 +1445,68 @@ const ReportTable = (props) => { </span> )} </div> - <div className="flex flex-row justify-center items-center gap-3"> + <div className="flex flex-row justify-center items-center gap-3 mb-2"> + {/* Search input for report bigger in width */} + {titleElement?.width > 500 && ( + <div className="flex"> + <input + type="search" + value={props.searchTerm} + onChange={props.handleSearchChange} + placeholder={ + props.ReportName === "Contactbook" + ? t("search-contacts") + : isTemplateReport + ? t("search-templates") + : t("search-documents") + } + onPaste={props.handleSearchPaste} + className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-64 text-xs" + /> + </div> + )} + {/* contact import */} {props.isImport && ( - <div className="cursor-pointer" onClick={() => handleImportBtn()}> - <i className="fa-light fa-upload op-text-secondary text-[23px] md:text-[30px]"></i> + <div + className="cursor-pointer flex" + onClick={() => handleImportBtn()} + > + <i className="fa-light fa-upload op-text-secondary text-[23px] md:text-[25px]"></i> </div> )} + {/* add contact form */} {props.form && ( <div - className="cursor-pointer" + className="cursor-pointer flex" onClick={() => handleContactFormModal()} > - <i className="fa-light fa-square-plus text-accent text-[30px] md:text-[35px]"></i> + <i className="fa-light fa-square-plus text-accent text-[30px] md:text-[32px]"></i> </div> )} + {/* create template form */} {isTemplateReport && ( - <i + <div data-tut="reactourFirst" + className="cursor-pointer flex" onClick={() => navigate("/form/template")} - className="cursor-pointer fa-light fa-square-plus text-accent text-[30px] md:text-[35px]" - ></i> + > + <i className="cursor-pointer fa-light fa-square-plus text-accent text-[30px] md:text-[32px]"></i> + </div> + )} + {/* search icon/magnifer icon */} + {titleElement?.width < 500 && ( + <button + className="flex justify-center items-center focus:outline-none rounded-md text-[18px]" + aria-label="Search" + onClick={() => + props.setMobileSearchOpen(!props.mobileSearchOpen) + } + > + <i + style={{ color: `${iconColor}` }} + className="fa-solid fa-magnifying-glass" + ></i> + </button> )} <ModalUi isOpen={isModal?.export} @@ -1536,6 +1595,19 @@ const ReportTable = (props) => { </ModalUi> </div> </div> + {/* Search input for report smalle in width */} + {titleElement?.width < 500 && props.mobileSearchOpen && ( + <div className="top-full left-0 w-full bg-white px-3 pt-1 pb-3"> + <input + type="search" + value={props.searchTerm} + onChange={props.handleSearchChange} + placeholder={t("search-documents")} + onPaste={props.handleSearchPaste} + className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" + /> + </div> + )} <div className={`overflow-auto w-full border-b ${ props.List?.length > 0 @@ -1602,7 +1674,7 @@ const ReportTable = (props) => { handleClose={handleClose} > <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> + <div className="text-lg font-normal text-base-content"> {t("contact-delete-alert")} </div> <hr className="bg-[#ccc] mt-4 " /> @@ -1837,33 +1909,39 @@ const ReportTable = (props) => { {isOption[item.objectId] && act.action === "option" && ( <ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow-black/20 shadow bg-base-100 text-base-content rounded-box"> - {act.subaction?.map((subact) => ( - <li - key={subact.btnId} - onClick={() => - handleActionBtn( - subact, - item - ) - } - title={t( - `btnLabel.${subact.hoverLabel}` - )} - > - <span> - <i - className={`${subact.btnIcon} mr-1.5`} - ></i> - {subact.btnLabel && ( - <span className="text-[13px] capitalize font-medium"> - {t( - `btnLabel.${subact.btnLabel}` + {act.subaction?.map( + (subact) => + !restrictBtn( + item, + subact + ) && ( + <li + key={subact.btnId} + onClick={() => + handleActionBtn( + subact, + item + ) + } + title={t( + `btnLabel.${subact.hoverLabel}` + )} + > + <span> + <i + className={`${subact.btnIcon} mr-1.5`} + ></i> + {subact.btnLabel && ( + <span className="text-[13px] capitalize font-medium"> + {t( + `btnLabel.${subact.btnLabel}` + )} + </span> )} </span> - )} - </span> - </li> - ))} + </li> + ) + )} </ul> )} </div> @@ -1972,7 +2050,7 @@ const ReportTable = (props) => { </div> ) : ( <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> + <div className="text-lg font-normal text-base-content"> {t("save-as-template-?")} </div> <hr className="bg-[#ccc] mt-3" /> @@ -2010,7 +2088,7 @@ const ReportTable = (props) => { </label> <input type="date" - className="rounded-full mb-2 bg-base-300 w-full px-4 py-2 text-black border-2 hover:border-spacing-2" + className="rounded-full mb-2 bg-base-300 w-full px-4 py-2 text-base-content border-2 hover:border-spacing-2" defaultValue={ item?.ExpiryDate?.iso?.split("T")?.[0] } @@ -2120,7 +2198,7 @@ const ReportTable = (props) => { handleClose={handleClose} > <div className="m-[20px]"> - <div className="text-lg font-normal text-black"> + <div className="text-lg font-normal text-base-content"> {t("delete-document-alert")} </div> <hr className="bg-[#ccc] mt-4" /> @@ -2177,7 +2255,7 @@ const ReportTable = (props) => { {shareUrls.map((share, i) => ( <div key={i} - className="text-sm font-normal text-black flex my-2 justify-between items-center" + className="text-sm font-normal text-base-content flex my-2 justify-between items-center" > <span className="w-[150px] mr-[5px] md:mr-0 md:w-[300px] whitespace-nowrap overflow-hidden text-ellipsis text-sm font-semibold"> {share.email} @@ -2219,14 +2297,14 @@ const ReportTable = (props) => { handleClose={handleClose} > <div className="m-[20px]"> - <div className="text-sm md:text-lg font-normal text-black"> + <div className="text-sm md:text-lg font-normal text-base-content"> {t("revoke-document-alert")} </div> <div className="mt-2"> <textarea rows={3} placeholder="Reason (optional)" - className="px-4 op-textarea op-textarea-bordered focus:outline-none hover:border-base-content w-full text-xs" + className="px-4 op-textarea op-textarea-bordered text-base-content focus:outline-none hover:border-base-content w-full text-xs" value={reason} onChange={(e) => setReason(e.target.value)} ></textarea> @@ -2337,7 +2415,7 @@ const ReportTable = (props) => { )} {Object?.keys(isNextStep) <= 0 && ( <div className="flex justify-between items-center gap-2 my-2 px-3"> - <div className="text-black"> + <div className="text-base-content"> {user?.signerPtr?.Name || "-"}{" "} {`<${ user?.email diff --git a/apps/OpenSign/src/primitives/PdfDeclineModal.jsx b/apps/OpenSign/src/primitives/PdfDeclineModal.jsx index 3bef8d5431..1200581dda 100644 --- a/apps/OpenSign/src/primitives/PdfDeclineModal.jsx +++ b/apps/OpenSign/src/primitives/PdfDeclineModal.jsx @@ -43,7 +43,7 @@ function CustomModal(props) { </h3> {!isExtendExpiry && ( <div className="p-[10px] px-[20px]"> - <p className="text-[15px]">{props.bodyMssg && props.bodyMssg}</p> + <p className="text-[15px] text-base-content">{props.bodyMssg && props.bodyMssg}</p> </div> )} {!isExtendExpiry && ( @@ -68,7 +68,7 @@ function CustomModal(props) { )} {props.footerMessage && ( <> - <div className="mx-3"> + <div className="mx-3 text-base-content"> <textarea rows={3} placeholder="Reason (optional)" @@ -108,7 +108,7 @@ function CustomModal(props) { </label> <input type="date" - className="rounded-full bg-base-300 w-full px-4 py-2 text-black border-2 hover:border-spacing-2" + className="rounded-full bg-base-300 w-full px-4 py-2 text-base-content border-2 hover:border-spacing-2" defaultValue={props?.doc?.ExpiryDate?.iso?.split("T")?.[0]} onChange={(e) => setExpiryDate(e.target.value)} /> diff --git a/apps/OpenSign/src/styles/AddUser.css b/apps/OpenSign/src/styles/AddUser.css index 0ce531387c..f723dc0a25 100644 --- a/apps/OpenSign/src/styles/AddUser.css +++ b/apps/OpenSign/src/styles/AddUser.css @@ -10,6 +10,10 @@ background-position: right 0.7rem top 50%; background-size: 1rem auto; } +[data-theme="opensigndark"] .validationlist{ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAQ1JREFUSEvtlDtKBTEARc/Fys8OXumndhl2ggt4jTuwsVJQK92DtY1Yuw3bp2jlDvxUcn2BDIQ4k2Sa1ziBNMmdHO4JGbGCoRUwmCCjLP8DXbYPgD3gVtLnkB/bm8AxsJD02Jfr1WX7EJjHD16BC0kf+QG2N4BzYDfu3Um6z3N/ILb3gbMs+AJcpo1igwDYybIh95Su9UFmwPVyrg+BCoBv4FTSexESNm1vRw1bGegNuAFOEkVdJABCi0VVVxewHS49aAve0/EDrGVrX8u1qz5AyBXfSaFRyhhs0IWqj7ECKjZohhTuqAlQ1ZU6yRo1A0ZBYqPw6I6AB0nPrX/J6p20HlTKTZBRFiddo3T9ArZOWBrGcf52AAAAAElFTkSuQmCC"); +} + @media (max-width: 375px) { .validationlist { @@ -17,7 +21,7 @@ } } -@media (min-width:375px) and (max-width: 767px) { +@media (min-width: 375px) and (max-width: 767px) { .validationlist { background-position: right 1rem top 50%; } diff --git a/apps/OpenSign/src/styles/dark-theme-improvements.css b/apps/OpenSign/src/styles/dark-theme-improvements.css new file mode 100644 index 0000000000..11f530c57f --- /dev/null +++ b/apps/OpenSign/src/styles/dark-theme-improvements.css @@ -0,0 +1,143 @@ +/* VS Code Dark Theme Improvements for OpenSign */ + +/* Better disabled button styling for dark mode */ +[data-theme="opensigndark"] { + /* Primary button disabled state */ + .op-btn-primary:disabled { + background-color: #3C3C3C !important; + color: #CCCCCC !important; + border-color: #565656 !important; + opacity: 1 !important; + cursor: not-allowed !important; + } + + .op-btn-primary:disabled:hover { + background-color: #3C3C3C !important; + color: #CCCCCC !important; + border-color: #565656 !important; + transform: none !important; + box-shadow: none !important; + } + + /* Secondary button disabled state */ + .op-btn-secondary:disabled { + background-color: #2A2A2A !important; + color: #999999 !important; + border-color: #444444 !important; + opacity: 1 !important; + cursor: not-allowed !important; + } + + /* Ghost button disabled state */ + .op-btn-ghost:disabled { + background-color: transparent !important; + color: #666666 !important; + border-color: #444444 !important; + opacity: 1 !important; + cursor: not-allowed !important; + } + + /* Better icon visibility for various states */ + .icon-disabled, + .fa-light.text-gray-400, + .fa-light.text-gray-500 { + color: #858585 !important; + } + + .icon-visible, + .nav-icon, + .folder-icon { + color: #CCCCCC !important; + } + + /* Muted/inactive icons with better visibility */ + .muted-icon, + .inactive-icon { + color: #999999 !important; + } + + /* Hover states for better interactivity */ + .hover\\:bg-gray-200:hover { + background-color: #2A2A2A !important; + } + + .hover\\:text-gray-600:hover { + color: #E5E7EB !important; + } + + /* Form elements in disabled state */ + .op-input:disabled, + .op-select:disabled, + .op-textarea:disabled { + background-color: #2A2A2A !important; + color: #999999 !important; + border-color: #444444 !important; + cursor: not-allowed !important; + } + + /* Dropdown menu items */ + .dropdown-item { + color: #E5E7EB !important; + } + + .dropdown-item:hover { + background-color: #2A2A2A !important; + color: #FFFFFF !important; + } + + /* Better text contrast for various elements */ + .text-gray-600 { + color: #CCCCCC !important; + } + + .text-gray-500 { + color: #999999 !important; + } + + .text-gray-400 { + color: #858585 !important; + } + + /* Status indicators with better visibility */ + .status-badge { + box-shadow: 0 2px 4px rgba(255, 255, 255, 0.1) !important; + } + + /* Tooltip improvements */ + .op-tooltip { + background-color: #1F2937 !important; + color: #E5E7EB !important; + border-color: #4B5563 !important; + } + + /* Card and panel borders */ + .op-card, + .border-gray-300 { + border-color: #2C2C2C !important; + } + + /* Loading states */ + .opacity-50 { + opacity: 0.7 !important; + } + + /* Focus states for better accessibility */ + .op-btn:focus-visible, + .op-input:focus-visible, + .op-select:focus-visible { + outline: 2px solid #007ACC !important; + outline-offset: 2px !important; + } +} + +/* Ensure these styles don't affect light mode */ +[data-theme="opensigncss"] { + /* Keep original colors for light mode */ + .icon-disabled { + color: #9CA3AF; + } + + .icon-visible { + color: #6B7280; + } +} diff --git a/apps/OpenSign/src/styles/managesign.css b/apps/OpenSign/src/styles/managesign.css index a15c18073d..d8e749b2b9 100644 --- a/apps/OpenSign/src/styles/managesign.css +++ b/apps/OpenSign/src/styles/managesign.css @@ -1,3 +1,26 @@ +/* Dark mode support for custom warning in Managesign */ +[data-theme="opensigndark"] .customwarning { + background-color: #374151 !important; + color: #E5E7EB !important; + border-color: #4B5563 !important; +} + +[data-theme="opensigndark"] .customwarning::before { + border-color: transparent transparent #4B5563 transparent !important; +} + +/* Dark mode support for signature management warning */ +[data-theme="opensigndark"] .signWarning { + background-color: #374151 !important; + color: #E5E7EB !important; +} + +/* Ensure the Managesign page background is consistent */ +[data-theme="opensigndark"] .managesign-container { + background-color: #121212 !important; + color: #F3F4F6 !important; +} + .customwarning { position: absolute; padding: 8px; diff --git a/apps/OpenSign/src/styles/opensigndrive.css b/apps/OpenSign/src/styles/opensigndrive.css index bd1fd4ea84..8d167b030e 100644 --- a/apps/OpenSign/src/styles/opensigndrive.css +++ b/apps/OpenSign/src/styles/opensigndrive.css @@ -204,6 +204,15 @@ a { align-items: center; } +/* Dark mode support for HoverCard */ +[data-theme="opensigndark"] .HoverCardContent { + background-color: #1F2937; + color: #E5E7EB; + box-shadow: + hsl(0 0% 0% / 50%) 0px 10px 38px -10px, + hsl(0 0% 0% / 30%) 0px 10px 20px -15px; +} + .HoverCardContent[data-side="top"] { animation-name: slideDownAndFade; } @@ -224,6 +233,11 @@ a { fill: white; } +/* Dark mode support for HoverCard arrow */ +[data-theme="opensigndark"] .HoverCardArrow { + fill: #1F2937; +} + @keyframes slideUpAndFade { 0% { opacity: 0; diff --git a/apps/OpenSign/src/styles/signature.css b/apps/OpenSign/src/styles/signature.css index 61c8311b9b..2365a2a9de 100644 --- a/apps/OpenSign/src/styles/signature.css +++ b/apps/OpenSign/src/styles/signature.css @@ -6,7 +6,7 @@ .react-datepicker__input-container { position: initial !important; } -.select-none-cls{ +.select-none-cls { -webkit-user-select: none; /* Disable text selection in WebKit browsers */ -moz-user-select: none; @@ -33,10 +33,17 @@ width: 440px; height: 167px; } -.tabWidth{ +.tabWidth { + border: 1px solid #f3f4f6; + background-color: #f3f4f6; width: 440px; } +[data-theme="opensigndark"] .tabWidth { + border: 1px solid #1f2937 !important; + background-color: #1f2937 !important; +} + .intialSignatureCanvas { width: 150px; height: 150px; @@ -52,7 +59,6 @@ background-color: #111111; /* blue-500 */ } - .intialSignature { border: 2px solid #888; background-color: rgb(255, 255, 255); @@ -60,6 +66,40 @@ height: 183px; } +/* Dark mode support for initials box in /managesign */ +[data-theme="opensigndark"] .intialSignature { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + +[data-theme="opensigndark"] .intialSignatureCanvas { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + +/* Also support signature canvas for consistency */ +[data-theme="opensigndark"] .signatureCanvas { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + +/* Dark mode support for initials box in /managesign */ +[data-theme="opensigndark"] .intialSignature { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + +[data-theme="opensigndark"] .intialSignatureCanvas { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + +/* Also support signature canvas for consistency */ +[data-theme="opensigndark"] .signatureCanvas { + background-color: #1f2937 !important; + border-color: #4b5563 !important; +} + .penContainerDefault { width: 460px; } @@ -401,7 +441,7 @@ option { width: 300px; height: 120px; } - .tabWidth{ + .tabWidth { width: 300px; } @@ -427,7 +467,7 @@ option { width: 280px; height: 112px; } - .tabWidth{ + .tabWidth { width: 280px; } @@ -451,7 +491,7 @@ option { width: 230px; height: 92px; } - .tabWidth{ + .tabWidth { width: 230px; } diff --git a/apps/OpenSign/tailwind.config.js b/apps/OpenSign/tailwind.config.js index f7c10fe1d9..5dddda0da5 100644 --- a/apps/OpenSign/tailwind.config.js +++ b/apps/OpenSign/tailwind.config.js @@ -6,11 +6,67 @@ module.exports = { }, plugins: [ require("daisyui"), - function ({ addUtilities }) { + function ({ addUtilities, theme }) { addUtilities({ // Prevent iOS long-press popup ".touch-callout-none": { "-webkit-touch-callout": "none" + }, + // VS Code-style disabled button for all themes + ".op-btn-vscode-disabled": { + "background-color": "#3C3C3C !important", + color: "#CCCCCC !important", + "border-color": "#565656 !important", + cursor: "not-allowed !important", + opacity: "1 !important", + "&:hover": { + "background-color": "#3C3C3C !important", + color: "#CCCCCC !important", + "border-color": "#565656 !important", + transform: "none !important" + } + }, + // Dark mode icon improvements using DaisyUI theme detection + '[data-theme="opensigndark"] .icon-improved': { + color: "#CCCCCC !important" + }, + '[data-theme="opensigndark"] .icon-muted': { + color: "#999999 !important" + }, + '[data-theme="opensigndark"] .icon-disabled': { + color: "#858585 !important" + }, + // Gray text improvements for dark mode + '[data-theme="opensigndark"] .text-gray-500': { + color: "#CCCCCC !important" + }, + '[data-theme="opensigndark"] .text-gray-400': { + color: "#999999 !important" + }, + '[data-theme="opensigndark"] .text-gray-600': { + color: "#CCCCCC !important" + }, + // CSS variable utilities that work with arbitrary values + ".icon-themed": { + color: "var(--icon-color)" + }, + ".icon-themed-muted": { + color: "var(--icon-color-muted)" + }, + ".icon-themed-disabled": { + color: "var(--icon-color-disabled)" + }, + ".btn-themed-disabled": { + "background-color": "var(--btn-disabled-bg)", + color: "var(--btn-disabled-color)", + "border-color": "var(--btn-disabled-border)", + cursor: "not-allowed", + "&:hover": { + "background-color": "var(--btn-disabled-bg)", + color: "var(--btn-disabled-color)", + "border-color": "var(--btn-disabled-border)", + transform: "none" + } } }); } @@ -18,7 +74,48 @@ module.exports = { daisyui: { // themes: true, themes: [ - "dark", + { + opensigndark: { + primary: "#007ACC", // VS Code blue - CTA & highlight color + "primary-content": "#FFFFFF", + + secondary: "#1F2937", // Sidebar background (darker slate) + "secondary-content": "#E5E7EB", + + accent: "#4A9EFF", // Lighter VS Code blue for hover, minor CTA + "accent-content": "#FFFFFF", + + neutral: "#3C3C3C", // VS Code inactive/disabled element background + "neutral-content": "#CCCCCC", // VS Code inactive text color + + "base-100": "#121212", // App background + "base-200": "#181818", // Slight elevation (cards) + "base-300": "#1E1E1E", // Further elevated items (panels) + "base-content": "#F3F4F6", // Main text color (soft white) + + info: "#2563EB", // For info panels like "Out for signature" + success: "#22C55E", // Optional: for completed docs or alerts + warning: "#FBBF24", + error: "#EF4444", + + "--rounded-btn": "1.9rem", + "--tab-border": "2px", + "--tab-radius": "0.7rem", + + // Custom CSS variables for icon and button states + "--icon-color": "#CCCCCC", + "--icon-color-muted": "#999999", + "--icon-color-disabled": "#858585", + "--btn-disabled-bg": "#3C3C3C", + "--btn-disabled-color": "#CCCCCC", + "--btn-disabled-border": "#565656", + + // Optional polish + "--navbar-padding": "0.8rem", + "--border-color": "#2C2C2C", // Card/table separation + "--tooltip-color": "#1F2937" + } + }, { opensigncss: { primary: "#002864", diff --git a/apps/OpenSignServer/Dockerhubfile b/apps/OpenSignServer/Dockerhubfile index 45a2aea7ad..459562228e 100644 --- a/apps/OpenSignServer/Dockerhubfile +++ b/apps/OpenSignServer/Dockerhubfile @@ -2,6 +2,11 @@ FROM node:22.14.0 +# Install LibreOffice for DOCX to PDF conversions +RUN apt-get update \ + && apt-get install -y libreoffice \ + && rm -rf /var/lib/apt/lists/* + # Set the working directory inside the container WORKDIR /usr/src/app diff --git a/apps/OpenSignServer/cloud/customRoute/customApp.js b/apps/OpenSignServer/cloud/customRoute/customApp.js index 98ae7cb11e..2fdb0a21e9 100644 --- a/apps/OpenSignServer/cloud/customRoute/customApp.js +++ b/apps/OpenSignServer/cloud/customRoute/customApp.js @@ -2,6 +2,8 @@ import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import uploadFile from './uploadFile.js'; +import docxtopdf, { upload as docxUpload } from './docxtopdf.js'; +import decryptpdf, { upload as decryptUpload } from './decryptpdf.js'; export const app = express(); @@ -11,4 +13,5 @@ app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); app.post('/file_upload', uploadFile); - +app.post('/docxtopdf', docxUpload.single('file'), docxtopdf); +app.post('/decryptpdf', decryptUpload.single('file'), decryptpdf); diff --git a/apps/OpenSignServer/cloud/customRoute/decryptpdf.js b/apps/OpenSignServer/cloud/customRoute/decryptpdf.js new file mode 100644 index 0000000000..f8aa99c374 --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/decryptpdf.js @@ -0,0 +1,43 @@ +import fs from 'node:fs'; +import multer from 'multer'; +import Coherentpdf from 'coherentpdf'; + +const storage = multer.diskStorage({ + destination(req, file, cb) { + cb(null, 'exports'); + }, + filename(req, file, cb) { + cb(null, file.originalname); + }, +}); + +export const upload = multer({ storage }); + +export default async function decryptpdf(req, res) { + const inputPath = req.file.path; + const password = req.body.password || ''; + try { + const file = fs.readFileSync(inputPath); + const pdf = await Coherentpdf.fromMemory(file, password); + await Coherentpdf.decryptPdf(pdf, password); + // Get decrypted buffer directly from memory (no file I/O) + const buffer = await Coherentpdf.toMemory(pdf, false, false); + res.set({ + 'Content-Type': 'application/pdf', + 'Content-Disposition': 'inline; filename="decrypted.pdf"', + 'Content-Length': buffer.length, + }); + res.send(buffer); + fs.unlink(inputPath, () => {}); + } catch (err) { + fs.unlink(inputPath, () => {}); + console.log('Error in decrypt file: ', err); + let code = err?.code ? err.code : 400; + let message = err?.[2]?.c ? err[2].c : 'Something went wrong.'; + if (err?.[2]?.c?.includes('Bad password') || err?.[2]?.c?.includes('decrypt_pdf_inner')) { + code = 401; + message = 'Incorrect password.'; + } + return res.status(code).json({ error: message }); + } +} diff --git a/apps/OpenSignServer/cloud/customRoute/docxtopdf.js b/apps/OpenSignServer/cloud/customRoute/docxtopdf.js new file mode 100644 index 0000000000..76f8f3567f --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/docxtopdf.js @@ -0,0 +1,166 @@ +import fs from 'node:fs'; +import axios from 'axios'; +import multer from 'multer'; +import libre from 'libreoffice-convert'; +import util from 'node:util'; +import { cloudServerUrl, getSecureUrl } from '../../Utils.js'; + +libre.convertAsync = util.promisify(libre.convert); + +const storage = multer.diskStorage({ + destination(req, file, cb) { + cb(null, 'exports'); + }, + filename(req, file, cb) { + cb(null, file.originalname); + }, +}); + +export const upload = multer({ storage }); + +function generatePdfName(length) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; +} + +export default async function docxtopdf(req, res) { + const serverUrl = cloudServerUrl; + const appId = process.env.APP_ID; + const masterKey = process.env.MASTER_KEY; + const inputPath = req.file.path; + const name = generatePdfName(16); + const fileName = `${name}.pdf`; + const outputPath = './exports/output.pdf'; + + try { + const userRes = await axios.get(serverUrl + '/users/me', { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Session-Token': req.headers['sessiontoken'], + }, + }); + const userId = JSON.stringify({ + UserId: { + __type: 'Pointer', + className: '_User', + objectId: userRes.data.objectId, + }, + }); + const resUser = await axios.get( + serverUrl + `/classes/contracts_Users?where=${userId}&limit=1&include=TenantId`, + { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Master-Key': masterKey, + }, + } + ); + + if (resUser?.data?.results?.length > 0) { + const tenantId = resUser.data.results[0].TenantId?.objectId; + const ext = '.pdf'; + const outPath = `./exports/output${ext}`; + const docxBuf = fs.readFileSync(inputPath); + const pdfBuffer = await libre.convertAsync(docxBuf, ext, undefined); + fs.writeFileSync(outPath, pdfBuffer); + const file = fs.readFileSync(outPath); + const size = fs.statSync(outPath).size; + const PartnersTenant = JSON.stringify({ + PartnersTenant: { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenantId, + }, + }); + const resTenantCredit = await axios.get( + serverUrl + `/classes/partners_TenantCredits?where=${PartnersTenant}&limit=1`, + { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Master-Key': masterKey, + }, + } + ); + if (resTenantCredit.data?.results?.length > 0) { + const tenantCreditsId = resTenantCredit.data.results[0].objectId; + const activeFileAdapter = resUser.data.results[0].TenantId?.ActiveFileAdapter; + let fileUrl; + if (activeFileAdapter) { + const params = { + fileBase64: file.toString('base64'), + fileName, + id: activeFileAdapter, + }; + const url = serverUrl + '/functions/savetofileadapter'; + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': appId, + 'X-Parse-Session-Token': req.headers['sessiontoken'], + }; + try { + const savetos3 = await axios.post(url, params, { headers }); + fileUrl = savetos3?.data?.result?.url; + } catch (err) { + console.log('err in save to customfile', err); + } + } else { + const parsefile = await axios.post(serverUrl + `/files/${fileName}`, file, { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Master-Key': masterKey, + 'Content-Type': 'application/pdf', + }, + }); + const fileRes = getSecureUrl(parsefile.data.url); + fileUrl = fileRes.url; + } + const usedStorage = resTenantCredit.data.results[0].usedStorage + ? resTenantCredit.data.results[0].usedStorage + size + : size; + await axios.put( + serverUrl + `/classes/partners_TenantCredits/${tenantCreditsId}`, + { usedStorage }, + { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Master-Key': masterKey, + }, + } + ); + await axios.post( + serverUrl + '/classes/partners_DataFiles', + { + FileSize: size, + FileUrl: fileUrl, + TenantPtr: { + __type: 'Pointer', + className: 'partners_Tenant', + objectId: tenantId, + }, + }, + { + headers: { + 'X-Parse-Application-Id': appId, + 'X-Parse-Master-Key': masterKey, + }, + } + ); + [inputPath, outPath].forEach(p => fs.existsSync(p) && fs.unlinkSync(p)); + return res.status(200).json({ message: 'success.', url: fileUrl }); + } + } + } catch (err) { + [inputPath, outputPath].forEach(p => fs.existsSync(p) && fs.unlinkSync(p)); + const msg = + err?.response?.data?.error || err?.response?.data || err?.message || 'Something went wrong.'; + console.log(`Error converting file: ${msg}`); + + const message = + 'We are currently experiencing some issues with processing DOCX files. Please upload the PDF version or contact us on support@opensignlabs.com'; + return res.status(400).json({ error: message }); + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js index 2d4ce58996..0ac40523f4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -15,8 +15,6 @@ async function deductcount(docsCount, extUserId) { async function sendMail(document, publicUrl) { //sessionToken const baseUrl = new URL(publicUrl); - - // console.log("pdfDetails", pdfDetails); const timeToCompleteDays = document?.TimeToCompleteDays || 15; const ExpireDate = new Date(document.createdAt); ExpireDate.setDate(ExpireDate.getDate() + timeToCompleteDays); @@ -168,9 +166,9 @@ async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) { })), ACL: Acl, SentToOthers: true, - RemindOnceInEvery: x.RemindOnceInEvery || 5, + RemindOnceInEvery: x.RemindOnceInEvery ? parseInt(x.RemindOnceInEvery) : 5, AutomaticReminders: x.AutomaticReminders || false, - TimeToCompleteDays: x.TimeToCompleteDays || 15, + TimeToCompleteDays: x.TimeToCompleteDays ? parseInt(x.TimeToCompleteDays) : 15, OriginIp: Ip, DocSentAt: { __type: 'Date', iso: isoDate }, IsEnableOTP: x?.IsEnableOTP || false, @@ -231,6 +229,7 @@ export default async function createBatchDocs(request) { const sessionToken = request.headers?.sessiontoken; const type = request.headers?.type || 'quicksend'; const Documents = JSON.parse(strDocuments); + const Ip = request?.headers?.['x-real-ip'] || ''; // Access the host from the headers const publicUrl = request.headers.public_url; diff --git a/apps/OpenSignServer/cloud/parsefunction/generateCertificatebydocId.js b/apps/OpenSignServer/cloud/parsefunction/generateCertificatebydocId.js index 84a2f32df6..5c322ae460 100644 --- a/apps/OpenSignServer/cloud/parsefunction/generateCertificatebydocId.js +++ b/apps/OpenSignServer/cloud/parsefunction/generateCertificatebydocId.js @@ -65,7 +65,7 @@ export default async function generateCertificatebydocId(req) { const certificate = await GenerateCertificate(doc); const certificatePdf = await PDFDocument.load(certificate); const p12 = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null }); - // `pdflibAddPlaceholder` is used to add code of only digitial sign in certificate + // `pdflibAddPlaceholder` is used to add code of only digital sign in certificate pdflibAddPlaceholder({ pdfDoc: certificatePdf, reason: `Digitally signed by ${eSignName}.`, diff --git a/apps/OpenSignServer/cloud/parsefunction/getReport.js b/apps/OpenSignServer/cloud/parsefunction/getReport.js index a55496c6e0..85eb6f6622 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getReport.js +++ b/apps/OpenSignServer/cloud/parsefunction/getReport.js @@ -2,19 +2,26 @@ import { cloudServerUrl } from '../../Utils.js'; import reportJson from './reportsJson.js'; import axios from 'axios'; +// Escape regex special characters. Copied from filterDocs.js +function escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + export default async function getReport(request) { const reportId = request.params.reportId; const limit = request.params.limit; const skip = request.params.skip; + const searchTerm = request.params.searchTerm || ''; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const appId = process.env.APP_ID; const masterKey = process.env.MASTER_KEY; + const sessionToken = request.headers['sessiontoken'] || request.headers['x-parse-session-token']; try { const userRes = await axios.get(serverUrl + '/users/me', { headers: { 'X-Parse-Application-Id': appId, - 'X-Parse-Session-Token': request.headers['sessiontoken'], + 'X-Parse-Session-Token': sessionToken, }, }); const userId = userRes.data && userRes.data.objectId; @@ -25,7 +32,7 @@ export default async function getReport(request) { const { params, keys } = json; const orderBy = '-updatedAt'; const strKeys = keys.join(); - let strParams = JSON.stringify(params); + let paramsObj = { ...params }; if (reportId == '6TeaPr321t') { const extUserQuery = new Parse.Query('contracts_Users'); extUserQuery.equalTo('Email', userRes.data.email); @@ -36,8 +43,8 @@ export default async function getReport(request) { if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) { let teamArr = []; _extUser?.TeamIds?.forEach(x => (teamArr = [...teamArr, ...x.Ancestors])); - strParams = JSON.stringify({ - ...params, + paramsObj = { + ...paramsObj, $or: [ { SharedWith: { $in: teamArr } }, { @@ -55,15 +62,23 @@ export default async function getReport(request) { }, }, ], - }); + }; } else { - strParams = JSON.stringify({ - ...params, + paramsObj = { + ...paramsObj, CreatedBy: { __type: 'Pointer', className: '_User', objectId: userId }, - }); + }; } } } + if (searchTerm) { + const escaped = escapeRegExp(searchTerm); + paramsObj = { + ...paramsObj, + Name: { $regex: `.*${escaped}.*`, $options: 'i' }, + }; + } + const strParams = JSON.stringify(paramsObj); const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId, diff --git a/apps/OpenSignServer/cloud/parsefunction/recreateDocument.js b/apps/OpenSignServer/cloud/parsefunction/recreateDocument.js index 16dbbf7c31..8ddcaf26e6 100644 --- a/apps/OpenSignServer/cloud/parsefunction/recreateDocument.js +++ b/apps/OpenSignServer/cloud/parsefunction/recreateDocument.js @@ -14,11 +14,14 @@ export default async function recreateDocument(request) { if (!doc) { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found'); } + if (doc?.get('IsSignyourself')) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Signyourself Document not allowed'); + } const _docRes = doc?.toJSON(); const { objectId, SignedUrl, AuditTrail, ACL, DeclineBy, DeclineReason, ...docRes } = _docRes; const createDoc = new Parse.Object('contracts_Document'); Object.entries(docRes).forEach(([key, value]) => { - if (key === 'IsDeclined') { + if (key === 'IsDeclined' || key === 'IsCompleted') { createDoc.set(key, false); } else { createDoc.set(key, value); diff --git a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js index 387387ac86..2a2e796510 100644 --- a/apps/OpenSignServer/cloud/parsefunction/reportsJson.js +++ b/apps/OpenSignServer/cloud/parsefunction/reportsJson.js @@ -55,7 +55,7 @@ export default function reportJson(id, userId) { 'ExpiryDate', ], }; - // In progess report + // In progress report case '1MwEuxLEkF': return { reportName: 'In-progress documents', @@ -123,6 +123,8 @@ export default function reportJson(id, userId) { 'TimeToCompleteDays', 'IsSignyourself', 'IsCompleted', + 'ExpiryDate', + 'IsSignyourself', ], }; // declined documents report diff --git a/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js b/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js index 85433c476a..53708732dd 100644 --- a/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js +++ b/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js @@ -51,6 +51,17 @@ export default async function saveAsTemplate(request) { if (_docRes?.Placeholders?.length > 0) { if (_docRes?.IsSignyourself) { + //add required option for all widget when save as template using signyour-self draft document + const updatedPlaceholder = _docRes?.Placeholders.map(pageItem => ({ + ...pageItem, + pos: pageItem.pos.map(p => ({ + ...p, + options: { + ...p.options, + status: 'required', + }, + })), + })); const placeHolders = { signerObjId: '', signerPtr: {}, @@ -58,7 +69,7 @@ export default async function saveAsTemplate(request) { blockColor: '#93a3db', Role: 'Role 1', email: '', - placeHolder: _docRes?.Placeholders, + placeHolder: updatedPlaceholder, }; templateCls.set('Placeholders', [placeHolders]); } else { diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js b/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js index b02905d9c2..fb5c85f64f 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js @@ -53,10 +53,16 @@ const makeEmail = async ( const isSecure = new URL(url)?.protocol === 'https:' && new URL(url)?.hostname !== 'localhost'; if (isSecure) { - https.get(url, async function (response) { - response.pipe(Pdf); - response.on('end', () => resolve('success')); - }); + https + .get(url, async function (response) { + response.pipe(Pdf); + Pdf.on('finish', () => resolve('success')); + Pdf.on('error', () => resolve('error')); + }) + .on('error', e => { + console.error(`error: ${e.message}`); + resolve('error'); + }); } else { const httpsAgent = new https.Agent({ rejectUnauthorized: false }); // Disable SSL validation axios diff --git a/apps/OpenSignServer/package-lock.json b/apps/OpenSignServer/package-lock.json index 775af1d104..0656eda38b 100644 --- a/apps/OpenSignServer/package-lock.json +++ b/apps/OpenSignServer/package-lock.json @@ -9,23 +9,25 @@ "version": "2.21.1", "license": "MIT", "dependencies": { - "@aws-sdk/client-s3": "^3.824.0", - "@aws-sdk/s3-request-presigner": "^3.824.0", + "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.828.0", "@parse/fs-files-adapter": "^3.0.0", - "@parse/s3-files-adapter": "^4.1.1", + "@parse/s3-files-adapter": "^4.2.0", "@pdf-lib/fontkit": "^1.1.1", "@signpdf/placeholder-pdf-lib": "^3.2.6", "@signpdf/signer-p12": "^3.2.4", "@signpdf/signpdf": "^3.2.5", "aws-sdk": "^2.1692.0", - "axios": "^1.9.0", + "axios": "^1.10.0", + "coherentpdf": "^2.5.5", "cors": "^2.8.5", "date-fns-tz": "^3.2.0", "dotenv": "^16.5.0", "express": "^5.1.0", - "form-data": "^4.0.2", + "form-data": "^4.0.3", "generate-api-key": "^1.0.2", - "googleapis": "^149.0.0", + "googleapis": "^150.0.1", + "libreoffice-convert": "^1.6.1", "mailgun.js": "^12.0.2", "mongodb": "^6.17.0", "multer": "^2.0.1", @@ -37,7 +39,7 @@ "parse-server": "^8.2.1", "parse-server-api-mail-adapter": "^4.1.0", "pdf-lib": "^1.17.1", - "posthog-node": "^4.18.0", + "posthog-node": "^5.1.0", "qrcode": "^1.5.4", "rate-limiter-flexible": "^7.1.1", "speakeasy": "^2.0.0", @@ -45,9 +47,9 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.27.5", - "eslint": "^9.28.0", - "jasmine": "^5.7.1", - "mongodb-runner": "^5.8.3", + "eslint": "^9.29.0", + "jasmine": "^5.8.0", + "mongodb-runner": "^5.9.0", "nodemon": "^3.1.10", "nyc": "^17.1.0", "prettier": "^3.5.3" @@ -809,35 +811,35 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.824.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.824.0.tgz", - "integrity": "sha512-7neTQIdSVP/F4RTWG5T87LDpB955iQD6lxg9nJ00fdkIPczDcRtAEXow44NjF4fEdpQ1A9jokUtBSVE+GMXZ/A==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.828.0.tgz", + "integrity": "sha512-TvFyrEfJkf9NN3cq5mXCgFv/sPaA8Rm5tEPgV5emuLedeGsORlWmVpdSKqfZ4lSoED1tMfNM6LY4uA9D8/RS5g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.823.0", - "@aws-sdk/credential-provider-node": "3.823.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-node": "3.828.0", "@aws-sdk/middleware-bucket-endpoint": "3.821.0", "@aws-sdk/middleware-expect-continue": "3.821.0", - "@aws-sdk/middleware-flexible-checksums": "3.823.0", + "@aws-sdk/middleware-flexible-checksums": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-location-constraint": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-s3": "3.823.0", + "@aws-sdk/middleware-sdk-s3": "3.826.0", "@aws-sdk/middleware-ssec": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/signature-v4-multi-region": "3.824.0", + "@aws-sdk/signature-v4-multi-region": "3.826.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.823.0", + "@aws-sdk/util-user-agent-node": "3.828.0", "@aws-sdk/xml-builder": "3.821.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.1", + "@smithy/core": "^3.5.3", "@smithy/eventstream-serde-browser": "^4.0.4", "@smithy/eventstream-serde-config-resolver": "^4.1.2", "@smithy/eventstream-serde-node": "^4.0.4", @@ -848,21 +850,21 @@ "@smithy/invalid-dependency": "^4.0.4", "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.9", - "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.17", - "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", @@ -1232,44 +1234,44 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.823.0.tgz", - "integrity": "sha512-dBWdsbyGw8rPfdCsZySNtTOGQK4EZ8lxB/CneSQWRBPHgQ+Ys88NXxImO8xfWO7Itt1eh8O7UDTZ9+smcvw2pw==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.828.0.tgz", + "integrity": "sha512-qxw8JcPTaFaBwTBUr4YmLajaMh3En65SuBWAKEtjctbITRRekzR7tvr/TkwoyVOh+XoAtkwOn+BQeQbX+/wgHw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.823.0", + "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.1", + "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.9", - "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.17", - "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", @@ -1637,19 +1639,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.823.0.tgz", - "integrity": "sha512-1Cf4w8J7wYexz0KU3zpaikHvldGXQEjFldHOhm0SBGRy7qfYNXecfJAamccF7RdgLxKGgkv5Pl9zX/Z/DcW9zg==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.826.0.tgz", + "integrity": "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.5.1", + "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", @@ -2019,12 +2021,12 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.823.0.tgz", - "integrity": "sha512-AIrLLwumObge+U1klN4j5ToIozI+gE9NosENRyHe0GIIZgTLOG/8jxrMFVYFeNHs7RUtjDTxxewislhFyGxJ/w==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.826.0.tgz", + "integrity": "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", @@ -2060,18 +2062,18 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.823.0.tgz", - "integrity": "sha512-u4DXvB/J/o2bcvP1JP6n3ch7V3/NngmiJFPsM0hKUyRlLuWM37HEDEdjPRs3/uL/soTxrEhWKTA9//YVkvzI0w==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.826.0.tgz", + "integrity": "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" @@ -2437,18 +2439,18 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.823.0.tgz", - "integrity": "sha512-C0o63qviK5yFvjH9zKWAnCUBkssJoQ1A1XAHe0IAQkurzoNBSmu9oVemqwnKKHA4H6QrmusaEERfL00yohIkJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.823.0", - "@aws-sdk/credential-provider-env": "3.823.0", - "@aws-sdk/credential-provider-http": "3.823.0", - "@aws-sdk/credential-provider-process": "3.823.0", - "@aws-sdk/credential-provider-sso": "3.823.0", - "@aws-sdk/credential-provider-web-identity": "3.823.0", - "@aws-sdk/nested-clients": "3.823.0", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.828.0.tgz", + "integrity": "sha512-T3DJMo2/j7gCPpFg2+xEHWgua05t8WP89ye7PaZxA2Fc6CgScHkZsJZTri1QQIU2h+eOZ75EZWkeFLIPgN0kRQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.826.0", + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.828.0", + "@aws-sdk/credential-provider-web-identity": "3.828.0", + "@aws-sdk/nested-clients": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", @@ -2499,17 +2501,17 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.823.0.tgz", - "integrity": "sha512-nfSxXVuZ+2GJDpVFlflNfh55Yb4BtDsXLGNssXF5YU6UgSPsi8j2YkaE92Jv2s7dlUK07l0vRpLyPuXMaGeiRQ==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.828.0.tgz", + "integrity": "sha512-9z3iPwVYOQYNzVZj8qycZaS/BOSKRXWA+QVNQlfEnQ4sA4sOcKR4kmV2h+rJcuBsSFfmOF62ZDxyIBGvvM4t/w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.823.0", - "@aws-sdk/credential-provider-http": "3.823.0", - "@aws-sdk/credential-provider-ini": "3.823.0", - "@aws-sdk/credential-provider-process": "3.823.0", - "@aws-sdk/credential-provider-sso": "3.823.0", - "@aws-sdk/credential-provider-web-identity": "3.823.0", + "@aws-sdk/credential-provider-env": "3.826.0", + "@aws-sdk/credential-provider-http": "3.826.0", + "@aws-sdk/credential-provider-ini": "3.828.0", + "@aws-sdk/credential-provider-process": "3.826.0", + "@aws-sdk/credential-provider-sso": "3.828.0", + "@aws-sdk/credential-provider-web-identity": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", @@ -2560,12 +2562,12 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.823.0.tgz", - "integrity": "sha512-U/A10/7zu2FbMFFVpIw95y0TZf+oYyrhZTBn9eL8zgWcrYRqxrxdqtPj/zMrfIfyIvQUhuJSENN4dx4tfpCMWQ==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.826.0.tgz", + "integrity": "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -2615,14 +2617,14 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.823.0.tgz", - "integrity": "sha512-ff8IM80Wqz1V7VVMaMUqO2iR417jggfGWLPl8j2l7uCgwpEyop1ZZl5CFVYEwSupRBtwp+VlW1gTCk7ke56MUw==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.828.0.tgz", + "integrity": "sha512-9CEAXzUDSzOjOCb3XfM15TZhTaM+l07kumZyx2z8NC6T2U4qbCJqn4h8mFlRvYrs6cBj2SN40sD3r5Wp0Cq2Kw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.823.0", - "@aws-sdk/core": "3.823.0", - "@aws-sdk/token-providers": "3.823.0", + "@aws-sdk/client-sso": "3.828.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/token-providers": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -2672,13 +2674,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.823.0.tgz", - "integrity": "sha512-lzoZdJMQq9w7i4lXVka30cVBe/dZoUDZST8Xz/soEd73gg7RTKgG+0szL4xFWgdBDgcJDWLfZfJzlbyIVyAyOA==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.828.0.tgz", + "integrity": "sha512-MguDhGHlQBeK9CQ/P4NOY0whAJ4HJU4x+f1dphg3I1sGlccFqfB8Moor2vXNKu0Th2kvAwkn9pr7gGb/+NGR9g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", - "@aws-sdk/nested-clients": "3.823.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", @@ -2875,15 +2877,15 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.823.0.tgz", - "integrity": "sha512-Elt6G1ryEEdkrppqbyJON0o2x4x9xKknimJtMLdfG1b4YfO9X+UB31pk4R2SHvMYfrJ+p8DE2jRAhvV4g/dwIQ==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.826.0.tgz", + "integrity": "sha512-Fz9w8CFYPfSlHEB6feSsi06hdS+s+FB8k5pO4L7IV0tUa78mlhxF/VNlAJaVWYyOkZXl4HPH2K48aapACSQOXw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.1.3", @@ -3264,19 +3266,19 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.823.0.tgz", - "integrity": "sha512-UV755wt2HDru8PbxLn2S0Fvwgdn9mYamexn31Q6wyUGQ6rkpjKNEzL+oNDGQQmDQAOcQO+nLubKFsCwtBM02fQ==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.826.0.tgz", + "integrity": "sha512-8F0qWaYKfvD/de1AKccXuigM+gb/IZSncCqxdnFWqd+TFzo9qI9Hh+TpUhWOMYSgxsMsYQ8ipmLzlD/lDhjrmA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.5.1", + "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", @@ -3671,15 +3673,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.823.0.tgz", - "integrity": "sha512-TKRQK09ld1LrIPExC9rIDpqnMsWcv+eq8ABKFHVo8mDLTSuWx/IiQ4eCh9T5zDuEZcLY4nNYCSzXKqw6XKcMCA==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.828.0.tgz", + "integrity": "sha512-nixvI/SETXRdmrVab4D9LvXT3lrXkwAWGWk2GVvQvzlqN1/M/RfClj+o37Sn4FqRkGH9o9g7Fqb1YqZ4mqDAtA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", - "@smithy/core": "^3.5.1", + "@aws-sdk/util-endpoints": "3.828.0", + "@smithy/core": "^3.5.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -3927,44 +3929,44 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.823.0.tgz", - "integrity": "sha512-/BcyOBubrJnd2gxlbbmNJR1w0Z3OVN/UE8Yz20e+ou+Mijjv7EbtVwmWvio1e3ZjphwdA8tVfPYZKwXmrvHKmQ==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.828.0.tgz", + "integrity": "sha512-xmeOILiR9LvfC8MctgeRXXN8nQTwbOvO4wHvgE8tDRsjnBpyyO0j50R4+viHXdMUGtgGkHEXRv8fFNBq54RgnA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.823.0", + "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.823.0", + "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.5.1", + "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.9", - "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.17", - "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-defaults-mode-browser": "^4.0.19", + "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", @@ -4415,17 +4417,17 @@ } }, "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.824.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.824.0.tgz", - "integrity": "sha512-r8NueKxJaoWbZTnfENmIeoDFjdYbgA9sxALrT1mDKU6+sHeAMNZLJfgEtSFKm7CjVmmdk2ZbYblrP3DY9Ftqsg==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.828.0.tgz", + "integrity": "sha512-6817h11Xi6LqnmTnHIwZf4PQB0rIMaRFwkq8/mfR9oOn+hsahxBVDbpgu+q4xzP5q+W3m5Y/din0cJPVrnP6yQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.824.0", + "@aws-sdk/signature-v4-multi-region": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-format-url": "3.821.0", - "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-endpoint": "^4.1.11", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.1", + "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -4790,12 +4792,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.824.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.824.0.tgz", - "integrity": "sha512-HBjuWeN6Z1pvJjUvGXdMNLwEypKKB4km6zXj9jsbOOwP8NTL6J5rY+JmlX/mfBTmvzmI0kMu2bxlQ4ME2CIRbA==", + "version": "3.826.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz", + "integrity": "sha512-3fEi/zy6tpMzomYosksGtu7jZqGFcdBXoL7YRsG7OEeQzBbOW9B+fVaQZ4jnsViSjzA/yKydLahMrfPnt+iaxg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.823.0", + "@aws-sdk/middleware-sdk-s3": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", @@ -4832,13 +4834,13 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.823.0.tgz", - "integrity": "sha512-vz6onCb/+g4y+owxGGPMEMdN789dTfBOgz/c9pFv0f01840w9Rrt46l+gjQlnXnx+0KG6wNeBIVhFdbCfV3HyQ==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.828.0.tgz", + "integrity": "sha512-JdOjI/TxkfQpY/bWbdGMdCiePESXTbtl6MfnJxz35zZ3tfHvBnxAWCoYJirdmjzY/j/dFo5oEyS6mQuXAG9w2w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.823.0", - "@aws-sdk/nested-clients": "3.823.0", + "@aws-sdk/core": "3.826.0", + "@aws-sdk/nested-clients": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -4925,9 +4927,9 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", - "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.828.0.tgz", + "integrity": "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.821.0", @@ -5040,12 +5042,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.823.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.823.0.tgz", - "integrity": "sha512-WvNeRz7HV3JLBVGTXW4Qr5QvvWY0vtggH5jW/NqHFH+ZEliVQaUIJ/HNLMpMoCSiu/DlpQAyAjRZXAptJ0oqbw==", + "version": "3.828.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.828.0.tgz", + "integrity": "sha512-LdN6fTBzTlQmc8O8f1wiZN0qF3yBWVGis7NwpWK7FUEzP9bEZRxYfIkV9oV9zpt6iNRze1SedK3JQVB/udxBoA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", @@ -5484,9 +5486,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -5554,9 +5556,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", - "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", + "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6246,23 +6248,23 @@ "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md" }, "node_modules/@mongodb-js/mongodb-downloader": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.3.9.tgz", - "integrity": "sha512-6lEIESINiIAeQUw95+hkfxG6129r6KiPU2TNOcxb30PsGgFHPJFg7QY8UoSQXjDE9YaENlr6oQm3c1XDixWeEg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-0.4.0.tgz", + "integrity": "sha512-+xBDhZQn+tGY8bWZo9gE+Nd6Mw2r6TQWaLbgGw/H50M5ky092IS4vbTjwvJhR2m/efoxj31LZpZI0PYK7Pzu6w==", "dev": true, "license": "Apache-2.0", "dependencies": { "debug": "^4.4.0", "decompress": "^4.2.1", - "mongodb-download-url": "^1.5.7", + "mongodb-download-url": "^1.6.0", "node-fetch": "^2.7.0", "tar": "^6.1.15" } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", - "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -6643,12 +6645,12 @@ } }, "node_modules/@parse/s3-files-adapter": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@parse/s3-files-adapter/-/s3-files-adapter-4.1.1.tgz", - "integrity": "sha512-fEC8Fa1VcAuLnPyGLDBV+QCQK71uin7PcU5KbFU31K1t367Sitg0bpxyoN9WLyQ6K5gbx0bOcnijeTeHpQ2fnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@parse/s3-files-adapter/-/s3-files-adapter-4.2.0.tgz", + "integrity": "sha512-owHi9CBZ43FkKFdVifDgtOrDF2vcipVrIhCExTuVWCLd69CooxXo7IlaUMPzG1Yi9/ivF4M69WarqFQo0ZQbiA==", "license": "ISC", "dependencies": { - "@aws-sdk/client-s3": "3.806.0", + "@aws-sdk/client-s3": "3.824.0", "@aws-sdk/s3-request-presigner": "3.787.0" }, "engines": { @@ -6656,66 +6658,66 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/client-s3": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.806.0.tgz", - "integrity": "sha512-kQaBBBxEBU/IJ2wKG+LL2BK+uvBwpdvOA9jy1WhW+U2/DIMwMrjVs7M/ZvTlmVOJwhZaONcJbgQqsN4Yirjj4g==", + "version": "3.824.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.824.0.tgz", + "integrity": "sha512-7neTQIdSVP/F4RTWG5T87LDpB955iQD6lxg9nJ00fdkIPczDcRtAEXow44NjF4fEdpQ1A9jokUtBSVE+GMXZ/A==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.806.0", - "@aws-sdk/credential-provider-node": "3.806.0", - "@aws-sdk/middleware-bucket-endpoint": "3.806.0", - "@aws-sdk/middleware-expect-continue": "3.804.0", - "@aws-sdk/middleware-flexible-checksums": "3.806.0", - "@aws-sdk/middleware-host-header": "3.804.0", - "@aws-sdk/middleware-location-constraint": "3.804.0", - "@aws-sdk/middleware-logger": "3.804.0", - "@aws-sdk/middleware-recursion-detection": "3.804.0", - "@aws-sdk/middleware-sdk-s3": "3.806.0", - "@aws-sdk/middleware-ssec": "3.804.0", - "@aws-sdk/middleware-user-agent": "3.806.0", - "@aws-sdk/region-config-resolver": "3.806.0", - "@aws-sdk/signature-v4-multi-region": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@aws-sdk/util-endpoints": "3.806.0", - "@aws-sdk/util-user-agent-browser": "3.804.0", - "@aws-sdk/util-user-agent-node": "3.806.0", - "@aws-sdk/xml-builder": "3.804.0", - "@smithy/config-resolver": "^4.1.1", - "@smithy/core": "^3.3.1", - "@smithy/eventstream-serde-browser": "^4.0.2", - "@smithy/eventstream-serde-config-resolver": "^4.1.0", - "@smithy/eventstream-serde-node": "^4.0.2", - "@smithy/fetch-http-handler": "^5.0.2", - "@smithy/hash-blob-browser": "^4.0.2", - "@smithy/hash-node": "^4.0.2", - "@smithy/hash-stream-node": "^4.0.2", - "@smithy/invalid-dependency": "^4.0.2", - "@smithy/md5-js": "^4.0.2", - "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.3", - "@smithy/middleware-retry": "^4.1.4", - "@smithy/middleware-serde": "^4.0.3", - "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/node-http-handler": "^4.0.4", - "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", - "@smithy/url-parser": "^4.0.2", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/credential-provider-node": "3.823.0", + "@aws-sdk/middleware-bucket-endpoint": "3.821.0", + "@aws-sdk/middleware-expect-continue": "3.821.0", + "@aws-sdk/middleware-flexible-checksums": "3.823.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-location-constraint": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-s3": "3.823.0", + "@aws-sdk/middleware-ssec": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/signature-v4-multi-region": "3.824.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.823.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-blob-browser": "^4.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.11", - "@smithy/util-defaults-mode-node": "^4.0.11", - "@smithy/util-endpoints": "^3.0.3", - "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.3", - "@smithy/util-stream": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", + "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.3", + "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" }, "engines": { @@ -6723,47 +6725,47 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/client-sso": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.806.0.tgz", - "integrity": "sha512-X0p/9/u9e6b22rlQqKucdtjdqmjSNB4c/8zDEoD5MvgYAAbMF9HNE0ST2xaA/WsJ7uE0jFfhPY2/00pslL1DqQ==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.823.0.tgz", + "integrity": "sha512-dBWdsbyGw8rPfdCsZySNtTOGQK4EZ8lxB/CneSQWRBPHgQ+Ys88NXxImO8xfWO7Itt1eh8O7UDTZ9+smcvw2pw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.806.0", - "@aws-sdk/middleware-host-header": "3.804.0", - "@aws-sdk/middleware-logger": "3.804.0", - "@aws-sdk/middleware-recursion-detection": "3.804.0", - "@aws-sdk/middleware-user-agent": "3.806.0", - "@aws-sdk/region-config-resolver": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@aws-sdk/util-endpoints": "3.806.0", - "@aws-sdk/util-user-agent-browser": "3.804.0", - "@aws-sdk/util-user-agent-node": "3.806.0", - "@smithy/config-resolver": "^4.1.1", - "@smithy/core": "^3.3.1", - "@smithy/fetch-http-handler": "^5.0.2", - "@smithy/hash-node": "^4.0.2", - "@smithy/invalid-dependency": "^4.0.2", - "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.3", - "@smithy/middleware-retry": "^4.1.4", - "@smithy/middleware-serde": "^4.0.3", - "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/node-http-handler": "^4.0.4", - "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", - "@smithy/url-parser": "^4.0.2", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.823.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.11", - "@smithy/util-defaults-mode-node": "^4.0.11", - "@smithy/util-endpoints": "^3.0.3", - "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.3", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -6772,191 +6774,162 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/core": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.806.0.tgz", - "integrity": "sha512-HJRINPncdjPK0iL3f6cBpqCMaxVwq2oDbRCzOx04tsLZ0tNgRACBfT3d/zNVRvMt6fnOVKXoN1LAtQaw50pjEA==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.823.0.tgz", + "integrity": "sha512-1Cf4w8J7wYexz0KU3zpaikHvldGXQEjFldHOhm0SBGRy7qfYNXecfJAamccF7RdgLxKGgkv5Pl9zX/Z/DcW9zg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/core": "^3.3.1", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", - "@smithy/util-middleware": "^4.0.2", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.806.0.tgz", - "integrity": "sha512-nbPwmZn0kt6Q1XI2FaJWP6AhF9tro4cO5HlmZQx8NU+B0H1y9WMo659Q5zLLY46BXgoQVIJEsPSZpcZk27O4aw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/types": "^4.2.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/xml-builder": "3.821.0", + "@smithy/core": "^3.5.1", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.806.0.tgz", - "integrity": "sha512-e/gB2iJQQ4ZpecOVpEFhEvjGwuTqNCzhVaVsFYVc49FPfR1seuN7qBGYe1MO7mouGDQFInzJgcNup0DnYUrLiw==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.823.0.tgz", + "integrity": "sha512-AIrLLwumObge+U1klN4j5ToIozI+gE9NosENRyHe0GIIZgTLOG/8jxrMFVYFeNHs7RUtjDTxxewislhFyGxJ/w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/fetch-http-handler": "^5.0.2", - "@smithy/node-http-handler": "^4.0.4", - "@smithy/property-provider": "^4.0.2", - "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", - "@smithy/util-stream": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.806.0.tgz", - "integrity": "sha512-FogfbuYSEZgFxbNy0QcsBZHHe5mSv5HV3+JyB5n0kCyjOISCVCZD7gwxKdXjt8O1hXq5k5SOdQvydGULlB6rew==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/credential-provider-env": "3.806.0", - "@aws-sdk/credential-provider-http": "3.806.0", - "@aws-sdk/credential-provider-process": "3.806.0", - "@aws-sdk/credential-provider-sso": "3.806.0", - "@aws-sdk/credential-provider-web-identity": "3.806.0", - "@aws-sdk/nested-clients": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/credential-provider-imds": "^4.0.2", - "@smithy/property-provider": "^4.0.2", - "@smithy/shared-ini-file-loader": "^4.0.2", - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.806.0.tgz", - "integrity": "sha512-fZX8xP2Kf0k70kDTog/87fh/M+CV0E2yujSw1cUBJhDSwDX3RlUahiJk7TpB/KGw6hEFESMd6+7kq3UzYuw3rg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.806.0", - "@aws-sdk/credential-provider-http": "3.806.0", - "@aws-sdk/credential-provider-ini": "3.806.0", - "@aws-sdk/credential-provider-process": "3.806.0", - "@aws-sdk/credential-provider-sso": "3.806.0", - "@aws-sdk/credential-provider-web-identity": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/credential-provider-imds": "^4.0.2", - "@smithy/property-provider": "^4.0.2", - "@smithy/shared-ini-file-loader": "^4.0.2", - "@smithy/types": "^4.2.0", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.823.0.tgz", + "integrity": "sha512-u4DXvB/J/o2bcvP1JP6n3ch7V3/NngmiJFPsM0hKUyRlLuWM37HEDEdjPRs3/uL/soTxrEhWKTA9//YVkvzI0w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.806.0.tgz", - "integrity": "sha512-8Y8GYEw/1e5IZRDQL02H6nsTDcRWid/afRMeWg+93oLQmbHcTtdm48tjis+7Xwqy+XazhMDmkbUht11QPTDJcQ==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.823.0.tgz", + "integrity": "sha512-C0o63qviK5yFvjH9zKWAnCUBkssJoQ1A1XAHe0IAQkurzoNBSmu9oVemqwnKKHA4H6QrmusaEERfL00yohIkJA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/shared-ini-file-loader": "^4.0.2", - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/credential-provider-env": "3.823.0", + "@aws-sdk/credential-provider-http": "3.823.0", + "@aws-sdk/credential-provider-process": "3.823.0", + "@aws-sdk/credential-provider-sso": "3.823.0", + "@aws-sdk/credential-provider-web-identity": "3.823.0", + "@aws-sdk/nested-clients": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.806.0.tgz", - "integrity": "sha512-hT9OBwCxWMPBydNhXm2gdNNzx5AJNheS9RglwDDvXWzQ9qDuRztjuMBilMSUMb0HF9K4IqQjYzGqczMuktz4qQ==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.823.0.tgz", + "integrity": "sha512-nfSxXVuZ+2GJDpVFlflNfh55Yb4BtDsXLGNssXF5YU6UgSPsi8j2YkaE92Jv2s7dlUK07l0vRpLyPuXMaGeiRQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.806.0", - "@aws-sdk/core": "3.806.0", - "@aws-sdk/token-providers": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/shared-ini-file-loader": "^4.0.2", - "@smithy/types": "^4.2.0", + "@aws-sdk/credential-provider-env": "3.823.0", + "@aws-sdk/credential-provider-http": "3.823.0", + "@aws-sdk/credential-provider-ini": "3.823.0", + "@aws-sdk/credential-provider-process": "3.823.0", + "@aws-sdk/credential-provider-sso": "3.823.0", + "@aws-sdk/credential-provider-web-identity": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/credential-provider-imds": "^4.0.6", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.806.0.tgz", - "integrity": "sha512-XxaSY9Zd3D4ClUGENYMvi52ac5FuJPPAsvRtEfyrSdEpf6QufbMpnexWBZMYRF31h/VutgqtJwosGgNytpxMEg==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.823.0.tgz", + "integrity": "sha512-U/A10/7zu2FbMFFVpIw95y0TZf+oYyrhZTBn9eL8zgWcrYRqxrxdqtPj/zMrfIfyIvQUhuJSENN4dx4tfpCMWQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/nested-clients": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.806.0.tgz", - "integrity": "sha512-ACjuyKJw9OZl8z8HzPEaqn1o7ElVW94mowyoZvyUIDouwAPGqPGJbJ5V35qx1oDTFSAJX+N3O3AO6RyFc8nUhw==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.823.0.tgz", + "integrity": "sha512-ff8IM80Wqz1V7VVMaMUqO2iR417jggfGWLPl8j2l7uCgwpEyop1ZZl5CFVYEwSupRBtwp+VlW1gTCk7ke56MUw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.804.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", - "@smithy/util-config-provider": "^4.0.0", + "@aws-sdk/client-sso": "3.823.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/token-providers": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.804.0.tgz", - "integrity": "sha512-YW1hySBolALMII6C8y7Z0CRG2UX1dGJjLEBNFeefhO/xP7ZuE1dvnmfJGaEuBMnvc3wkRS63VZ3aqX6sevM1CA==", + "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.823.0.tgz", + "integrity": "sha512-lzoZdJMQq9w7i4lXVka30cVBe/dZoUDZST8Xz/soEd73gg7RTKgG+0szL4xFWgdBDgcJDWLfZfJzlbyIVyAyOA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/nested-clients": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -6964,22 +6937,22 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.806.0.tgz", - "integrity": "sha512-YEmuU2Nr/+blhi70gS38fnCe2IoL6OVVZXMp4MbzqZRUqeBbnxZhHQrd5YOiboJz7iq+g98xwFebHY167iejcg==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.823.0.tgz", + "integrity": "sha512-Elt6G1ryEEdkrppqbyJON0o2x4x9xKknimJtMLdfG1b4YfO9X+UB31pk4R2SHvMYfrJ+p8DE2jRAhvV4g/dwIQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", - "@smithy/util-middleware": "^4.0.2", - "@smithy/util-stream": "^4.2.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -6987,82 +6960,24 @@ "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.804.0.tgz", - "integrity": "sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.804.0.tgz", - "integrity": "sha512-AMtKnllIWKgoo7hiJfphLYotEwTERfjVMO2+cKAncz9w1g+bnYhHxiVhJJoR94y047c06X4PU5MsTxvdQ73Znw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-logger": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.804.0.tgz", - "integrity": "sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.804.0.tgz", - "integrity": "sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.806.0.tgz", - "integrity": "sha512-K1ssdovHH/kPN9EUS1LznwzoL+r89Cx8qAkp0K8MqdCQuBjZ0KRnjvo9nx69Vg5d/rg01VYTxomFUPXfcPtVXw==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.823.0.tgz", + "integrity": "sha512-UV755wt2HDru8PbxLn2S0Fvwgdn9mYamexn31Q6wyUGQ6rkpjKNEzL+oNDGQQmDQAOcQO+nLubKFsCwtBM02fQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.3.1", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", + "@smithy/core": "^3.5.1", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.2", - "@smithy/util-stream": "^4.2.0", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -7070,32 +6985,18 @@ "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-ssec": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.804.0.tgz", - "integrity": "sha512-Tk8jK0gOIUBvEPTz/wwSlP1V70zVQ3QYqsLPAjQRMO6zfOK9ax31dln3MgKvFDJxBydS2tS3wsn53v+brxDxTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.806.0.tgz", - "integrity": "sha512-XoIromVffgXnc+/mjlR2EVzQVIei3bPVtafIZNsHuEmUvIWJXiWsa2eJpt3BUqa0HF9YPknK7ommNEhqRb8ucg==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.823.0.tgz", + "integrity": "sha512-TKRQK09ld1LrIPExC9rIDpqnMsWcv+eq8ABKFHVo8mDLTSuWx/IiQ4eCh9T5zDuEZcLY4nNYCSzXKqw6XKcMCA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@aws-sdk/util-endpoints": "3.806.0", - "@smithy/core": "^3.3.1", - "@smithy/protocol-http": "^5.1.0", - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@smithy/core": "^3.5.1", + "@smithy/protocol-http": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -7103,47 +7004,47 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/nested-clients": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.806.0.tgz", - "integrity": "sha512-ua2gzpfQ9MF8Rny+tOAivowOWWvqEusez2rdcQK8jdBjA1ANd/0xzToSZjZh0ziN8Kl8jOhNnHbQJ0v6dT6+hg==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.823.0.tgz", + "integrity": "sha512-/BcyOBubrJnd2gxlbbmNJR1w0Z3OVN/UE8Yz20e+ou+Mijjv7EbtVwmWvio1e3ZjphwdA8tVfPYZKwXmrvHKmQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.806.0", - "@aws-sdk/middleware-host-header": "3.804.0", - "@aws-sdk/middleware-logger": "3.804.0", - "@aws-sdk/middleware-recursion-detection": "3.804.0", - "@aws-sdk/middleware-user-agent": "3.806.0", - "@aws-sdk/region-config-resolver": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@aws-sdk/util-endpoints": "3.806.0", - "@aws-sdk/util-user-agent-browser": "3.804.0", - "@aws-sdk/util-user-agent-node": "3.806.0", - "@smithy/config-resolver": "^4.1.1", - "@smithy/core": "^3.3.1", - "@smithy/fetch-http-handler": "^5.0.2", - "@smithy/hash-node": "^4.0.2", - "@smithy/invalid-dependency": "^4.0.2", - "@smithy/middleware-content-length": "^4.0.2", - "@smithy/middleware-endpoint": "^4.1.3", - "@smithy/middleware-retry": "^4.1.4", - "@smithy/middleware-serde": "^4.0.3", - "@smithy/middleware-stack": "^4.0.2", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/node-http-handler": "^4.0.4", - "@smithy/protocol-http": "^5.1.0", - "@smithy/smithy-client": "^4.2.3", - "@smithy/types": "^4.2.0", - "@smithy/url-parser": "^4.0.2", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.821.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.823.0", + "@smithy/config-resolver": "^4.1.4", + "@smithy/core": "^3.5.1", + "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-node": "^4.0.4", + "@smithy/invalid-dependency": "^4.0.4", + "@smithy/middleware-content-length": "^4.0.4", + "@smithy/middleware-endpoint": "^4.1.9", + "@smithy/middleware-retry": "^4.1.10", + "@smithy/middleware-serde": "^4.0.8", + "@smithy/middleware-stack": "^4.0.4", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/node-http-handler": "^4.0.6", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.1", + "@smithy/types": "^4.3.1", + "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.11", - "@smithy/util-defaults-mode-node": "^4.0.11", - "@smithy/util-endpoints": "^3.0.3", - "@smithy/util-middleware": "^4.0.2", - "@smithy/util-retry": "^4.0.3", + "@smithy/util-defaults-mode-browser": "^4.0.17", + "@smithy/util-defaults-mode-node": "^4.0.17", + "@smithy/util-endpoints": "^3.0.6", + "@smithy/util-middleware": "^4.0.4", + "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -7151,23 +7052,6 @@ "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.806.0.tgz", - "integrity": "sha512-cuv5pX55JOlzKC/iLsB5nZ9eUyVgncim3VhhWHZA/KYPh7rLMjOEfZ+xyaE9uLJXGmzOJboFH7+YdTRdIcOgrg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/types": "^4.2.0", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/s3-request-presigner": { "version": "3.787.0", "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.787.0.tgz", @@ -7277,16 +7161,16 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.806.0.tgz", - "integrity": "sha512-IrbEnpKvG8d9rUWAvsF28g8qBlQ02FaOxn4cGXtTs0b0BGMK1M+cGQrYjJ7Ak08kIXDxBqsdIlZGsKYr+Ds9+w==", + "version": "3.824.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.824.0.tgz", + "integrity": "sha512-HBjuWeN6Z1pvJjUvGXdMNLwEypKKB4km6zXj9jsbOOwP8NTL6J5rY+JmlX/mfBTmvzmI0kMu2bxlQ4ME2CIRbA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/protocol-http": "^5.1.0", - "@smithy/signature-v4": "^5.1.0", - "@smithy/types": "^4.2.0", + "@aws-sdk/middleware-sdk-s3": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/protocol-http": "^5.1.2", + "@smithy/signature-v4": "^5.1.2", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -7294,29 +7178,17 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/token-providers": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.806.0.tgz", - "integrity": "sha512-I6SxcsvV7yinJZmPgGullFHS0tsTKa7K3jEc5dmyCz8X+kZPfsWNffZmtmnCvWXPqMXWBvK6hVaxwomx79yeHA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/nested-clients": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/property-provider": "^4.0.2", - "@smithy/shared-ini-file-loader": "^4.0.2", - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/types": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", - "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.823.0.tgz", + "integrity": "sha512-vz6onCb/+g4y+owxGGPMEMdN789dTfBOgz/c9pFv0f01840w9Rrt46l+gjQlnXnx+0KG6wNeBIVhFdbCfV3HyQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.2.0", + "@aws-sdk/core": "3.823.0", + "@aws-sdk/nested-clients": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/property-provider": "^4.0.4", + "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -7324,14 +7196,14 @@ } }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/util-endpoints": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.806.0.tgz", - "integrity": "sha512-3YRRgZ+qFuWDdm5uAbxKsr65UAil4KkrFKua9f4m7Be3v24ETiFOOqhanFUIk9/WOtvzF7oFEiDjYKDGlwV2xg==", + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", + "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/types": "^4.2.0", - "@smithy/util-endpoints": "^3.0.3", + "@aws-sdk/types": "3.821.0", + "@smithy/types": "^4.3.1", + "@smithy/util-endpoints": "^3.0.6", "tslib": "^2.6.2" }, "engines": { @@ -7366,28 +7238,16 @@ "node": ">=18.0.0" } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.804.0.tgz", - "integrity": "sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.804.0", - "@smithy/types": "^4.2.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.806.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.806.0.tgz", - "integrity": "sha512-Az2e4/gmPZ4BpB7QRj7U76I+fctXhNcxlcgsaHnMhvt+R30nvzM2EhsyBUvsWl8+r9bnLeYt9BpvEZeq2ANDzA==", + "version": "3.823.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.823.0.tgz", + "integrity": "sha512-WvNeRz7HV3JLBVGTXW4Qr5QvvWY0vtggH5jW/NqHFH+ZEliVQaUIJ/HNLMpMoCSiu/DlpQAyAjRZXAptJ0oqbw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.806.0", - "@aws-sdk/types": "3.804.0", - "@smithy/node-config-provider": "^4.1.0", - "@smithy/types": "^4.2.0", + "@aws-sdk/middleware-user-agent": "3.823.0", + "@aws-sdk/types": "3.821.0", + "@smithy/node-config-provider": "^4.1.3", + "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -7402,19 +7262,6 @@ } } }, - "node_modules/@parse/s3-files-adapter/node_modules/@aws-sdk/xml-builder": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.804.0.tgz", - "integrity": "sha512-JbGWp36IG9dgxtvC6+YXwt5WDZYfuamWFtVfK6fQpnmL96dx+GUPOXPKRWdw67WLKf2comHY28iX2d3z35I53Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@parse/s3-files-adapter/node_modules/@smithy/abort-controller": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", @@ -10724,9 +10571,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -11015,9 +10862,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", - "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -11222,9 +11069,10 @@ "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11590,6 +11438,12 @@ "node": ">=0.10.0" } }, + "node_modules/coherentpdf": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/coherentpdf/-/coherentpdf-2.5.5.tgz", + "integrity": "sha512-AAD3RG7lq2n34AtH352sbNDhJ82yfp2vFUp63/+Vu7snAqW0vyvY33XuhZdj6ubZUlFe1DNSSEaYcyc7+jTJ5Q==", + "license": "AGPL-3.0-or-later" + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -11762,7 +11616,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", @@ -11820,6 +11675,15 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/date-fns": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", @@ -12275,18 +12139,18 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/eslint": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", - "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", + "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", + "@eslint/config-array": "^0.20.1", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.28.0", + "@eslint/js": "9.29.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -12298,9 +12162,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -12381,9 +12245,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -12397,9 +12261,10 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -12436,14 +12301,14 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12453,9 +12318,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12808,6 +12673,29 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -13025,20 +12913,33 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -13419,10 +13320,11 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -13502,33 +13404,196 @@ "node": ">=14" } }, + "node_modules/google-logging-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz", + "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/googleapis": { - "version": "149.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-149.0.0.tgz", - "integrity": "sha512-LTMc/njwYy7KTeaUHDcQt7KxftHyghdzm2XzbL46PRLd1AXB09utT9Po2ZJn2X0EApz0pE2T5x5A9zM8iue6zw==", + "version": "150.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-150.0.1.tgz", + "integrity": "sha512-9Wa9vm3WtDpss0VFBHsbZWcoRccpOSWdpz7YIfb1LBXopZJEg/Zc8ymmaSgvDkP4FhN+pqPS9nZjO7REAJWSUg==", "license": "Apache-2.0", "dependencies": { - "google-auth-library": "^9.0.0", - "googleapis-common": "^7.0.0" + "google-auth-library": "^10.0.0-rc.1", + "googleapis-common": "^8.0.2-rc.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, "node_modules/googleapis-common": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", - "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "version": "8.0.2-rc.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.2-rc.0.tgz", + "integrity": "sha512-JTcxRvmFa9Ec1uyfMEimEMeeKq1sHNZX3vn2qmoUMtnvixXXvcqTcbDZvEZXkEWpGlPlOf4joyep6/qs0BrLyg==", + "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", - "gaxios": "^6.0.3", - "google-auth-library": "^9.7.0", + "gaxios": "^7.0.0-rc.4", + "google-auth-library": "^10.0.0-rc.1", "qs": "^6.7.0", - "url-template": "^2.0.8", - "uuid": "^9.0.0" + "url-template": "^2.0.8" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/googleapis-common/node_modules/gaxios": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.0.tgz", + "integrity": "sha512-y1Q0MX1Ba6eg67Zz92kW0MHHhdtWksYckQy1KJsI6P4UlDQ8cvdvpLEPslD/k7vFkdPppMESFGTvk7XpSiKj8g==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis-common/node_modules/gcp-metadata": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.0.tgz", + "integrity": "sha512-3PfRTzvT3Msu0Hy8Gf9ypxJvaClG2IB9pyH0r8QOmRBW5mUcrHgYpF4GYP+XulDbfhxEhBYtJtJJQb5S2wM+LA==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis-common/node_modules/google-auth-library": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.1.0.tgz", + "integrity": "sha512-GspVjZj1RbyRWpQ9FbAXMKjFGzZwDKnUHi66JJ+tcjcu5/xYAP1pdlWotCuIkMwjfVsxxDvsGZXGLzRt72D0sQ==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^7.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis-common/node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis-common/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/googleapis/node_modules/gaxios": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.0.tgz", + "integrity": "sha512-y1Q0MX1Ba6eg67Zz92kW0MHHhdtWksYckQy1KJsI6P4UlDQ8cvdvpLEPslD/k7vFkdPppMESFGTvk7XpSiKj8g==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis/node_modules/gcp-metadata": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.0.tgz", + "integrity": "sha512-3PfRTzvT3Msu0Hy8Gf9ypxJvaClG2IB9pyH0r8QOmRBW5mUcrHgYpF4GYP+XulDbfhxEhBYtJtJJQb5S2wM+LA==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis/node_modules/google-auth-library": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.1.0.tgz", + "integrity": "sha512-GspVjZj1RbyRWpQ9FbAXMKjFGzZwDKnUHi66JJ+tcjcu5/xYAP1pdlWotCuIkMwjfVsxxDvsGZXGLzRt72D0sQ==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^7.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis/node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/googleapis/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/gopd": { @@ -14314,23 +14379,23 @@ } }, "node_modules/jasmine": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.7.1.tgz", - "integrity": "sha512-E/4fkRNy/9ALz6z3Z3/tYXFAohoznVy7In9FWutG2fqBSkILJHFzbgZtHJUw5UrL3jgUQ4sdGYOVZ5KpSXYjGw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.8.0.tgz", + "integrity": "sha512-1V6HGa0+TMoMY20+/vp++RqLlL1noupV8awzV6CiPuICC0g7iKZ9z87zV2KyelRyoig0G1lHn7ueElXVMGVagg==", "dev": true, "license": "MIT", "dependencies": { "glob": "^10.2.2", - "jasmine-core": "~5.7.0" + "jasmine-core": "~5.8.0" }, "bin": { "jasmine": "bin/jasmine.js" } }, "node_modules/jasmine-core": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.7.0.tgz", - "integrity": "sha512-EnUzZBHxS1Ofq+FPWs16rs2YC9o6Hb3buKJQDlkhJBDx+Bm5wNF+J1gUS06dWuW2ozaQ3oNIA1SESX9M5LopOQ==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.8.0.tgz", + "integrity": "sha512-Q9dqmpUAfptwyueW3+HqBOkSuYd9I/clZSSfN97wXE/Nr2ROFNCwIBEC1F6kb3QXS9Fcz0LjFYSDQT+BiwjuhA==", "dev": true, "license": "MIT" }, @@ -14605,6 +14670,19 @@ "node": ">= 0.8.0" } }, + "node_modules/libreoffice-convert": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/libreoffice-convert/-/libreoffice-convert-1.6.1.tgz", + "integrity": "sha512-j4YtAp8/5EZIGPH1zyzuPHYXBTUXDWvDyDxJY1w67Fw7F2qryvMlYnl6W0cUZN9yWs6aZCG0Ix2/RePOlvh1bA==", + "license": "MIT", + "dependencies": { + "async": "^3.2.3", + "tmp": "^0.2.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -15050,9 +15128,9 @@ } }, "node_modules/mongodb-download-url": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.5.7.tgz", - "integrity": "sha512-GpQJAfYmfYwqVXUyfIUQXe5kFoiCK5kORBJnPixAmQGmabZix6fBTpX7vSy3J46VgiAe+VEOjSikK/TcGKTw+A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.6.0.tgz", + "integrity": "sha512-IQcQfKi3zdejKIAPaCpsW2F1FpMBsdifzY5K0YdddmJSFDJAGY7xUbCVm0pdL36+1ck6+c2ytTSqzProLhvGoA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -15066,9 +15144,9 @@ } }, "node_modules/mongodb-download-url/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -15079,14 +15157,14 @@ } }, "node_modules/mongodb-runner": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.8.3.tgz", - "integrity": "sha512-LLmbE9A/aGqABaaTmxFJ+TiHjmhZx1kZRLV14nFLvBz2zV3F/rLBo9kJ/Pz00h0IYk3zi7abL82I+WKZVzkoSQ==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.9.0.tgz", + "integrity": "sha512-31oyYEmLvkuqDV9J9kuwwOE2SHV1LaPyqr+fZsgYT56ceynqq4ABUOXmmdZBXm3zdLM1QGUft5UJokkkhXGPCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@mongodb-js/mongodb-downloader": "^0.3.9", - "@mongodb-js/saslprep": "^1.2.2", + "@mongodb-js/mongodb-downloader": "^0.4.0", + "@mongodb-js/saslprep": "^1.3.0", "debug": "^4.4.0", "mongodb": "^6.9.0", "mongodb-connection-string-url": "^3.0.0", @@ -15164,6 +15242,26 @@ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", "license": "MIT" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -16471,15 +16569,12 @@ } }, "node_modules/posthog-node": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-4.18.0.tgz", - "integrity": "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.1.0.tgz", + "integrity": "sha512-U+b2GJEhK1O0BuIdsoYJu3M+3OUZIbQAhAmIYHTMTCaA7dFx30Pv1nXqA4gPjEPoXhXc6akJbMTRREY5ncJ/Gw==", "license": "MIT", - "dependencies": { - "axios": "^1.8.2" - }, "engines": { - "node": ">=15.0.0" + "node": ">=20" } }, "node_modules/precond": { @@ -16518,7 +16613,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/process-on-spawn": { "version": "1.0.0", @@ -16900,6 +16996,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -16914,7 +17011,8 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/readdirp": { "version": "3.6.0", @@ -18033,10 +18131,33 @@ "dev": true, "license": "MIT" }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.0.tgz", + "integrity": "sha512-uM2Zapt+GfaxYhCxKtUn7ktNOjAHL8nthQIQVhvNCjGLe8t+ULQ5i5hyFpMzuRxHkg2nrKNFTQpYDLhzxC3Jew==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, "license": "MIT" }, @@ -18310,7 +18431,8 @@ "node_modules/url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" }, "node_modules/url/node_modules/punycode": { "version": "1.3.2", @@ -18438,6 +18560,15 @@ "node": ">= 16" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/apps/OpenSignServer/package.json b/apps/OpenSignServer/package.json index 4085a29e62..12c0d809b8 100644 --- a/apps/OpenSignServer/package.json +++ b/apps/OpenSignServer/package.json @@ -18,23 +18,25 @@ "watch": "nodemon index.js" }, "dependencies": { - "@aws-sdk/client-s3": "^3.824.0", - "@aws-sdk/s3-request-presigner": "^3.824.0", + "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.828.0", "@parse/fs-files-adapter": "^3.0.0", - "@parse/s3-files-adapter": "^4.1.1", + "@parse/s3-files-adapter": "^4.2.0", "@pdf-lib/fontkit": "^1.1.1", "@signpdf/placeholder-pdf-lib": "^3.2.6", "@signpdf/signer-p12": "^3.2.4", "@signpdf/signpdf": "^3.2.5", "aws-sdk": "^2.1692.0", - "axios": "^1.9.0", + "axios": "^1.10.0", + "coherentpdf": "^2.5.5", "cors": "^2.8.5", "date-fns-tz": "^3.2.0", "dotenv": "^16.5.0", "express": "^5.1.0", - "form-data": "^4.0.2", + "form-data": "^4.0.3", "generate-api-key": "^1.0.2", - "googleapis": "^149.0.0", + "googleapis": "^150.0.1", + "libreoffice-convert": "^1.6.1", "mailgun.js": "^12.0.2", "mongodb": "^6.17.0", "multer": "^2.0.1", @@ -46,7 +48,7 @@ "parse-server": "^8.2.1", "parse-server-api-mail-adapter": "^4.1.0", "pdf-lib": "^1.17.1", - "posthog-node": "^4.18.0", + "posthog-node": "^5.1.0", "qrcode": "^1.5.4", "rate-limiter-flexible": "^7.1.1", "speakeasy": "^2.0.0", @@ -55,9 +57,9 @@ "type": "module", "devDependencies": { "@babel/eslint-parser": "^7.27.5", - "eslint": "^9.28.0", - "jasmine": "^5.7.1", - "mongodb-runner": "^5.8.3", + "eslint": "^9.29.0", + "jasmine": "^5.8.0", + "mongodb-runner": "^5.9.0", "nodemon": "^3.1.10", "nyc": "^17.1.0", "prettier": "^3.5.3" From 898344076350cd0ccf2cfa812ed31176cdb134c5 Mon Sep 17 00:00:00 2001 From: nxglabs <nxglabs@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:54:13 +0000 Subject: [PATCH 2/3] Merge pull request #864 from nxglabs/staging --- apps/OpenSign/src/constant/Utils.jsx | 32 +++++++++++++++++++++++++++- apps/OpenSign/src/pages/Form.jsx | 5 +++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.jsx b/apps/OpenSign/src/constant/Utils.jsx index e2726624a0..f3028c34f2 100644 --- a/apps/OpenSign/src/constant/Utils.jsx +++ b/apps/OpenSign/src/constant/Utils.jsx @@ -98,6 +98,27 @@ export const getSecureUrl = async (url) => { } }; +/** + * Removes a trailing path segment from a URL string, if present. + * + * @param {string} url - The original URL. + * @param {string} segment - The segment to strip off (default: "app"). + * @returns {string} - The URL with the trailing segment removed, or unmodified if it didn’t match. + */ +export function removeTrailingSegment(url, segment = "app") { + // Normalize a trailing slash (e.g. “/app/” → “/app”) + const normalized = url.endsWith("/") ? url.slice(0, -1) : url; + + const lastSlash = normalized.lastIndexOf("/"); + const lastPart = normalized.slice(lastSlash + 1); + + if (lastPart === segment) { + return normalized.slice(0, lastSlash); + } + + return normalized; +} + export const color = [ "#93a3db", "#e6c3db", @@ -3425,7 +3446,7 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => { export const decryptPdf = async (file, password) => { const name = generatePdfName(16); const baseApi = localStorage.getItem("baseUrl") || ""; - const url = baseApi.replace("/app", "") + "decryptpdf?ts=" + Date.now(); + const url = removeTrailingSegment(baseApi) + "/decryptpdf?ts=" + Date.now(); let formData = new FormData(); formData.append("file", file); formData.append("password", password); @@ -3468,6 +3489,15 @@ export function base64ToFile(base64String, filename) { return new File([u8arr], filename, { type: mime }); } +/** + * Reads the given File object and returns its contents as an ArrayBuffer. + * + * @param {File} file + * The File instance to be read. + * @returns {Promise<ArrayBuffer>} + * A promise that resolves with the file’s binary data as an ArrayBuffer, + * or rejects with an error if the read fails. + */ export function getFileAsArrayBuffer(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); diff --git a/apps/OpenSign/src/pages/Form.jsx b/apps/OpenSign/src/pages/Form.jsx index 01cf12bb1c..72673a1bfc 100644 --- a/apps/OpenSign/src/pages/Form.jsx +++ b/apps/OpenSign/src/pages/Form.jsx @@ -16,7 +16,8 @@ import { toDataUrl, decryptPdf, base64ToFile, - getFileAsArrayBuffer + getFileAsArrayBuffer, + removeTrailingSegment } from "../constant/Utils"; import { PDFDocument } from "pdf-lib"; import axios from "axios"; @@ -240,7 +241,7 @@ const Forms = (props) => { ) { try { const baseApi = localStorage.getItem("baseUrl") || ""; - const url = baseApi.replace("/app", "") + "docxtopdf"; + const url = removeTrailingSegment(baseApi) + "/docxtopdf"; let fd = new FormData(); fd.append("file", file); setfileload(true); From 5561673fdca06d01f0d54b8be67deb7c81273508 Mon Sep 17 00:00:00 2001 From: nxglabs <nxglabs@users.noreply.github.com> Date: Thu, 19 Jun 2025 09:31:17 +0000 Subject: [PATCH 3/3] Merge pull request #866 from nxglabs/fix_main_issues --- .../src/components/pdf/BorderResize.jsx | 7 ++--- .../src/components/pdf/CellsWidget.jsx | 8 +++-- .../src/components/pdf/Placeholder.jsx | 31 ++++++++++--------- .../src/components/pdf/PlaceholderType.jsx | 1 + .../src/components/pdf/WidgetNameModal.jsx | 19 +++++++++++- .../src/components/pdf/WidgetsValueModal.jsx | 12 ++++++- apps/OpenSign/src/pages/PlaceHolderSign.jsx | 10 ++---- 7 files changed, 56 insertions(+), 32 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/BorderResize.jsx b/apps/OpenSign/src/components/pdf/BorderResize.jsx index ca40eba721..0315100141 100644 --- a/apps/OpenSign/src/components/pdf/BorderResize.jsx +++ b/apps/OpenSign/src/components/pdf/BorderResize.jsx @@ -12,12 +12,9 @@ function BorderResize(props) { return ( <div - style={{ - width: getHeight() || "14px", - height: getHeight() || "14px" - }} + style={{ width: getHeight() || "14px", height: getHeight() || "14px" }} className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${ - props.top ? `-bottom-[12px]` : "-bottom-[2px] " + props.top ? `-bottom-[12px]` : "-bottom-[2px]" } absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`} ></div> ); diff --git a/apps/OpenSign/src/components/pdf/CellsWidget.jsx b/apps/OpenSign/src/components/pdf/CellsWidget.jsx index ea6ff5b7aa..8a94dd687d 100644 --- a/apps/OpenSign/src/components/pdf/CellsWidget.jsx +++ b/apps/OpenSign/src/components/pdf/CellsWidget.jsx @@ -1,6 +1,7 @@ import { useState, useEffect, useRef } from "react"; const Cell = ({ + isEnabled, count, h, value, @@ -15,10 +16,11 @@ const Cell = ({ hint }) => ( <div - className="flex items-center justify-center border border-gray-800 bg-white" + className={`${isEnabled ? "bg-white border-gray-400" : "select-none-cls pointer-events-none border-gray-500"} flex items-center justify-center border-[1px]`} style={{ flex: `0 0 ${100 / count}%`, height: h }} > <input + disabled={!isEnabled} maxLength={1} value={value} readOnly={!editable} @@ -27,7 +29,7 @@ const Cell = ({ onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined} // trigger validation when leaving a cell onBlur={editable ? (e) => onBlur && onBlur(e, index) : undefined} - className="w-full text-center focus:outline-none bg-transparent placeholder-gray-300" + className={`${isEnabled ? "placeholder-gray-300" : "placeholder-gray-500"} w-full text-center focus:outline-none bg-transparent`} placeholder={hint} style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }} /> @@ -35,6 +37,7 @@ const Cell = ({ ); export default function CellsWidget({ + isEnabled, count = 8, height = 40, value = "", @@ -120,6 +123,7 @@ export default function CellsWidget({ {cells.map((val, i) => ( <Cell key={i} + isEnabled={isEnabled} count={cellCount} h={height} value={val} diff --git a/apps/OpenSign/src/components/pdf/Placeholder.jsx b/apps/OpenSign/src/components/pdf/Placeholder.jsx index 49c109b218..27be575927 100644 --- a/apps/OpenSign/src/components/pdf/Placeholder.jsx +++ b/apps/OpenSign/src/components/pdf/Placeholder.jsx @@ -740,7 +740,7 @@ function Placeholder(props) { return "not-allowed"; } } else { - return "all-scroll"; + return "move"; } }; @@ -946,23 +946,24 @@ function Placeholder(props) { props.pos.key === props?.currWidgetsDetails?.key && <BorderResize /> )} - {/* 1- Show a ouline if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget. - 2- If props.isShowBorder is true, display ouline for all widgets. - 3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show ouline: - 3.1- When isAlllowModify is true, show ouline. - 3.2- Do not display ouline for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true). + {/* 1- Show a border if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget. + 2- If props.isShowBorder is true, display border for all widgets. + 3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show border: + 3.1- When isAlllowModify is true, show border. + 3.2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true). */} + {props.pos.key === props?.currWidgetsDetails?.key && + (props.isShowBorder || + (props?.isAlllowModify && + !props?.assignedWidgetId.includes(props.pos.key))) && ( + <div + style={{ borderColor: themeColor }} + className="w-[calc(100%+21px)] h-[calc(100%+21px)] cursor-move absolute inline-block border-[1px] border-dashed" + ></div> + )} <div - className={`${ - props.pos.key === props?.currWidgetsDetails?.key && - (props.isShowBorder || - (props?.isAlllowModify && - !props?.assignedWidgetId.includes(props.pos.key))) - ? "outline-[0.3px] outline-dashed outline-offset-[10px]" - : "" - } flex items-stretch justify-center`} + className="flex items-stretch justify-center" style={{ - outlineColor: themeColor, left: xPos(props.pos, props.isSignYourself), top: yPos(props.pos, props.isSignYourself), width: "100%", diff --git a/apps/OpenSign/src/components/pdf/PlaceholderType.jsx b/apps/OpenSign/src/components/pdf/PlaceholderType.jsx index d22d0e09f9..bda8ac2f89 100644 --- a/apps/OpenSign/src/components/pdf/PlaceholderType.jsx +++ b/apps/OpenSign/src/components/pdf/PlaceholderType.jsx @@ -226,6 +226,7 @@ function PlaceholderType(props) { props.isPlaceholder || props.isSignYourself || props.isSelfSign; return ( <CellsWidget + isEnabled={iswidgetEnable} count={count} height={height} value={cells.join("")} diff --git a/apps/OpenSign/src/components/pdf/WidgetNameModal.jsx b/apps/OpenSign/src/components/pdf/WidgetNameModal.jsx index da13603909..b2e1a8b00e 100644 --- a/apps/OpenSign/src/components/pdf/WidgetNameModal.jsx +++ b/apps/OpenSign/src/components/pdf/WidgetNameModal.jsx @@ -123,6 +123,23 @@ const WidgetNameModal = (props) => { } }; + const handleChangeValidateInput = (e) => { + if (e) { + if (e.target.value === "ssn") { + setFormdata({ + ...formdata, + [e.target.name]: e.target.value, + hint: "xxx-xx-xxxx", + cellCount: 11 + }); + } else { + setFormdata({ ...formdata, [e.target.name]: e.target.value }); + } + } else { + setFormdata({ ...formdata, textvalidate: "" }); + } + }; + const handledefaultChange = (e) => { if (formdata.textvalidate) { const regexObject = RegexParser(handleValidation(formdata.textvalidate)); @@ -148,7 +165,7 @@ const WidgetNameModal = (props) => { //allow space in text regex return "/^[a-zA-Z ]+$/"; case "ssn": - return "^\\d{3}-\\d{2}-\\d{4}$"; + return "/^(?!000|666|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0000)\\d{4}$/"; default: return type; } diff --git a/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx b/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx index c2a962fb25..89240dbe7d 100644 --- a/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx +++ b/apps/OpenSign/src/components/pdf/WidgetsValueModal.jsx @@ -1472,6 +1472,7 @@ function WidgetsValueModal(props) { case cellsWidget: return ( <CellsWidget + isEnabled={true} count={cellsValue.length} height="100%" value={cellsValue.join("")} @@ -1762,6 +1763,10 @@ function WidgetsValueModal(props) { regexValidation = /^[0-9\s]*$/; validateExpression(regexValidation); break; + case "ssn": + regexValidation = /^(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}$/; + validateExpression(regexValidation); + break; default: // Grab the current pattern (if it exists) const pattern = currWidgetsDetails?.options?.validation?.pattern; @@ -1872,6 +1877,11 @@ function WidgetsValueModal(props) { } }; const handleclose = () => { + // If validation is shown, clear the response and close the modal + if (isShowValidation) { + handleClear(); + setIsShowValidation(false); + } dispatch(setIsShowModal({})); dispatch(setLastIndex("")); if (currWidgetsDetails?.type === textWidget && uniqueId) { @@ -1936,7 +1946,7 @@ function WidgetsValueModal(props) { <> <ModalUi isOpen={true} - handleClose={() => !isShowValidation && handleclose()} + handleClose={() => handleclose()} position="bottom" > <div className="h-[100%] p-[18px]"> diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.jsx b/apps/OpenSign/src/pages/PlaceHolderSign.jsx index faedb49cda..51e3f8412f 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.jsx +++ b/apps/OpenSign/src/pages/PlaceHolderSign.jsx @@ -1608,13 +1608,7 @@ function PlaceHolderSign() { parseInt(defaultdata?.cellCount || 5) ), validation: - isSubscribe && inputype - ? { - type: inputype, - pattern: - inputype === "regex" ? defaultdata.textvalidate : "" - } - : {}, + {}, fontSize: fontSize || currWidgetsDetails?.options?.fontSize || 12, fontColor: @@ -2636,7 +2630,7 @@ function PlaceHolderSign() { title={t("document-alert")} showClose={false} > - <div className="h-[100%] p-[20px]"> + <div className="h-[100%] p-[20px] text-base-content"> <p>{isAlreadyPlace.message}</p> <div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div> <button