Merge pull request #1757 from OpenSignLabs/staging

v2.22.0
This commit is contained in:
prafull-opensignlabs
2025-05-28 11:27:44 +05:30
committed by GitHub
42 changed files with 8999 additions and 21536 deletions
+4585 -17683
View File
File diff suppressed because it is too large Load Diff
+20 -34
View File
@@ -1,15 +1,15 @@
{
"name": "open_sign",
"version": "0.1.0",
"version": "2.21.1",
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.13.5",
"@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.6",
"@reduxjs/toolkit": "^2.7.0",
"@radix-ui/themes": "^3.2.1",
"@reduxjs/toolkit": "^2.8.2",
"@imgly/background-removal": "^1.6.0",
"axios": "^1.9.0",
"css-minimizer-webpack-plugin": "^7.0.2",
"date-fns-tz": "^3.2.0",
"file-saver": "^2.0.5",
"i18next": "^23.16.8",
@@ -18,46 +18,40 @@
"jszip": "^3.10.1",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
"nth-check": "^2.1.1",
"parse": "^6.1.1",
"pdf-lib": "^1.17.1",
"print-js": "^1.6.0",
"radix-ui": "^1.0.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.9",
"prismjs": "^1.30.0",
"radix-ui": "^1.4.2",
"react": "^18.3.1",
"react-bootstrap": "^2.10.10",
"react-confetti": "^6.4.0",
"react-cookie": "^7.2.2",
"react-datepicker": "^7.6.0",
"react-datepicker": "^8.3.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^9.0.0",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-dom": "^18.3.1",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-i18next": "^15.5.0",
"react-i18next": "^15.5.1",
"react-konva": "^18.2.10",
"react-pdf": "^9.2.1",
"react-quill-new": "^3.4.6",
"react-redux": "^9.2.0",
"react-rnd": "^10.5.2",
"react-router": "^7.5.3",
"react-scripts": "^5.0.1",
"react-router": "^7.6.0",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.10.1",
"react-signature-canvas": "^1.0.7",
"react-syntax-highlighter": "^15.6.1",
"react-signature-canvas": "^1.1.0-alpha.2",
"react-timezone-select": "^3.2.8",
"react-tooltip": "^5.28.1",
"react-web-share": "^2.0.2",
"reactour": "^1.19.4",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.1",
"serve": "^14.2.4",
"styled-components": "^5.3.0",
"web-vitals": "^4.2.4",
"ws": "^8.18.2",
"styled-components": "^5.3.11",
"web-vitals": "^5.0.1",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
},
"scripts": {
@@ -99,13 +93,13 @@
},
"devDependencies": {
"@babel/core": "^7.27.1",
"@babel/preset-env": "^7.27.1",
"@babel/preset-env": "^7.27.2",
"@babel/preset-react": "^7.27.1",
"@babel/runtime-corejs2": "^7.27.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^18.3.21",
"@types/react": "^18.3.22",
"@vitejs/plugin-react": "^4.4.1",
"@vitejs/plugin-react-swc": "^3.9.0",
"autoprefixer": "^10.4.21",
@@ -115,27 +109,19 @@
"css-loader": "^7.1.2",
"daisyui": "^4.12.24",
"dotenv": "^16.5.0",
"dotenv-webpack": "^8.1.0",
"eslint": "^8.57.1",
"eslint": "^9.27.0",
"eslint-plugin-prettier": "^5.4.0",
"eslint-plugin-react": "^7.37.5",
"lint-staged": "^15.5.1",
"mini-css-extract-plugin": "^2.9.2",
"lint-staged": "^16.0.0",
"postcss": "^8.5.3",
"prettier": "^3.5.3",
"pretty-quick": "^4.1.1",
"rollup-plugin-node-polyfills": "^0.2.1",
"tailwindcss": "^3.4.17",
"terser-webpack-plugin": "^5.3.14",
"vite": "^6.3.5",
"vite-plugin-svgr": "^4.3.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.1.3",
"webpack-cli": "^5.1.4"
},
"overrides": {
"nth-check": "$nth-check",
"ws": "$ws"
"vitest": "^3.1.4"
},
"engines": {
"node": "18 || 20 || 22"
@@ -39,8 +39,10 @@
"save": "Speichern",
"cancel": "Abbrechen",
"upgrade-now": "Jetzt upgraden",
"contact-now": "Jetzt kontaktieren",
"upgrade-to": "Upgrade zu",
"plan": "Plan",
"subscription-renew-warning": "Ihr Abonnement läuft in {{remainingDays}} Tagen ab. Bitte verlängern Sie Ihr Abonnement.",
"subscribe-card-teamplan": "Entfesseln Sie die volle Kraft der Zusammenarbeit! Erstellen Sie unbegrenzt Organisationen, Teams und Hierarchien. Teilen Sie Vorlagen nahtlos zwischen Teams und weisen Sie benutzerdefinierte Benutzerrollen zu. Optimieren Sie Ihren Workflow noch heute!",
"subscribe-card-plan": "Entsperren Sie Premium-Funktionen ab nur {{premiumPrice}}/Monat. Genießen Sie eine verbesserte Leistung und zahlen Sie nur {{addonPrice}} pro zusätzlichem Credit nach den enthaltenen Premium-Credits.",
"user-name-limit-char": "Um einen Benutzernamen mit weniger als 8 Zeichen zu haben, abonnieren Sie bitte.",
@@ -142,6 +144,7 @@
"Quick send": "Schnell senden",
"Edit": "Bearbeiten",
"Share with team": "Mit Team teilen",
"Share with user": "Mit Kollegen teilen",
"Share": "Teilen",
"View": "Ansehen",
"option": "Option",
@@ -245,6 +248,7 @@
"API": "API",
"api-token": "API-Token",
"regenerate-token": "Live-Token neu generieren",
"remove-background": "Hintergrund entfernen",
"generate-token": "Live-Token generieren",
"view-docs": "Dokumentation ansehen",
"generate-token-alert": "Sind Sie sicher, dass Sie das Token neu generieren möchten? Das alte Token wird ablaufen.",
@@ -478,6 +482,7 @@
"document-alert": "Dokument-Warnung",
"owner-subscription-expired": "Das Abonnement des Besitzers ist abgelaufen.",
"subscription-expired": "Abonnement abgelaufen",
"owner-doesnt-have-paid-plan": "Der Inhaber hat keinen kostenpflichtigen Plan.",
"alert-message": "Warnmeldung",
"document-decline": "Dokument ablehnen",
"decline-alert-1": "Sind Sie sicher, dass Sie dieses Dokument ablehnen möchten?",
@@ -971,5 +976,9 @@
"kiosk-sign": "Kiosk-Unterschrift",
"dont-have-access-to-template": "Das template wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.",
"kiosk-info": "Kiosk Modus ermöglicht es Ihnen, persönliche Unterschriften schnell und effizient zu erfassen. Ideal für Messen, Veranstaltungen oder Laufkundschaft, bei denen alle Unterzeichner physisch anwesend sind. ",
"learn-more": "Mehr erfahren"
"learn-more": "Mehr erfahren",
"finish-mssg": "Sind Sie sicher, dass Sie das Dokument abschließen möchten?",
"review": "Überprüfen",
"next-field": "Nächstes Feld",
"required-mssg": "{{leftRequiredWidget}} von {{totalWidget}} Feldern übrig"
}
@@ -39,8 +39,10 @@
"save": "Save",
"cancel": "Cancel",
"upgrade-now": "Upgrade now",
"contact-now": "Contact now",
"upgrade-to": "Upgrade to",
"plan": "Plan",
"subscription-renew-warning": "Your subscription will expire in {{remainingDays}} days. Please renew your subscription.",
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
"user-name-limit-char": "To have a username less than 8 character please subscribe",
@@ -142,6 +144,7 @@
"Quick send": "Quick send",
"Edit": "Edit",
"Share with team": "Share with team",
"Share with user": "Share with colleague",
"Share": "Share",
"View": "View",
"option": "Option",
@@ -245,6 +248,7 @@
"API": "API",
"api-token": "API token",
"regenerate-token": "Regenerate live token",
"remove-background": "Remove Background",
"generate-token": "Generate live token",
"view-docs": "View docs",
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
@@ -478,6 +482,7 @@
"document-alert": "Document alert",
"owner-subscription-expired": "Owner's subscription has expired.",
"subscription-expired": "Subscription Expired",
"owner-doesnt-have-paid-plan": "Owner doesn't have paid plan.",
"alert-message": "Alert message",
"document-decline": "Document decline",
"decline-alert-1": "Are you sure want to decline this document ?",
@@ -971,5 +976,9 @@
"kiosk-sign": "Kiosk Sign",
"dont-have-access-to-template": "The template has been deleted or you don't have access. Please contact the sender.",
"kiosk-info": "Kiosk Mode lets you collect in-person signatures quickly and efficiently. Ideal for trade shows, events, or walk-in scenarios where all signers are physically present. ",
"learn-more": "Learn more"
"learn-more": "Learn more",
"finish-mssg":" Are you sure you want to finish the document ?",
"review":"Review",
"next-field":"Next Field",
"required-mssg":"{{leftRequiredWidget}} of {{totalWidget}} fields left"
}
@@ -39,8 +39,10 @@
"save": "Guardar",
"cancel": "Cancelar",
"upgrade-now": "Mejorar ahora",
"contact-now": "Contactar ahora",
"upgrade-to": "Mejorar a",
"plan": "Plan",
"subscription-renew-warning": "Su suscripción vencerá en {{remainingDays}} días. Por favor, renueve su suscripción.",
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
@@ -142,6 +144,7 @@
"Quick send": "Envío rápido",
"Edit": "Editar",
"Share with team": "Compartir con el equipo",
"Share with user": "Compartir con un colega",
"Share": "Compartir",
"View": "Ver",
"option": "Opción",
@@ -246,6 +249,7 @@
"API": "API",
"api-token": "Token API",
"regenerate-token": "Regenerar token activo",
"remove-background": "Eliminar fondo",
"generate-token": "Generar token activo",
"view-docs": "Ver documentación",
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
@@ -477,6 +481,7 @@
"mail-not-delivered": "correo no entregado",
"document-alert": "Alerta de documento",
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
"owner-doesnt-have-paid-plan": "El propietario no tiene un plan de pago.",
"subscription-expired": "Suscripción expirada",
"alert-message": "Mensaje de alerta",
"document-decline": "Rechazar documento",
@@ -971,5 +976,9 @@
"kiosk-sign": "Firma en quiosco",
"dont-have-access-to-template": "El template ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.",
"kiosk-info": "El Modo Kiosco le permite recopilar firmas en persona de forma rápida y eficiente. Ideal para ferias, eventos o situaciones con personas que firman en el lugar. ",
"learn-more": "Más información"
"learn-more": "Más información",
"finish-mssg": "¿Está seguro de que desea finalizar el documento?",
"review": "Revisar",
"next-field": "Siguiente campo",
"required-mssg": "{{leftRequiredWidget}} de {{totalWidget}} campos restantes"
}
@@ -39,9 +39,11 @@
"save": "Sauvegarder",
"cancel": "Annuler",
"upgrade-now": "Mettre à jour maintenant",
"contact-now": "Contacter maintenant",
"upgrade-to": "Mettre à niveau vers",
"pro": "PRO",
"plan": "Offre",
"subscription-renew-warning": "Votre abonnement expirera dans {{remainingDays}} jours. Veuillez renouveler votre abonnement.",
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
@@ -163,6 +165,7 @@
"Quick send": "Envoi rapide",
"Edit": "Modifier",
"Share with team": "Partager avec l'équipe",
"Share with user": "Partager avec un collègue",
"Share": "Partager",
"View": "Voir",
"option": "Option",
@@ -245,6 +248,7 @@
"API": "API",
"api-token": "Jeton API",
"regenerate-token": "Régénérer en direct jeton",
"remove-background": "Supprimer l'arrière-plan",
"generate-token": "Générer en direct jeton",
"view-docs": "Afficher les documents",
"generate-token-alert": "Êtes-vous sûr de vouloir régénérer le jeton, votre ancien jeton sera supprimer?",
@@ -477,6 +481,7 @@
"mail-not-delivered": "Courrier non distribué",
"document-alert": "Alerte document",
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
"owner-doesnt-have-paid-plan": "Le propriétaire n'a pas de plan payant.",
"subscription-expired": "Abonnement expiré",
"alert-message": "Message d'alerte",
"document-decline": "Document-refusé",
@@ -971,5 +976,9 @@
"kiosk-sign": "Signature sur kiosque",
"dont-have-access-to-template": "Le template a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur.",
"kiosk-info": "Le Mode Kiosque vous permet de recueillir des signatures en personne rapidement et efficacement. Idéal pour les salons, événements ou situations où tous les signataires sont physiquement présents. ",
"learn-more": "En savoir plus"
"learn-more": "En savoir plus",
"finish-mssg": "Êtes-vous sûr de vouloir terminer le document ?",
"review": "Revoir",
"next-field": "Champ suivant",
"required-mssg":"{{leftRequiredWidget}} champs sur {{totalWidget}} restants"
}
@@ -39,8 +39,10 @@
"save": "Salva",
"cancel": "Annulla",
"upgrade-now": "Aggiorna ora",
"contact-now": "Contatta ora",
"upgrade-to": "Aggiorna a",
"plan": "Piano",
"subscription-renew-warning": "Il tuo abbonamento scadrà tra {{remainingDays}} giorni. Ti preghiamo di rinnovarlo.",
"subscribe-card-teamplan": "Sblocca tutto il potenziale della collaborazione! Crea organizzazioni, team e gerarchie illimitati. Condividi modelli senza problemi tra i team e assegna ruoli personalizzati agli utenti. Migliora il tuo flusso di lavoro oggi stesso!",
"subscribe-card-plan": "Sblocca le funzionalità premium a partire da soli {{premiumPrice}}/mese. Approfitta di prestazioni migliorate e paga solo {{addonPrice}} per ogni credito aggiuntivo dopo quelli inclusi.",
"user-name-limit-char": "Per un nome utente con meno di 8 caratteri, abbonati",
@@ -142,6 +144,7 @@
"Quick send": "Invio rapido",
"Edit": "Modifica",
"Share with team": "Condividi con il team",
"Share with user": "Condividi con un collega",
"Share": "Condividi",
"View": "Visualizza",
"option": "Opzione",
@@ -245,6 +248,7 @@
"API": "API",
"api-token": "Token API",
"regenerate-token": "Rigenera token live",
"remove-background": "Rimuovi sfondo",
"generate-token": "Genera token live",
"view-docs": "Visualizza documenti",
"generate-token-alert": "Sei sicuro di voler rigenerare il token? Questo invaliderà il vecchio token.",
@@ -477,6 +481,7 @@
"mail-not-delivered": "Mail non consegnata",
"document-alert": "Avviso Documento",
"owner-subscription-expired": "L'abbonamento del proprietario è scaduto.",
"owner-doesnt-have-paid-plan": "Il proprietario non ha un piano a pagamento.",
"subscription-expired": "Abbonamento Scaduto",
"alert-message": "Messaggio di avviso",
"document-decline": "Documento rifiutato",
@@ -971,5 +976,9 @@
"kiosk-sign": "Firma su chiosco",
"dont-have-access-to-template": "Il template è stato eliminato o non hai accesso. Si prega di contattare il mittente.",
"kiosk-info": "La Modalità Kiosk consente di raccogliere firme in presenza in modo rapido ed efficiente. Ideale per fiere, eventi o situazioni con firmatari fisicamente presenti. ",
"learn-more": "Scopri di più"
"learn-more": "Scopri di più",
"finish-mssg": "Sei sicuro di voler completare il documento?",
"review": "Rivedere",
"next-field": "Campo successivo",
"required-mssg":"{{leftRequiredWidget}} di {{totalWidget}} campi rimanenti"
}
Binary file not shown.
-1
View File
@@ -1,4 +1,3 @@
import React from "react";
import { Helmet } from "react-helmet";
function Title({ title, drive }) {
@@ -1,10 +1,10 @@
import React, { useState, useEffect, useRef } from "react";
import "../../styles/opensigndrive.css";
import axios from "axios";
import * as ContextMenu from "@radix-ui/react-context-menu";
import { ContextMenu } from "radix-ui";
import { useNavigate } from "react-router";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import { HoverCard } from "radix-ui";
import ModalUi from "../../primitives/ModalUi";
import FolderModal from "../shared/fields/FolderModal";
import { useTranslation } from "react-i18next";
@@ -1,7 +1,10 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
function DefaultSignature(props) {
const { t } = useTranslation();
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
const myInitial = useSelector((state) => state.widget.myInitial)
const tabName = ["my-signature", "my-initials"];
const [activeTab, setActiveTab] = useState(0);
const confirmToaddDefaultSign = (type) => {
@@ -69,15 +72,15 @@ function DefaultSignature(props) {
<img
alt="signature"
className="w-full h-full object-contain"
src={props?.defaultSignImg}
src={defaultSignImg}
/>
) : (
activeTab === 1 &&
(props?.myInitial ? (
(myInitial ? (
<img
alt="signature"
className="w-full h-full object-contain"
src={props?.myInitial}
src={myInitial}
/>
) : (
<div className="flex justify-center items-center h-full">
@@ -97,7 +100,7 @@ function DefaultSignature(props) {
disabled={
activeTab === 0 && !props?.isDefault
? true
: activeTab === 1 && !props.myInitial
: activeTab === 1 && !myInitial
? true
: false
}
@@ -12,7 +12,7 @@ function DropdownWidgetOption(props) {
]);
const [minCount, setMinCount] = useState(0);
const [maxCount, setMaxCount] = useState(0);
const [dropdownName, setDropdownName] = useState(props.type);
const [dropdownName, setDropdownName] = useState();
const [isReadOnly, setIsReadOnly] = useState(false);
const [isHideLabel, setIsHideLabel] = useState(false);
const [status, setStatus] = useState("required");
@@ -22,7 +22,7 @@ function DropdownWidgetOption(props) {
const resetState = () => {
setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type);
setDropdownName( props.currWidgetsDetails?.options?.name || props.type);
setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0);
@@ -30,11 +30,10 @@ function DropdownWidgetOption(props) {
setDefaultCheckbox([]);
setDefaultValue("");
};
useEffect(() => {
if (
props.currWidgetsDetails?.options?.name &&
props.currWidgetsDetails?.options?.values
props.currWidgetsDetails?.options?.values?.length > 0
) {
setDropdownName(props.currWidgetsDetails?.options?.name);
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
@@ -116,16 +115,7 @@ function DropdownWidgetOption(props) {
defaultData,
isHideLabel
);
// props.setShowDropdown(false);
setDropdownOptionList(["option-1", "option-2"]);
setDropdownName(props.type);
// props.setCurrWidgetsDetails({});
setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0);
setMaxCount(0);
setDefaultCheckbox([]);
setDefaultValue("");
resetState()
};
@@ -137,7 +127,6 @@ function DropdownWidgetOption(props) {
return false;
}
};
return (
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
<div className="h-full p-[15px] text-base-content">
@@ -156,7 +145,6 @@ function DropdownWidgetOption(props) {
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
defaultValue={dropdownName}
value={dropdownName}
onChange={(e) => setDropdownName(e.target.value)}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -166,7 +154,7 @@ function DropdownWidgetOption(props) {
{t("options")}
</label>
<div className="flex flex-col">
{dropdownOptionList.map((option, index) => (
{dropdownOptionList?.map((option, index) => (
<div
key={index}
className="flex flex-row mb-[5px] items-center"
@@ -9,7 +9,7 @@ import {
handleToPrint
} from "../../constant/Utils";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { DropdownMenu } from "radix-ui";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next";
+87 -193
View File
@@ -21,6 +21,8 @@ import moment from "moment";
import "../../styles/opensigndrive.css";
import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { setIsShowModal } from "../../redux/reducers/widgetSlice";
const selectFormat = (data) => {
switch (data) {
@@ -72,27 +74,25 @@ const getDefaultDate = (dateStr, format) => {
};
function Placeholder(props) {
//'isTouchDevice' is used to detect whether a device has a touchscreen or is mouse-based
const isTouchDevice = navigator.maxTouchPoints > 0;
const { t } = useTranslation();
const dispatch = useDispatch();
const widgetData =
props.pos?.options?.defaultValue || props.pos?.options?.response;
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
const [isDateModal, setIsDateModal] = useState(false);
const [containerScale, setContainerScale] = useState();
const holdTimeout = useRef(null);
const startTime = useRef(null); // Track when the user starts holdings
const [isDisableDragging, setIsDisableDragging] = useState(true);
const [selectDate, setSelectDate] = useState({});
const [dateFormat, setDateFormat] = useState([]);
const [clickonWidget, setClickonWidget] = useState({});
const [startDate, setStartDate] = useState(
props?.pos?.options?.response
? getDefaultDate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
)
: new Date()
);
const startDate = props?.pos?.options?.response
? getDefaultDate(
props?.pos?.options?.response,
props.pos?.options?.validation?.format
)
: new Date();
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
width: null,
height: null
@@ -117,33 +117,28 @@ function Placeholder(props) {
];
useEffect(() => {
const updateWidth = () => {
if (props?.pos?.type === "date") {
const isDateChange = true;
const dateObj = {
date: startDate,
format: getDefaultFormat(props.pos?.options?.validation?.format)
};
handleSaveDate(dateObj, isDateChange); //function to save date and format in local array
}
}, [widgetData]);
const handleGetDaynamicWH = () => {
if (
props?.pos?.type === "checkbox" ||
props?.pos?.type === radioButtonWidget
) {
const rndElement = document.getElementById(props.pos.key);
if (rndElement) {
const { width, height } = rndElement.getBoundingClientRect();
setGetCheckboxRenderWidth({ width: width, height: height });
}
};
// Delay to ensure rendering is complete
const timer = setTimeout(updateWidth, 0);
return () => clearTimeout(timer);
}, [props.pos]);
useEffect(() => {
const onOutsideClick = () => {
if (!isDraggingEnabled) {
setDraggingEnabled(true);
}
};
document.addEventListener("click", onOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", onOutsideClick);
};
}, [isDraggingEnabled]);
}
};
//function change format array list with selected date and format
const changeDateFormat = () => {
const updateDate = [];
@@ -175,30 +170,18 @@ function Placeholder(props) {
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
const handleWidgetIdandPopup = async () => {
if (props.setSelectWidgetId) {
props.setSelectWidgetId(props.pos.key);
}
const widgetTypeExist = [
textInputWidget,
"checkbox",
"name",
"company",
"job title",
"date",
"email",
textWidget
].includes(props.pos.type);
if (widgetTypeExist) {
setDraggingEnabled(false);
}
if (props.isOpenSignPad && !props.isDragging) {
//'props.isOpenSignPad' variable is used to check flow to open signature pad for finish document
if (
props.isOpenSignPad &&
!props.isDragging &&
!props.pos.options?.isReadOnly &&
props.pos.type !== textWidget
) {
if (props?.ispublicTemplate) {
props.handleUserDetails();
} else {
if (props?.isNeedSign) {
//funcion is used to heightlight widgets on top if two widgets on overlap
//funcion is used to highlight widgets on top when click any widget if two widgets on overlap
const getCurrentSignerPos = props.xyPosition.find(
(x) => x.Id === props.uniqueId
);
@@ -212,23 +195,14 @@ function Placeholder(props) {
);
props.setXyPosition(updatesignerPos);
}
if (
["signature", "stamp", "image", "initials"].includes(props.pos.type)
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
}
if (props.pos.type === "initials") {
props.setIsInitial(true);
}
dispatch(setIsShowModal({ [props.pos.key]: true }));
}
} else if (
props.isPlaceholder &&
!props.isDragging &&
props.pos.type !== textWidget
) {
if (props.pos.key === props.selectWidgetId) {
if (props.pos.key === props?.currWidgetsDetails?.key) {
props.handleLinkUser(props.data.Id);
props.setUniqueId(props.data.Id);
const checkIndex = props.xyPosition.findIndex(
@@ -236,31 +210,17 @@ function Placeholder(props) {
);
props.setIsSelectId(checkIndex || 0);
}
} else if (!props.pos.type) {
if (
!props.pos.type &&
props.isNeedSign &&
props.data.signerObjId === props.signerObjId
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (
(props.isNeedSign && props.pos.type === "signature") ||
props.pos.type === "stamp"
) {
props.setIsSignPad(true);
props.setSignKey(props.pos.key);
props.setIsStamp(props.pos.isStamp);
} else if (props.isNeedSign && props.pos.type === "dropdown") {
props.setSignKey(props.pos.key);
}
//handle prefill 'text widget' click then save previos in tem variable after save or close button again assign current selected userId
} else if (props.pos.type === textWidget) {
dispatch(setIsShowModal({ [props.pos.key]: true }));
props.setTempSignerId(props?.uniqueId);
props.setUniqueId(props?.data?.Id);
}
};
const widgetClickHandler = () => {
//The else condition opens the signature pad if it's a request signature flow and the user clicking is identified as a signer.
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
//condition to check in request signing flow user click on agree or not
if (props?.data?.signerObjId === props?.signerObjId && !props.isDragging) {
if (!props.isAgree && !props.isSelfSign) {
props.setIsAgreeTour && props.setIsAgreeTour(true);
@@ -272,7 +232,10 @@ function Placeholder(props) {
handleWidgetIdandPopup();
}
};
const handleOnClickPlaceholder = () => {
//'props.isDragging' variable is used to checking if user take any widget and try to drag then in that case
//onclick event call. so to prevent onclick and do not open unecessary modal open.
//condition only for request signing flow and self signing flow then apply one click copy sign url of previous drawn signature
if (props.isApplyAll) {
props.setRequestSignTour && props.setRequestSignTour(true);
@@ -342,13 +305,11 @@ function Placeholder(props) {
} else {
widgetClickHandler();
}
// }
// }
} else {
//The else condition is used to handle the case when the user clicks on a widget and open signature pad to draw sign
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
props.setWidgetType(props.pos.type);
handleWidgetIdandPopup();
handleGetDaynamicWH();
}
};
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
@@ -391,8 +352,6 @@ function Placeholder(props) {
props.setTempSignerId(props.uniqueId);
props.setUniqueId(props?.data?.Id);
}
props.setSignKey(props.pos.key);
props.setWidgetType(props.pos.type);
props.setCurrWidgetsDetails(props.pos);
};
//function to set required state value onclick on widget's copy icon
@@ -425,7 +384,7 @@ function Placeholder(props) {
)
) {
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
props.setCurrWidgetsDetails(props.pos);
} else {
//function to create new widget next to just widget
handleCopyNextToWidget(
@@ -439,18 +398,6 @@ function Placeholder(props) {
}
};
//function to save date and format after seleted new date in response field and after finish document it should be emebed new selected date instead of current date
useEffect(() => {
if (props.pos.type === "date") {
const isDateChange = true;
const dateObj = {
date: startDate,
format: getDefaultFormat(props.pos?.options?.validation?.format)
};
handleSaveDate(dateObj, isDateChange); //function to save date and format in local array
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [startDate]);
//function to save date and format on local array onchange date and onclick format
const handleSaveDate = (data, isDateChange) => {
let updateDate = data.date;
@@ -476,8 +423,6 @@ function Placeholder(props) {
props.data && props.data.Id,
false,
data?.format,
null,
null,
props.fontSize || props.pos?.options?.fontSize || 12,
props.fontColor || props.pos?.options?.fontColor || "black"
);
@@ -586,7 +531,7 @@ function Placeholder(props) {
e.stopPropagation();
setClickonWidget(props.pos);
if (props.data) {
props.setSignKey(props.pos.key);
props.setCurrWidgetsDetails(props.pos);
props.setUniqueId(props.data.Id);
const checkIndex = props.xyPosition.findIndex(
(data) => data.Id === props.data.Id
@@ -599,7 +544,7 @@ function Placeholder(props) {
e.stopPropagation();
setIsDateModal(!isDateModal);
if (props.data) {
props.setSignKey(props.pos.key);
props.setCurrWidgetsDetails(props.pos);
props.setUniqueId(props.data.Id);
const checkIndex = props.xyPosition.findIndex(
(data) => data.Id === props.data.Id
@@ -636,7 +581,6 @@ function Placeholder(props) {
//condition for signyour-self flow
else {
props.handleDeleteSign(props.pos.key);
props.setIsStamp(false);
}
}}
//for mobile and tablet touch event
@@ -649,7 +593,6 @@ function Placeholder(props) {
//condition for signyour-self flow
else {
props.handleDeleteSign(props.pos.key);
props.setIsStamp(false);
}
}}
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
@@ -744,43 +687,7 @@ function Placeholder(props) {
return "all-scroll";
}
};
const handleDragging = () => {
//condition for request signing flow
if (props.isNeedSign) {
//enable dragging functionality only if isAlllowModify true on tab that widget and hold 1sec
if (
props.isAlllowModify &&
props?.assignedWidgetId.includes(props.pos.key) &&
props.data?.signerObjId === props.signerObjId
) {
//if 'isTouchDevice' then handle dragging functionality conditionaly
if (isTouchDevice) {
return isDisableDragging;
} else {
//no need to handle dragging functionality it auto enable and working or mouse click devices
return false;
}
} else if (
//condition when 'isAlllowModify' is true and user add new widgets then handle dragging functionality like signyourself flow
props.isAlllowModify &&
!props?.assignedWidgetId.includes(props.pos.key) &&
props.data?.signerObjId === props.signerObjId
) {
return !isDraggingEnabled;
} else {
//if 'isAlllowModify' is false then disbale dragging functionality
return true;
}
} //dragging enable in placeholder,template and not text widget flow
else if (props.isPlaceholder && ![textWidget].includes(props.pos.type)) {
return false;
} //dragging depend on 'isDraggingEnabled' variable in self sign and signyourself flow
else if (isTouchDevice) {
return !isDraggingEnabled;
} else {
return false;
}
};
//function to handle widget background color
const handleBackground = () => {
if (props.data) {
@@ -812,42 +719,38 @@ function Placeholder(props) {
}
if (!props.isNeedSign || props.isAlllowModify) handleOnClickPlaceholder();
};
const handleTouchStart = () => {
clearTimeout(holdTimeout.current); // Ensure no previous timeouts are running
startTime.current = Date.now(); // Store touch start time
holdTimeout.current = setTimeout(() => {
//handlle vibration and tab any widget and hold for 1 sec then show border outside widget and then user can able to drag
if (isDisableDragging) {
if (
props.isNeedSign &&
props.isAlllowModify &&
props?.assignedWidgetId.includes(props.pos.key)
) {
try {
navigator.vibrate(200); // Vibrate for 200ms
} catch (e) {
console.log("error in navigator.vibrate", e);
}
setIsDisableDragging(false);
props.setSelectWidgetId(props.pos.key);
} else if (!props.isNeedSign) {
setIsDisableDragging(false);
}
}
}, 1000); // Hold for 1 second before vibrating
};
const fontSize = calculateFont(props.pos.options?.fontSize);
const fontColor = props.pos.options?.fontColor || "black";
const handleDragging = () => {
//condition for request signing flow
if (props.isNeedSign) {
//enable dragging functionality only if isAlllowModify true on tab that widget and hold 1sec
if (
props.isAlllowModify &&
!props?.assignedWidgetId.includes(props.pos.key) &&
props.data?.signerObjId === props.signerObjId
) {
return false;
} else {
//if 'isAlllowModify' is false then disbale dragging functionality
return true;
}
} else {
return false;
}
};
return (
<>
{/* Check if a text widget (prefill type) exists. Once the user enters a value and clicks outside or the widget becomes non-selectable, it should appear as plain text (just like embedded text in a document). When the user clicks on the text again, it should become editable. */}
{props.pos?.options?.response &&
props.pos.key !== props.selectWidgetId &&
props.pos.key !== props?.currWidgetsDetails?.key &&
props.pos.type === textWidget ? (
<span
onClick={() => {
props.setSelectWidgetId && props.setSelectWidgetId(props.pos.key);
props.setCurrWidgetsDetails &&
props.setCurrWidgetsDetails(props.pos);
}}
style={{
fontFamily: "Arial, sans-serif",
@@ -896,7 +799,7 @@ function Placeholder(props) {
: false
: props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId &&
props.pos.key === props?.currWidgetsDetails?.key &&
true,
bottomLeft: false,
topLeft: false
@@ -909,7 +812,7 @@ function Placeholder(props) {
cursor: getCursor(),
zIndex:
props.pos.type === "date"
? props.pos.key === props.selectWidgetId
? props.pos.key === props?.currWidgetsDetails?.key
? 99 + 1
: 99
: props?.pos?.zIndex
@@ -920,7 +823,7 @@ function Placeholder(props) {
background: handleBackground()
}}
onDrag={() => {
setDraggingEnabled(true);
handleGetDaynamicWH();
props.handleTabDrag && props.handleTabDrag(props.pos.key);
}}
size={{
@@ -938,7 +841,6 @@ function Placeholder(props) {
minHeight={calculateFont(props.pos.options?.fontSize, true)}
maxHeight="auto"
onResizeStart={() => {
setDraggingEnabled(true);
props.setIsResize && props.setIsResize(true);
}}
onResizeStop={(e, direction, ref) => {
@@ -958,9 +860,7 @@ function Placeholder(props) {
props.isResize
);
}}
disableDragging={handleDragging()}
onDragStop={(event, dragElement) => {
setIsDisableDragging(true);
props.handleStop &&
props.handleStop(
event,
@@ -979,11 +879,9 @@ function Placeholder(props) {
h: ref.offsetHeight / (props.scale * containerScale)
});
}}
// onClick={() =>
// !props.isResize && !isMobile && handleOnClickPlaceholder()
// }
disableDragging={handleDragging()}
>
{props.pos.key === props.selectWidgetId &&
{props.pos.key === props?.currWidgetsDetails?.key &&
((props.isShowBorder &&
![radioButtonWidget, "checkbox"].includes(props.pos.type)) ||
(props?.isAlllowModify &&
@@ -1010,22 +908,20 @@ function Placeholder(props) {
)
) : (
![radioButtonWidget, "checkbox"].includes(props.pos.type) &&
props.pos.key === props.selectWidgetId && <BorderResize />
props.pos.key === props?.currWidgetsDetails?.key && <BorderResize />
)}
{/* 1- Show a border if props.pos.key === props.selectWidgetId, indicating the current user's selected widget.
{/* 1- Show a border if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
2- If props.isShowBorder is true, display borders for all widgets.
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show borders:
1- When isAlllowModify is true, show borders.
2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
*/}
{props.pos.key === props.selectWidgetId &&
{props.pos.key === props?.currWidgetsDetails?.key &&
(props.isShowBorder ||
!isDisableDragging ||
(props?.isAlllowModify &&
!props?.assignedWidgetId.includes(props.pos.key))) && (
<PlaceholderBorder
setDraggingEnabled={setDraggingEnabled}
pos={props.pos}
isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
@@ -1051,18 +947,18 @@ function Placeholder(props) {
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10"
}}
onTouchEnd={() => handleTouchEnd()}
onTouchEnd={() => handleOnClickPlaceholder()}
onClick={() => handleOnClickPlaceholder()}
onTouchStart={() => handleTouchStart()}
>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
{props.pos.key === props?.currWidgetsDetails?.key && (
<PlaceholderIcon />
)}
<PlaceholderType
pos={props.pos}
xyPosition={props.xyPosition}
index={props.index}
setXyPosition={props.setXyPosition}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
@@ -1073,8 +969,6 @@ function Placeholder(props) {
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
@@ -43,10 +43,6 @@ function PlaceholderBorder(props) {
};
return (
<div
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(false)}
onTouchEnd={() =>
props.pos.type === textWidget && props?.setDraggingEnabled(false)
}
className="absolute inline-block w-[14px] h-[14px] border-[0.2px] overflow-hidden border-dashed"
style={{
borderColor: themeColor,
@@ -2,19 +2,17 @@ import React, { useEffect, useState, forwardRef, useRef } from "react";
import {
getMonth,
getYear,
onChangeHeightOfTextArea,
onChangeInput,
radioButtonWidget,
range,
textInputWidget,
textWidget,
widgetDataValue
months,
years,
selectCheckbox,
checkRegularExpress
} from "../../constant/Utils";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css";
import RegexParser from "regex-parser";
import { emailRegex } from "../../constant/const";
import { useTranslation } from "react-i18next";
const textWidgetCls =
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
@@ -25,32 +23,19 @@ const widgetCls =
function PlaceholderType(props) {
const { t } = useTranslation();
const type = props?.pos?.type;
const iswidgetEnable =
props.isSignYourself ||
((props.isSelfSign || props.isNeedSign) &&
props.data?.signerObjId === props.signerObjId);
const widgetData =
props.pos?.options?.defaultValue || props.pos?.options?.response;
const widgetTypeTranslation = t(`widgets-name.${props?.pos?.type}`);
const [selectOption, setSelectOption] = useState("");
const [validatePlaceholder, setValidatePlaceholder] = useState("");
const inputRef = useRef(null);
const [textValue, setTextValue] = useState();
const [widgetValue, setwidgetValue] = useState();
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
const [hint, setHint] = useState("");
const years = range(1950, getYear(new Date()) + 16, 1);
const fontSize = props.calculateFont(props.pos.options?.fontSize);
const fontColor = props.pos.options?.fontColor || "black";
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
const textWidgetStyle = {
fontSize: fontSize,
color: fontColor,
@@ -61,89 +46,30 @@ function PlaceholderType(props) {
display: "flex",
height: "100%"
};
const validateExpression = (regexValidation) => {
if (textValue && regexValidation) {
let regexObject = regexValidation;
if (props.pos?.options?.validation?.type === "regex") {
regexObject = RegexParser(regexValidation);
}
// new RegExp(regexValidation);
let isValidate = regexObject.test(textValue);
if (!isValidate) {
props?.setValidateAlert(true);
inputRef.current.focus();
}
}
};
const handleInputBlur = () => {
const validateType = props.pos?.options?.validation?.type;
let regexValidation;
if (validateType && validateType !== "text") {
switch (validateType) {
case "email":
regexValidation = emailRegex;
validateExpression(regexValidation);
break;
case "number":
regexValidation = /^[0-9\s]*$/;
validateExpression(regexValidation);
break;
default:
regexValidation = props.pos?.options?.validation?.pattern || "";
validateExpression(regexValidation);
}
}
};
const handleTextValid = (e) => {
const textInput = e.target.value;
setTextValue(textInput);
};
function checkRegularExpress(validateType) {
switch (validateType) {
case "email":
setValidatePlaceholder("demo@gmail.com");
break;
case "number":
setValidatePlaceholder("12345");
break;
case "text":
setValidatePlaceholder("please enter text");
break;
default:
setValidatePlaceholder("please enter value");
}
}
useEffect(() => {
if (type && type === "checkbox" && props.isNeedSign) {
const isDefaultValue = props.pos.options?.defaultValue;
if (isDefaultValue) {
setSelectedCheckbox(isDefaultValue);
if (type !== "date") {
if (type && type === "checkbox") {
setSelectedCheckbox(
props?.pos?.options?.response ||
props?.pos?.options?.defaultValue ||
[]
);
} else {
if (widgetData) {
setwidgetValue(widgetData);
}
}
if (props.pos?.options?.hint) {
setHint(props.pos?.options.hint);
} else if (props.pos?.options?.validation?.type) {
checkRegularExpress(props.pos?.options?.validation?.type, setHint);
} else {
setHint(props.pos?.type);
}
} else if (props.pos?.options?.hint) {
setValidatePlaceholder(props.pos?.options.hint);
} else if (props.pos?.options?.validation?.type) {
checkRegularExpress(props.pos?.options?.validation?.type);
}
setTextValue(
props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
);
setSelectOption(
props.pos?.options?.response
? props.pos?.options?.response
: props.pos?.options?.defaultValue
? props.pos?.options?.defaultValue
: ""
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [props.pos]);
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div
style={{
@@ -160,53 +86,10 @@ function PlaceholderType(props) {
</div>
));
ExampleCustomInput.displayName = "ExampleCustomInput";
useEffect(() => {
if (
["name", "email", "job title", "company", textInputWidget].includes(
type
) &&
props.isNeedSign &&
props.data?.signerObjId === props.signerObjId
) {
if (widgetData) {
setTextValue(widgetData);
}
}
if (props.pos?.options?.hint) {
setHint(props.pos?.options.hint);
} else {
setHint(props.pos?.type);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [type, widgetData]);
//function for show checked checkbox
const selectCheckbox = (ind) => {
const res = props.pos.options?.response;
const defaultCheck = props.pos.options?.defaultValue;
if (res && res?.length > 0) {
const isSelectIndex = res.indexOf(ind);
if (isSelectIndex > -1) {
return true;
} else {
return false;
}
// }
} else if (defaultCheck) {
const isSelectIndex = defaultCheck.indexOf(ind);
if (isSelectIndex > -1) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const handleRadioCheck = (data) => {
const defaultData = props.pos.options?.defaultValue;
if (textValue === data) {
if (widgetValue === data) {
return true;
} else if (defaultData === data) {
return true;
@@ -215,90 +98,22 @@ function PlaceholderType(props) {
}
};
//function for set checked and unchecked value of checkbox
const handleCheckboxValue = (isChecked, ind) => {
let updateSelectedCheckbox = [],
checkedList;
let isDefaultValue, isDefaultEmpty;
if (type === "checkbox") {
updateSelectedCheckbox = selectedCheckbox ? selectedCheckbox : [];
if (isChecked) {
updateSelectedCheckbox.push(ind);
setSelectedCheckbox(updateSelectedCheckbox);
} else {
checkedList = selectedCheckbox.filter((data) => data !== ind);
setSelectedCheckbox(checkedList);
}
if (props.isNeedSign) {
isDefaultValue = props.pos.options?.defaultValue;
}
if (isDefaultValue && isDefaultValue.length > 0) {
isDefaultEmpty = true;
}
onChangeInput(
checkedList ? checkedList : updateSelectedCheckbox,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data.Id,
false,
null,
isDefaultEmpty
);
}
};
//function to handle select radio widget and set value seletced by user
const handleCheckRadio = (isChecked, data) => {
let isDefaultValue,
isDefaultEmpty,
isRadio = true;
if (props.isNeedSign) {
isDefaultValue = props.pos.options?.defaultValue;
}
if (isDefaultValue) {
isDefaultEmpty = true;
}
if (isChecked) {
setTextValue(data);
} else {
setTextValue("");
}
onChangeInput(
data,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data.Id,
false,
null,
isDefaultEmpty,
isRadio
);
};
//function to set onchange date
const handleOnDateChange = (date) => {
props.setStartDate(date);
};
//handle height on enter press in text area
const handleEnterPress = (e) => {
const height = 18;
if (e.key === "Enter") {
//function to save height of text area
onChangeHeightOfTextArea(
height,
props.pos.type,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id
);
}
};
// const handleEnterPress = (e) => {
// const height = 18;
// if (e.key === "Enter") {
// //function to save height of text area
// onChangeHeightOfTextArea(
// height,
// props.pos.type,
// props.pos.key,
// props.xyPosition,
// props.index,
// props.setXyPosition,
// props.data && props.data?.Id
// );
// }
// };
switch (type) {
case "signature":
return props.pos.SignUrl ? (
@@ -319,9 +134,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation
: widgetTypeTranslation}
{hint || widgetTypeTranslation}
</div>
)}
</div>
@@ -345,9 +158,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation
: widgetTypeTranslation}
{hint || widgetTypeTranslation}
</div>
)}
</div>
@@ -359,7 +170,7 @@ function PlaceholderType(props) {
return (
<div
key={ind}
className="select-none-cls flex items-center text-center gap-0.5"
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
>
<input
id={`checkbox-${props.pos.key + ind}`}
@@ -367,37 +178,14 @@ function PlaceholderType(props) {
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox rounded-[1px] `}
onBlur={handleInputBlur}
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
type="checkbox"
checked={selectCheckbox(ind)}
onChange={(e) => {
if (e.target.checked) {
if (!props.isPlaceholder) {
const maxRequired =
props.pos.options?.validation?.maxRequiredCount;
const maxCountInt =
maxRequired && parseInt(maxRequired);
if (maxCountInt > 0) {
if (
selectedCheckbox &&
selectedCheckbox?.length <= maxCountInt - 1
) {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
}}
readOnly
checked={!!selectCheckbox(ind, selectedCheckbox)}
/>
{!props.pos.options?.isHideLabel && (
<label
@@ -414,40 +202,25 @@ function PlaceholderType(props) {
</div>
);
case textInputWidget:
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return props.isSignYourself || iswidgetEnable ? (
<textarea
ref={inputRef}
placeholder={validatePlaceholder || t("widgets-name.text")}
placeholder={hint || t("widgets-name.text")}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
value={widgetValue}
className={`${
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId
? "select-none"
: "" + textWidgetCls
: textWidgetCls
}`}
style={{
fontSize: fontSize,
color: fontColor,
background: props.data?.blockColor
background: props.data?.blockColor,
pointerEvents: "none"
}}
readOnly
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
@@ -457,73 +230,20 @@ function PlaceholderType(props) {
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{textValue || widgetTypeTranslation}</span>
<span>{hint || widgetTypeTranslation}</span>
</div>
);
case "dropdown":
return props.data?.signerObjId === props.signerObjId ? (
<select
style={{ fontSize: fontSize, color: fontColor }}
className={`${
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
? " disabled:bg-inherit select-none "
: "" + `${selectWidgetCls} text-[12px] bg-inherit`
}`}
id="myDropdown"
value={selectOption}
onChange={(e) => {
setSelectOption(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
>
{/* Default/Title option */}
<option
style={{ fontSize: fontSize, color: fontColor }}
value=""
disabled
hidden
>
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values?.map((data, ind) => {
return (
<option
style={{ fontSize: fontSize, color: fontColor }}
key={ind}
value={data}
>
{data}
</option>
);
})}
</select>
) : (
return (
<div
style={textWidgetStyle}
className="select-none-cls flex justify-between items-center"
>
{props.pos?.options?.name
? props.pos.options.name
: widgetTypeTranslation}
{widgetData || hint || widgetTypeTranslation}
<i className="fa-light fa-circle-chevron-down mr-1 "></i>
</div>
);
case "initials":
return props.pos.SignUrl ? (
<img
@@ -543,111 +263,76 @@ function PlaceholderType(props) {
}}
className="font-medium text-center"
>
{props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation
: widgetTypeTranslation}
{hint || widgetTypeTranslation}
</div>
)}
</div>
);
case "name":
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return iswidgetEnable ? (
<textarea
readOnly
ref={inputRef}
placeholder={hint || widgetTypeTranslation}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
const isDefault = false;
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
isDefault
);
}}
value={widgetValue}
className={textWidgetCls}
style={{ fontSize: fontSize, color: fontColor }}
style={{
fontSize: fontSize,
color: fontColor,
pointerEvents: "none"
}}
cols="50"
/>
) : (
<div className="flex h-full select-none-cls" style={textWidgetStyle}>
<span>{widgetTypeTranslation}</span>
<span> {props.pos?.options?.hint || widgetTypeTranslation}</span>
</div>
);
case "company":
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return iswidgetEnable ? (
<textarea
readOnly
ref={inputRef}
placeholder={hint || widgetTypeTranslation}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
value={widgetValue}
className={textWidgetCls}
style={{ fontSize: fontSize, color: fontColor }}
style={{
fontSize: fontSize,
color: fontColor,
pointerEvents: "none"
}}
cols="50"
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span>
<span>{hint || widgetTypeTranslation}</span>
</div>
);
case "job title":
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return iswidgetEnable ? (
<textarea
readOnly
ref={inputRef}
placeholder={hint || widgetTypeTranslation}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
value={widgetValue}
className={textWidgetCls}
style={{ fontSize: fontSize, color: fontColor }}
style={{
fontSize: fontSize,
color: fontColor,
pointerEvents: "none"
}}
cols="50"
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span>
<span>{hint || widgetTypeTranslation}</span>
</div>
);
case "date":
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return iswidgetEnable ? (
<DatePicker
renderCustomHeader={({ date, changeYear, changeMonth }) => (
<div className="flex justify-start ml-2 ">
@@ -677,15 +362,10 @@ function PlaceholderType(props) {
</select>
</div>
)}
disabled={
props.isPlaceholder ||
(props.isNeedSign && props.data?.signerObjId !== props.signerObjId)
}
onBlur={handleInputBlur}
disabled={true}
closeOnScroll={true}
className={`${selectWidgetCls} outline-[#007bff]`}
selected={props?.startDate}
onChange={(date) => handleOnDateChange(date)}
popperPlacement="top-end"
customInput={<ExampleCustomInput />}
dateFormat={
@@ -729,52 +409,32 @@ function PlaceholderType(props) {
}}
className="font-medium text-center"
>
{props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation
: widgetTypeTranslation}
{hint || widgetTypeTranslation}
</div>
)}
</div>
);
case "email":
return props.isSignYourself ||
(props.isSelfSign && props.data?.signerObjId === props.signerObjId) ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
return iswidgetEnable ? (
<textarea
readOnly
ref={inputRef}
placeholder={hint || widgetTypeTranslation}
rows={1}
onKeyDown={(e) => {
// Prevent new line on Enter key press
if (e.key === "Enter") {
e.preventDefault();
}
}}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
value={widgetValue}
className={textWidgetCls}
style={{
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif"
fontFamily: "Arial, sans-serif",
pointerEvents: "none"
}}
disabled
cols="1"
/>
) : (
<div style={textWidgetStyle} className="select-none-cls">
<span>{widgetTypeTranslation}</span>
<span>{hint || widgetTypeTranslation}</span>
</div>
);
case radioButtonWidget:
@@ -784,16 +444,19 @@ function PlaceholderType(props) {
return (
<div
key={ind}
className="select-none-cls flex items-center text-center gap-0.5"
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
>
<input
readOnly
id={`radio-${props.pos.key + ind}`}
style={{
width: fontSize,
height: fontSize,
marginTop: ind > 0 ? "10px" : "0px"
}}
className={`flex justify-center op-radio`}
className={`op-radio rounded-full border- border-black appearance-none bg-white inline-block align-middle relative ${
handleRadioCheck(data) ? "checked-radio" : ""
}`}
type="radio"
disabled={
props.isNeedSign &&
@@ -801,11 +464,6 @@ function PlaceholderType(props) {
props.data?.signerObjId !== props.signerObjId)
}
checked={handleRadioCheck(data)}
onChange={(e) => {
if (!props.isPlaceholder) {
handleCheckRadio(e.target.checked, data);
}
}}
/>
{!props.pos.options?.isHideLabel && (
<label
@@ -824,23 +482,10 @@ function PlaceholderType(props) {
case textWidget:
return (
<textarea
readOnly
placeholder={t("widgets-name.text")}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
onChangeInput(
e.target.value,
props.pos.key,
props.xyPosition,
props.index,
props.setXyPosition,
props.data && props.data?.Id,
false
);
}}
value={widgetValue}
className={textWidgetCls}
style={{
fontFamily: "Arial, sans-serif",
@@ -872,9 +517,7 @@ function PlaceholderType(props) {
}}
className="font-medium"
>
{props.isNeedSign
? props.pos?.options?.hint || widgetTypeTranslation
: widgetTypeTranslation}
{hint || widgetTypeTranslation}
</div>
)}
</div>
+18 -46
View File
@@ -95,15 +95,12 @@ function RenderPdf(props) {
return (
<React.Fragment key={key}>
{placeData.pageNumber === props.pageNumber &&
placeData.pos.map((pos) => {
placeData.pos.map((pos, ind) => {
return (
pos && (
<React.Fragment key={pos.key}>
<React.Fragment key={ind}>
<Placeholder
pos={pos}
setSignKey={props.setSignKey}
setIsSignPad={props.setIsSignPad}
setIsStamp={props.setIsStamp}
handleSignYourselfImageResize={handleImageResize}
index={props.pageNumber}
xyPosition={props.signerPos}
@@ -121,11 +118,7 @@ function RenderPdf(props) {
posHeight={posHeight}
isDragging={props.isDragging}
pdfDetails={props.pdfDetails}
setIsInitial={props.setIsInitial}
setValidateAlert={props.setValidateAlert}
unSignedWidgetId={props.unSignedWidgetId}
setSelectWidgetId={props.setSelectWidgetId}
selectWidgetId={props.selectWidgetId}
setCurrWidgetsDetails={props.setCurrWidgetsDetails}
uniqueId={props.uniqueId}
scale={props.scale}
@@ -139,7 +132,6 @@ function RenderPdf(props) {
isAgree={props.isAgree}
handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop}
setWidgetType={props.setWidgetType}
setUniqueId={props.setUniqueId}
setIsSelectId={props.setIsSelectId}
handleDeleteSign={props.handleDeleteSign}
@@ -156,6 +148,8 @@ function RenderPdf(props) {
setFontColor={props.setFontColor}
setRequestSignTour={props.setRequestSignTour}
calculateFontsize={calculateFontsize}
currWidgetsDetails={props?.currWidgetsDetails}
setTempSignerId={props.setTempSignerId}
/>
</React.Fragment>
)
@@ -241,11 +235,9 @@ function RenderPdf(props) {
<Placeholder
pos={pos}
setIsPageCopy={props.setIsPageCopy}
setSignKey={props.setSignKey}
handleDeleteSign={
props.handleDeleteSign
}
setIsStamp={props.setIsStamp}
handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop}
handleSignYourselfImageResize={
@@ -270,18 +262,11 @@ function RenderPdf(props) {
posHeight={posHeight}
isDragging={props.isDragging}
setIsValidate={props.setIsValidate}
setWidgetType={props.setWidgetType}
setIsRadio={props.setIsRadio}
setIsCheckbox={props.setIsCheckbox}
setCurrWidgetsDetails={
props.setCurrWidgetsDetails
}
setSelectWidgetId={
props.setSelectWidgetId
}
selectWidgetId={
props.selectWidgetId
}
handleNameModal={
props.handleNameModal
}
@@ -309,6 +294,9 @@ function RenderPdf(props) {
calculateFontsize={
calculateFontsize
}
currWidgetsDetails={
props?.currWidgetsDetails
}
/>
</React.Fragment>
);
@@ -331,9 +319,7 @@ function RenderPdf(props) {
key={id}
pos={pos}
setIsPageCopy={props.setIsPageCopy}
setSignKey={props.setSignKey}
handleDeleteSign={props.handleDeleteSign}
setIsStamp={props.setIsStamp}
handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop}
handleSignYourselfImageResize={
@@ -343,19 +329,13 @@ function RenderPdf(props) {
xyPosition={props.xyPosition}
setXyPosition={props.setXyPosition}
containerWH={props.containerWH}
setIsSignPad={props.setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={props.pdfDetails[0]}
isDragging={props.isDragging}
setIsInitial={props.setIsInitial}
setWidgetType={props.setWidgetType}
setSelectWidgetId={props.setSelectWidgetId}
selectWidgetId={props.selectWidgetId}
setIsCheckbox={props.setIsCheckbox}
setValidateAlert={props.setValidateAlert}
setCurrWidgetsDetails={
props.setCurrWidgetsDetails
}
@@ -374,6 +354,9 @@ function RenderPdf(props) {
isFreeResize={false}
isOpenSignPad={true}
calculateFontsize={calculateFontsize}
currWidgetsDetails={
props?.currWidgetsDetails
}
/>
)
);
@@ -388,7 +371,7 @@ function RenderPdf(props) {
loading={t("loading-doc")}
onLoadSuccess={props.pageDetails}
onClick={() =>
props.setSelectWidgetId && props.setSelectWidgetId("")
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({})
}
file={pdfDataBase64}
>
@@ -454,11 +437,9 @@ function RenderPdf(props) {
<Placeholder
pos={pos}
setIsPageCopy={props.setIsPageCopy}
setSignKey={props.setSignKey}
handleDeleteSign={
props.handleDeleteSign
}
setIsStamp={props.setIsStamp}
handleTabDrag={props.handleTabDrag}
handleStop={props.handleStop}
handleSignYourselfImageResize={
@@ -483,18 +464,11 @@ function RenderPdf(props) {
posHeight={posHeight}
isDragging={props.isDragging}
setIsValidate={props.setIsValidate}
setWidgetType={props.setWidgetType}
setIsRadio={props.setIsRadio}
setIsCheckbox={props.setIsCheckbox}
setCurrWidgetsDetails={
props.setCurrWidgetsDetails
}
setSelectWidgetId={
props.setSelectWidgetId
}
selectWidgetId={
props.selectWidgetId
}
handleNameModal={
props.handleNameModal
}
@@ -522,6 +496,9 @@ function RenderPdf(props) {
calculateFontsize={
calculateFontsize
}
currWidgetsDetails={
props?.currWidgetsDetails
}
/>
</React.Fragment>
);
@@ -544,9 +521,7 @@ function RenderPdf(props) {
<Placeholder
pos={pos}
setIsPageCopy={props.setIsPageCopy}
setSignKey={props.setSignKey}
handleDeleteSign={props.handleDeleteSign}
setIsStamp={props.setIsStamp}
handleTabDrag={props.handleTabDrag}
handleStop={(event, dragElement) =>
props.handleStop(
@@ -561,19 +536,13 @@ function RenderPdf(props) {
index={props.index}
xyPosition={props.xyPosition}
setXyPosition={props.setXyPosition}
setIsSignPad={props.setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={props.pdfDetails[0]}
isDragging={props.isDragging}
setIsInitial={props.setIsInitial}
setWidgetType={props.setWidgetType}
setSelectWidgetId={props.setSelectWidgetId}
selectWidgetId={props.selectWidgetId}
setIsCheckbox={props.setIsCheckbox}
setValidateAlert={props.setValidateAlert}
setCurrWidgetsDetails={
props.setCurrWidgetsDetails
}
@@ -593,6 +562,9 @@ function RenderPdf(props) {
isFreeResize={false}
isOpenSignPad={true}
calculateFontsize={calculateFontsize}
currWidgetsDetails={
props?.currWidgetsDetails
}
/>
</React.Fragment>
);
@@ -608,7 +580,7 @@ function RenderPdf(props) {
loading={t("loading-doc")}
onLoadSuccess={props.pageDetails}
onClick={() =>
props.setSelectWidgetId && props.setSelectWidgetId("")
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({})
}
file={pdfDataBase64}
>
@@ -1,854 +0,0 @@
import React, { useRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import SignatureCanvas from "react-signature-canvas";
import Parse from "parse";
import {
generateTitleFromFilename,
getBase64FromUrl,
getSecureUrl
} from "../../constant/Utils";
import sanitizeFileName from "../../primitives/sanitizeFileName";
import { SaveFileSize } from "../../constant/saveFileSize";
import Loader from "../../primitives/Loader";
function SignPad(props) {
const { t } = useTranslation();
const [penColor, setPenColor] = useState("blue");
const allColor = ["blue", "red", "black"];
const canvasRef = useRef(null);
const [isDefaultSign, setIsDefaultSign] = useState(false);
const [isTab, setIsTab] = useState("");
const [isSignImg, setIsSignImg] = useState("");
const [textWidth, setTextWidth] = useState(0);
const [textHeight, setTextHeight] = useState(0);
const [signatureType, setSignatureType] = useState("");
const [isSignTypes, setIsSignTypes] = useState(true);
const [typedSignature, setTypedSignature] = useState("");
const fontOptions = [
{ value: "Fasthand" },
{ value: "Dancing Script" },
{ value: "Cedarville Cursive" },
{ value: "Delicious Handrawn" }
// Add more font options as needed
];
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
const [isSavedSign, setIsSavedSign] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const accesstoken = localStorage.getItem("accesstoken") || "";
const senderUser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = senderUser && JSON.parse(senderUser);
const currentUserName = jsonSender && jsonSender?.name;
useEffect(() => {
handleTab();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.signatureTypes]);
function handleTab() {
const signtypes = props?.signatureTypes || [];
const defaultIndex = signtypes?.findIndex(
(x) =>
x.name === "default" &&
x.enabled === true &&
props.defaultSign &&
(props?.currWidgetsDetails?.type || props.widgetType) !== "image" &&
(props?.currWidgetsDetails?.type || props.widgetType) !== "stamp"
);
const getIndex =
defaultIndex !== -1 // Check if the default index exists
? defaultIndex // If found, use it
: signtypes?.findIndex((x) => x.enabled === true);
if (getIndex !== -1) {
setIsSignTypes(true);
const tab = props?.signatureTypes[getIndex].name;
if (tab === "draw") {
setIsTab("draw");
setSignatureType("draw");
} else if (tab === "upload") {
props?.setIsImageSelect(true);
setIsTab("uploadImage");
} else if (tab === "typed") {
setIsTab("type");
} else if (tab === "default") {
if (
(props?.isInitial && props?.myInitial) ||
(!props?.isInitial && props?.defaultSign)
) {
setIsDefaultSign(true);
setIsTab("mysignature");
} else {
setIsTab("draw");
}
} else {
setIsTab(true);
}
} else {
setIsSignTypes(false);
}
}
function isTabEnabled(tabName) {
const isEnabled = props?.signatureTypes.find(
(x) => x.name === tabName
)?.enabled;
return isEnabled;
}
//function for clear signature image
const handleClear = () => {
if (isTab === "draw") {
if (canvasRef.current) {
canvasRef.current.clear();
} else if (props?.isStamp) {
props?.setImage("");
}
setIsSignImg("");
} else if (isTab === "uploadImage") {
props?.setImage("");
}
};
//function for set signature url
const handleSignatureChange = (data) => {
props?.setSignature(data);
setIsSignImg(data);
};
function base64StringtoFile(base64String, filename) {
let arr = base64String.split(","),
// type of uploaded image
mime = arr[0].match(/:(.*?);/)[1],
// decode base64
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const ext = mime.split("/").pop();
const name = `${filename}.${ext}`;
return new File([u8arr], name, { type: mime });
}
const uploadFile = async (file) => {
try {
const parseFile = new Parse.File(file.name, file);
const response = await parseFile.save();
if (response?.url()) {
const fileRes = await getSecureUrl(response.url());
if (fileRes.url) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(file.size, fileRes.url, tenantId);
return fileRes?.url;
} else {
alert(`${t("something-went-wrong-mssg")}`);
return false;
}
} else {
alert(`${t("something-went-wrong-mssg")}`);
return false;
}
} catch (err) {
console.log("sign upload err", err);
alert(`${err.message}`);
return false;
}
};
// `handlesavesign` is used to save signaute, initials, stamp as a default
const handleSaveSign = async () => {
if (isSignImg || props?.image?.src) {
setIsLoader(true);
try {
const User = Parse?.User?.current();
const sanitizename = generateTitleFromFilename(User?.get("name"));
const replaceSpace = sanitizeFileName(sanitizename);
let file;
if (isSignImg) {
file = base64StringtoFile(isSignImg, `${replaceSpace}__sign`);
} else {
file = base64StringtoFile(props?.image?.src, `${replaceSpace}__sign`);
}
const imageUrl = await uploadFile(file);
const userId = {
__type: "Pointer",
className: "_User",
objectId: User?.id
};
if (imageUrl) {
// below code is used to save or update default signaute, initials, stamp
try {
const signCls = new Parse.Object("contracts_Signature");
if (props?.saveSignCheckbox?.signId) {
signCls.id = props.saveSignCheckbox.signId;
}
if (
props.currWidgetsDetails?.type === "initials" ||
props?.widgetType === "initials"
) {
signCls.set("Initials", imageUrl);
} else if (
props.currWidgetsDetails?.type === "signature" ||
props?.widgetType === "signature"
) {
signCls.set("ImageURL", imageUrl);
}
signCls.set("UserId", userId);
const signRes = await signCls.save();
if (signRes) {
props.saveSignCheckbox.signId;
props.setSaveSignCheckbox((prev) => ({
...prev,
signId: signRes?.id
}));
const _signRes = JSON.parse(JSON.stringify(signRes));
if (
props.currWidgetsDetails?.type === "signature" ||
props?.widgetType === "signature"
) {
const defaultSign = await getBase64FromUrl(
_signRes?.ImageURL,
true
);
props.setDefaultSignImg(defaultSign);
} else if (
props.currWidgetsDetails?.type === "initials" ||
props?.widgetType === "initials"
) {
const defaultInitials = await getBase64FromUrl(
_signRes?.Initials,
true
);
props.setMyInitial(defaultInitials);
}
alert(t("saved-successfully"));
}
return signRes;
} catch (err) {
console.log(err);
alert(`${err.message}`);
} finally {
setIsLoader(false);
}
}
} catch (err) {
console.log("Err while saving signature", err);
}
}
};
const handleSaveBtn = async () => {
if (accesstoken && isSavedSign) {
await handleSaveSign();
resetToDefault();
} else {
resetToDefault();
}
};
const resetToDefault = () => {
props?.setCurrWidgetsDetails({});
if (!props?.image) {
if (isTab === "mysignature") {
setIsSignImg("");
if (props?.isInitial) {
props?.onSaveSign(signatureType, "initials");
} else {
props?.onSaveSign(null, "default");
}
} else {
if (isTab === "type") {
setIsSignImg("");
props?.onSaveSign(
null,
false,
!props?.isInitial && textWidth > 150 ? 150 : textWidth,
!props?.isInitial && textHeight > 35 ? 35 : textHeight,
typedSignature
);
} else {
setIsSignImg("");
canvasRef.current.clear();
props?.onSaveSign(signatureType);
}
}
setPenColor("blue");
} else {
setIsSignImg("");
props?.onSaveImage(signatureType);
}
props?.setIsSignPad(false);
props?.setIsInitial && props?.setIsInitial(false);
props?.setIsImageSelect(false);
setIsDefaultSign(false);
props?.setImage();
handleTab();
props?.setIsStamp(false);
};
//save button component
const SaveBtn = () => {
return (
<div>
{(isTab === "draw" || isTab === "uploadImage") && (
<button
type="button"
className="op-btn op-btn-ghost mr-1 mt-[2px]"
onClick={() => handleClear()}
>
{t("clear")}
</button>
)}
<button
onClick={() => handleSaveBtn()}
type="button"
className={`${
isSignImg ||
props?.image ||
isDefaultSign ||
textWidth ||
props.isAutoSign
? ""
: "pointer-events-none"
} op-btn op-btn-primary shadow-lg`}
disabled={
(isTab === "draw" && isSignImg) ||
(isTab === "image" && props?.image) ||
(isTab === "mysignature" && isDefaultSign) ||
(isTab === "type" && typedSignature) ||
props.isAutoSign
? false
: props?.image
? false
: true
}
>
{t("save")}
</button>
</div>
);
};
const autoSignAll = () => {
return (
<label className="cursor-pointer flex items-center mb-[6px] text-center text-[11px] md:text-base">
<input
className="mr-2 md:mr-3 op-checkbox op-checkbox-xs md:op-checkbox-sm"
type="checkbox"
value={props.isAutoSign}
onChange={(e) => {
props.setIsAutoSign(e.target.checked);
}}
/>
{t("auto-sign-mssg")}
</label>
);
};
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
useEffect(() => {
if (props?.currWidgetsDetails && canvasRef.current && props.isSignPad) {
const isWidgetType = props?.currWidgetsDetails?.type;
const signatureType = props?.currWidgetsDetails?.signatureType;
const url = props?.currWidgetsDetails?.SignUrl;
//checking widget type and draw type signature url
if (props?.isInitial) {
if (isWidgetType === "initials" && signatureType === "draw" && url) {
canvasRef.current.fromDataURL(url);
}
} else if (
isWidgetType === "signature" &&
signatureType === "draw" &&
url
) {
canvasRef.current.fromDataURL(url);
}
const trimmedName = currentUserName && currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = props?.isInitial ? firstCharacter : currentUserName;
const signatureValue = props?.currWidgetsDetails?.typeSignature;
setTypedSignature(signatureValue || userName || "");
setFontSelect("Fasthand");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.isSignPad]);
useEffect(() => {
const loadFont = async () => {
try {
await document.fonts.load(`20px ${fontSelect}`);
const selectFontSTyle = fontOptions.find(
(font) => font.value === fontSelect
);
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
} catch (error) {
console.error("Error loading font:", error);
}
};
loadFont();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fontSelect]);
useEffect(() => {
// Load the default signature after the component mounts
if (canvasRef.current) {
canvasRef.current.fromDataURL(isSignImg);
}
if (isTab === "type") {
const trimmedName = typedSignature
? typedSignature?.trim()
: currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = props?.isInitial ? firstCharacter : typedSignature;
const signatureValue = props?.currWidgetsDetails?.typeSignature;
setTypedSignature(signatureValue || userName || "");
convertToImg(fontSelect, userName);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isTab]);
//function for convert input text value in image
const convertToImg = async (fontStyle, text, color) => {
//get text content to convert in image
const textContent = text;
const fontfamily = fontStyle
? fontStyle
: fontSelect
? fontSelect
: "Fasthand";
const fontSizeValue = "40px";
//creating span for getting text content width
const span = document.createElement("span");
span.textContent = textContent;
span.style.font = `${fontSizeValue} ${fontfamily}`; // here put your text size and font family
span.style.color = color ? color : penColor;
span.style.display = "hidden";
document.body.appendChild(span); // Replace 'container' with the ID of the container element
//create canvas to render text in canvas and convert in image
const canvasElement = document.createElement("canvas");
// Draw the text content on the canvas
const ctx = canvasElement.getContext("2d");
const pixelRatio = window.devicePixelRatio || 1;
const addExtraWidth = props?.isInitial ? 10 : 50;
const width = span.offsetWidth + addExtraWidth;
const height = span.offsetHeight;
setTextWidth(width);
setTextHeight(height);
const font = span.style["font"];
// Set the canvas dimensions to match the span
canvasElement.width = width * pixelRatio;
canvasElement.height = height * pixelRatio;
// You can customize text styles if needed
ctx.font = font;
ctx.fillStyle = color ? color : penColor; // Set the text color
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.scale(pixelRatio, pixelRatio);
// Draw the content of the span onto the canvas
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
//remove span tag
document.body.removeChild(span);
// Convert the canvas to image data
const dataUrl = canvasElement.toDataURL("image/png");
props?.setSignature(dataUrl);
};
const PenColorComponent = (props) => {
return (
<div className="flex flex-row items-center m-[5px] gap-2">
{allColor.map((data, key) => {
return (
<i
key={key}
onClick={() => {
props?.convertToImg &&
props?.convertToImg(fontSelect, typedSignature, data);
setPenColor(allColor[key]);
}}
className={`border-b-[2px] ${key === 0 && penColor === "blue" ? "border-blue-600" : key === 1 && penColor === "red" ? "border-red-500" : key === 2 && penColor === "black" ? "border-black" : "border-white"} text-[${data}] text-[16px] fa-light fa-pen-nib`}
></i>
);
})}
</div>
);
};
// `handleCancelBtn` function trigger when user click on cross button
const handleCancelBtn = () => {
setPenColor("blue");
props?.setIsSignPad(false);
props?.setIsInitial && props?.setIsInitial(false);
props?.setIsImageSelect(false);
setIsDefaultSign(false);
props?.setImage();
handleTab();
props?.setIsStamp(false);
};
const savesigncheckbox = (
<label className="cursor-pointer flex items-center mb-0 text-center text-[11px] md:text-base">
<input
className="mr-2 md:mr-3 op-checkbox op-checkbox-xs md:op-checkbox-sm"
type="checkbox"
checked={isSavedSign}
onChange={(e) => setIsSavedSign(e.target.checked)}
/>
Save {props?.currWidgetsDetails?.type || props?.widgetType}
</label>
);
return (
<div>
{props?.isSignPad && (
<div className="op-modal op-modal-open">
<div className="op-modal-box px-[13px] pt-2 pb-0">
{isLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
)}
{isSignTypes ? (
<>
<div className="flex justify-between text-base-content items-center">
<div className="text-[1.2rem]">
<div className="flex flex-row justify-between mt-[3px]">
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
{props?.isStamp ? (
<span className="text-base-content font-bold text-lg">
{props?.widgetType === "image" ||
props?.currWidgetsDetails?.type === "image"
? t("upload-image")
: t("upload-stamp-image")}
</span>
) : (
<>
{!props?.isInitial &&
props?.defaultSign &&
isTabEnabled("default") ? (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
props?.setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-signature")}
</span>
</div>
) : (
props?.isInitial &&
props?.myInitial &&
isTabEnabled("default") && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
props?.setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-initials")}
</span>
</div>
)
)}
{isTabEnabled("draw") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(false);
setIsTab("draw");
props?.setImage();
if (isSignImg) {
props?.setSignature(isSignImg);
}
}}
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("draw")}
</span>
</div>
)}
{isTabEnabled("upload") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("upload-image")}
</span>
</div>
)}
{isTabEnabled("typed") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("type")}
</span>
</div>
)}
</>
)}
</div>
</div>
</div>
<div
className="text-[1.5rem] cursor-pointer"
onClick={handleCancelBtn}
>
&times;
</div>
</div>
<div className="p-[20px] h-full">
{isDefaultSign ? (
<>
{!props?.isInitial &&
props?.defaultSign &&
isTabEnabled("default") && (
<>
<div className="flex justify-center">
<div
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
>
<img
alt="stamp img"
className="w-full h-full object-contain bg-white"
draggable="false"
src={
props?.isInitial
? props?.myInitial
: props?.defaultSign
}
/>
</div>
</div>
{props.setIsAutoSign && autoSignAll()}
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)}
{props?.isInitial &&
props?.myInitial &&
isTabEnabled("default") && (
<>
<div className="flex justify-center">
<div
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
>
<img
alt="stamp img"
className="w-full h-full object-contain bg-white"
draggable="false"
src={
props?.isInitial
? props?.myInitial
: props?.defaultSign
}
/>
</div>
</div>
{props.setIsAutoSign && autoSignAll()}
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)}
</>
) : props?.isImageSelect || props?.isStamp ? (
!props?.image ? (
<div className="flex justify-center">
<div
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] flex flex-col justify-center items-center mb-[6px] cursor-pointer`}
onClick={() => props?.imageRef.current.click()}
>
<input
type="file"
onChange={props?.onImageChange}
className="filetype"
accept="image/png,image/jpeg"
ref={props?.imageRef}
hidden
/>
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
<div className="text-[10px]">{t("upload")}</div>
</div>
</div>
) : (
<>
<div className="flex justify-center">
<div
className={`${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} bg-white border-[1.3px] border-[#007bff] mb-[6px] overflow-hidden`}
>
<img
alt="print img"
ref={props?.imageRef}
src={props?.image.src}
draggable="false"
className="object-contain h-full w-full"
/>
</div>
</div>
{props.setIsAutoSign && autoSignAll()}
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div className="flex justify-between items-center">
<span className="mr-[5px] text-[12px]">
{props?.isInitial
? t("initial-teb")
: t("signature-tab")}
:
</span>
<input
maxLength={props?.isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
placeholder={
props?.isInitial
? t("initial-type")
: t("signature-type")
}
value={typedSignature}
onChange={(e) => {
setTypedSignature(e.target.value);
convertToImg(fontSelect, e.target.value);
}}
/>
</div>
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
{fontOptions.map((font, ind) => {
return (
<div
key={ind}
style={{
cursor: "pointer",
fontFamily: font.value,
backgroundColor:
fontSelect === font.value &&
"rgb(206 225 247)"
}}
onClick={() => {
setFontSelect(font.value);
convertToImg(font.value, typedSignature);
}}
>
<div
className="py-[5px] px-[10px] text-[20px]"
style={{ color: penColor }}
>
{typedSignature
? typedSignature
: "Your signature"}
</div>
</div>
);
})}
</div>
<div className="flex flex-col justify-between mt-[10px]">
{props.setIsAutoSign && autoSignAll()}
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
</div>
) : (
<>
<div className="flex justify-center">
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
className: `${props?.isInitial ? "intialSignatureCanvas" : "signatureCanvas"} border-[1.3px] border-[#007bff]`
}}
onEnd={() =>
handleSignatureChange(
canvasRef.current?.toDataURL()
)
}
dotSize={1}
/>
</div>
<div className="flex flex-col justify-between mt-[10px]">
{props.setIsAutoSign && autoSignAll()}
{accesstoken &&
props?.saveSignCheckbox?.isVisible &&
savesigncheckbox}
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent />
<SaveBtn />
</div>
</div>
</>
)}
</div>
</>
) : (
<div>
<div className="relative flex flex-row items-center justify-between">
<div className="text-base-content font-bold text-lg">
Signature
</div>
<div
className="text-[1.5rem] cursor-pointer"
onClick={handleCancelBtn}
>
&times;
</div>
</div>
<div className="mx-3 mb-6 mt-3">
<p>{t("at-least-one-signature-type")}</p>
</div>
</div>
)}
</div>
</div>
)}
</div>
);
}
export default SignPad;
@@ -25,17 +25,28 @@ const WidgetNameModal = (props) => {
const statusArr = ["Required", "Optional"];
const [signatureType, setSignatureType] = useState([]);
const handleHint = () => {
const type = props.defaultdata?.type;
if (type === "signature") {
return "Draw signature";
} else if (type === "stamp" || type === "image") {
return `Upload ${type}`;
} else if (type === "initials") {
return "Draw initial";
} else if (type === textInputWidget) {
return "Enter text";
} else {
return `Enter ${type}`;
}
};
useEffect(() => {
if (props.defaultdata) {
setFormdata({
name: props.defaultdata?.options?.name || "",
defaultValue: props.defaultdata?.options?.defaultValue || "",
status: props.defaultdata?.options?.status || "required",
hint:
props.defaultdata?.options?.hint ||
(props.defaultdata?.type === textInputWidget
? "Enter text"
: `Enter ${props.defaultdata?.options?.name}`),
hint: props.defaultdata?.options?.hint || handleHint(),
textvalidate:
props.defaultdata?.options?.validation?.type === "regex"
? props.defaultdata?.options?.validation?.pattern
File diff suppressed because it is too large Load Diff
+99 -55
View File
@@ -22,6 +22,35 @@ export const textInputWidget = "text input";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
//function for create list of year for date widget
export const range = (start, end, step) => {
const range = [];
for (let i = start; i <= end; i += step) {
range.push(i);
}
return range;
};
//function for get year
export const getYear = (date) => {
const newYear = new Date(date).getFullYear();
return newYear;
};
export const years = range(1950, getYear(new Date()) + 16, 1);
export const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
export const fileasbytes = async (filepath) => {
const response = await fetch(filepath); // Adjust the path accordingly
const arrayBuffer = await response.arrayBuffer();
@@ -304,56 +333,73 @@ export const changeDateToMomentFormat = (format) => {
}
};
export const addWidgetOptions = (type, signer, widgetValue) => {
const defaultOpt = { name: type, status: "required" };
const status = { status: "required" };
switch (type) {
case "signature":
return defaultOpt;
return { ...status, name: "Signature" };
case "stamp":
return defaultOpt;
return { ...status, name: "Upload stamp" };
case "checkbox":
return {
...defaultOpt,
options: { isReadOnly: false, isHideLabel: false }
...status,
name: "Checkbox",
isReadOnly: false,
isHideLabel: false
};
case textInputWidget:
return { ...defaultOpt, isReadOnly: false };
return { ...status, name: "Text", isReadOnly: false };
case "initials":
return defaultOpt;
return { ...status, name: "Initials" };
case "name":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...status,
name: "Name",
defaultValue: widgetValue ? widgetValue : ""
};
case "company":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...status,
name: "Company",
defaultValue: widgetValue ? widgetValue : ""
};
case "job title":
return { ...defaultOpt, defaultValue: widgetValue ? widgetValue : "" };
return {
...status,
name: "Job title",
defaultValue: widgetValue ? widgetValue : ""
};
case "date": {
const dateFormat = signer?.DateFormat
? selectFormat(signer?.DateFormat)
: "MM/dd/yyyy";
return {
...defaultOpt,
...status,
name: "Date",
response: getDate(signer?.DateFormat),
validation: { format: dateFormat, type: "date-format" }
};
}
case "image":
return defaultOpt;
return { ...status, name: "Upload image" };
case "email":
return {
...defaultOpt,
...status,
name: "Email",
validation: { type: "email", pattern: "" },
defaultValue: widgetValue ? widgetValue : ""
};
case "dropdown":
return defaultOpt;
return { ...status, name: "Dropdown" };
case radioButtonWidget:
return {
...defaultOpt,
...status,
name: "Radio button",
values: [],
isReadOnly: false,
isHideLabel: false
};
case textWidget:
return defaultOpt;
return { ...status, name: "Text" };
default:
return {};
}
@@ -362,30 +408,30 @@ export const addWidgetOptions = (type, signer, widgetValue) => {
export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
switch (type) {
case "signature":
return { name: "signature" };
return { name: "Signature" };
case "stamp":
return { name: "stamp" };
return { name: "Upload stamp" };
case "checkbox":
return { name: "checkbox" };
return { name: "Checkbox" };
case textWidget:
return { name: "text" };
return { name: "Text" };
case "initials":
return { name: "initials" };
return { name: "Initials" };
case "name":
return {
name: "name",
name: "Name",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
case "company":
return {
name: "company",
name: "Company",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
case "job title":
return {
name: "job title",
name: "Job title",
defaultValue: getWidgetValue(type),
validation: { type: "text", pattern: "" }
};
@@ -394,16 +440,16 @@ export const addWidgetSelfsignOptions = (type, getWidgetValue, owner) => {
? selectFormat(owner?.DateFormat)
: "MM/dd/yyyy";
return {
name: "date",
name: "Date",
response: getDate(owner?.DateFormat),
validation: { format: dateFormat, type: "date-format" }
};
}
case "image":
return { name: "image" };
return { name: "Upload image" };
case "email":
return {
name: "email",
name: "Email",
defaultValue: getWidgetValue(type),
validation: { type: "email", pattern: "" }
};
@@ -815,8 +861,6 @@ export const onChangeInput = (
userId,
initial,
dateFormat,
isDefaultEmpty,
isRadio,
fontSize,
fontColor
) => {
@@ -853,22 +897,13 @@ export const onChangeInput = (
}
}
};
} else if (isDefaultEmpty) {
return {
...position,
options: {
...position.options,
response: value,
defaultValue: isRadio ? "" : []
}
};
} else {
return {
...position,
options: {
...position.options,
response: value,
defaultValue:""
defaultValue: ""
}
};
}
@@ -915,7 +950,8 @@ export const onChangeInput = (
...positionData,
options: {
...positionData.options,
response: value
response: value,
defaultValue: ""
}
};
}
@@ -1196,7 +1232,6 @@ export function onSaveSign(
return updatedArray;
} //condition when user edit signature/initial then updated signature apply all existing drawn signatures
else if (isApplyAll) {
// console.log("signatureImg",signatureImg)
const updatedArray = updateXYposition.map((page) => ({
...page,
pos: page.pos.map(
@@ -2040,26 +2075,12 @@ export const addDefaultSignatureImg = (xyPosition, defaultSignImg, type) => {
return xyDefaultPos;
};
//function for create list of year for date widget
export const range = (start, end, step) => {
const range = [];
for (let i = start; i <= end; i += step) {
range.push(i);
}
return range;
};
//function for get month
export const getMonth = (date) => {
const newMonth = new Date(date).getMonth();
return newMonth;
};
//function for get year
export const getYear = (date) => {
const newYear = new Date(date).getFullYear();
return newYear;
};
//function to create/copy widget next to already dropped widget
export const handleCopyNextToWidget = (
position,
@@ -3117,3 +3138,26 @@ export const updateDateWidgetsRes = (documentData, signerId, journey) => {
}
});
};
//function for show checked checkbox
export const selectCheckbox = (ind, selectedCheckbox) => {
if (selectedCheckbox && selectedCheckbox?.length > 0) {
const isCheck = selectedCheckbox?.some((data) => data === ind);
return isCheck || false;
}
};
export const checkRegularExpress = (validateType, setValidatePlaceholder) => {
switch (validateType) {
case "email":
setValidatePlaceholder("demo@gmail.com");
break;
case "number":
setValidatePlaceholder("12345");
break;
case "text":
setValidatePlaceholder("please enter text");
break;
default:
setValidatePlaceholder("please enter value");
}
};
+3
View File
@@ -17,6 +17,9 @@ body {
scrollbar-width: none;
/* Firefox */
}
.react-datepicker-popper {
z-index: 9999 !important;
}
@media screen and (max-width: 766px) {
.reactour__close {
+8 -10
View File
@@ -4,7 +4,6 @@ import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { CookiesProvider } from "react-cookie";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import {
@@ -19,7 +18,8 @@ import "./polyfills";
import { serverUrl_fn } from "./constant/appinfo";
import "./i18n";
const appId = import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign";
const appId =
import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign";
const serverUrl = serverUrl_fn();
Parse.initialize(appId);
Parse.serverURL = serverUrl;
@@ -56,12 +56,10 @@ const generatePreview = (props) => {
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<CookiesProvider defaultSetOptions={{ path: "/" }}>
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
</CookiesProvider>
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
);
+2 -2
View File
@@ -533,9 +533,9 @@ export default function reportJson(id) {
btnId: "1873",
btnLabel: "Share with team",
hoverLabel: "Share with team",
btnIcon: "fa-light fa-share-nodes",
btnIcon: "fa-light fa-user-group",
redirectUrl: "",
action: "sharewith"
action: "sharewithteam"
});
return newItem;
}
+7 -45
View File
@@ -3,13 +3,12 @@ import Header from "../components/Header";
import Footer from "../components/Footer";
import Sidebar from "../components/sidebar/Sidebar";
import { useWindowSize } from "../hook/useWindowSize";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate, useLocation, Outlet } from "react-router";
import { useCookies } from "react-cookie";
import Loader from "../primitives/Loader";
import { showHeader } from "../redux/reducers/showHeader";
import { useTranslation } from "react-i18next";
@@ -30,7 +29,6 @@ const HomeLayout = () => {
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [tourConfigs, setTourConfigs] = useState([]);
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
const tenantId = localStorage.getItem("TenantId");
@@ -64,38 +62,11 @@ const HomeLayout = () => {
setIsUserValid(false);
}
})();
saveCookies();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tenantId]);
//function to use save data in cookies storage
const saveCookies = () => {
const main_Domain = window.location.origin;
const domainName = window.location.hostname;
// Find the index of the first dot in the string
const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
const serverUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
secure: true,
domain: updateDomain
});
setCookie("main_Domain", main_Domain, {
secure: true,
domain: updateDomain
});
setCookie("server_url", serverUrl, {
secure: true,
domain: updateDomain
});
setCookie("parse_app_id", parseAppId, {
secure: true,
domain: updateDomain
});
};
const showSidebar = () => {
setIsOpen((value) => !value);
dispatch(showHeader(!isOpen));
@@ -120,12 +91,7 @@ const HomeLayout = () => {
// const resArr = arr;
const resArr = arr.map((obj, index) => {
if (arr.length - 1 === index) {
return {
...obj
// actions: () => {
// setIsCloseBtn(true);
// },
};
return { ...obj };
} else {
return {
...obj,
@@ -137,27 +103,23 @@ const HomeLayout = () => {
});
setTourConfigs([
{
selector: '[data-tut="reactourFirst"]',
selector: '[data-tut="nonpresentmask"]',
content: t("tour-mssg.home-layout-1"),
position: "top"
// style: { backgroundColor: "#abd4d2" },
position: "center",
},
{
selector: '[data-tut="tourbutton"]',
content: t("tour-mssg.home-layout-2"),
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
...resArr,
{
selector: '[data-tut="reactourLast"]',
selector: '[data-tut="nonpresentmask"]',
content: t("tour-mssg.home-layout-3", { appName }),
position: "top"
// style: { backgroundColor: "#abd4d2" },
position: "center",
}
]);
checkTourStatus();
// console.log("resArr ", resArr);
}
};
const closeTour = async () => {
+1 -1
View File
@@ -11,7 +11,7 @@ import Title from "../components/Title";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import axios from "axios";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
+65 -224
View File
@@ -5,19 +5,23 @@ import Parse from "parse";
import axios from "axios";
import { DndProvider, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import SignPad from "../components/pdf/SignPad";
import { useDispatch, useSelector } from "react-redux";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import Confetti from "react-confetti";
import moment from "moment";
import {
setSaveSignCheckbox,
setMyInitial,
setDefaultSignImg,
resetWidgetState
} from "../redux/reducers/widgetSlice.js";
import {
contractDocument,
multiSignEmbed,
embedDocId,
pdfNewWidthFun,
signPdfFun,
onSaveSign,
onSaveImage,
addDefaultSignatureImg,
radioButtonWidget,
replaceMailVaribles,
@@ -40,7 +44,6 @@ import {
defaultWidthHeight,
addWidgetOptions,
textWidget,
compressedFileSize,
mailTemplate,
updateDateWidgetsRes,
widgetDataValue
@@ -49,7 +52,6 @@ import Header from "../components/pdf/PdfHeader";
import RenderPdf from "../components/pdf/RenderPdf";
import Title from "../components/Title";
import DefaultSignature from "../components/pdf/DefaultSignature";
import { useSelector } from "react-redux";
import SignerListComponent from "../components/pdf/SignerListComponent";
import PdfZoom from "../components/pdf/PdfZoom";
import { useTranslation } from "react-i18next";
@@ -65,29 +67,28 @@ import AgreementSign from "../components/pdf/AgreementSign";
import WidgetComponent from "../components/pdf/WidgetComponent";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import TextFontSetting from "../components/pdf/TextFontSetting";
import WidgetsValueModal from "../components/pdf/WidgetsValueModal.jsx";
function PdfRequestFiles(
) {
const { t } = useTranslation();
const dispatch = useDispatch();
const isShowModal = useSelector((state) => state.widget.isShowModal);
const saveSignCheckbox = useSelector(
(state) => state.widget.saveSignCheckbox
);
const defaultSignImg = useSelector((state) => state.widget.defaultSignImg);
const myInitial = useSelector((state) => state.widget.myInitial);
const appName =
"OpenSign™";
const [pdfDetails, setPdfDetails] = useState([]);
const [signedSigners, setSignedSigners] = useState([]);
const [unsignedSigners, setUnSignedSigners] = useState([]);
const [isSignPad, setIsSignPad] = useState(false);
const [pdfUrl, setPdfUrl] = useState();
const [allPages, setAllPages] = useState(null);
const numPages = 1;
const [pageNumber, setPageNumber] = useState(1);
const [image, setImage] = useState(null);
const [isImageSelect, setIsImageSelect] = useState(false);
const [signature, setSignature] = useState();
const [isStamp, setIsStamp] = useState(false);
const [signKey, setSignKey] = useState();
const [imgWH, setImgWH] = useState({});
const imageRef = useRef(null);
const [handleError, setHandleError] = useState();
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isCelebration, setIsCelebration] = useState(false);
const [requestSignTour, setRequestSignTour] = useState(true);
const [tourStatus, setTourStatus] = useState([]);
@@ -95,7 +96,6 @@ function PdfRequestFiles(
isLoad: true,
message: t("loading-mssg")
});
const [defaultSignImg, setDefaultSignImg] = useState();
const [isDocId, setIsDocId] = useState(false);
const [pdfNewWidth, setPdfNewWidth] = useState();
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
@@ -111,6 +111,9 @@ function PdfRequestFiles(
const [signerUserId, setSignerUserId] = useState();
const [isDontShow, setIsDontShow] = useState(false);
const [isDownloading, setIsDownloading] = useState("");
// tempSignerId is used to temporarily store the currently selected signer's unique ID, When editing a text widget, it automatically attaches a prefill user, and since prefill users are not shown in the signer list, the selected signer from before editing would be lost. To handle this, we store the currently selected signer's unique ID in tempSignerId before entering the text widget edit mode. Once the text widget settings are completed,
// we restore the original selected signer by setting tempSignerId back to uniqueId.This ensures that the correct signer remains selected and visible in the UI even after interacting with a prefill-only widget like the text widget.
const [tempSignerId, setTempSignerId] = useState("");
const [defaultSignAlert, setDefaultSignAlert] = useState({
isShow: false,
alertMessage: ""
@@ -119,14 +122,11 @@ function PdfRequestFiles(
isCertificate: false,
isModal: false
});
const [myInitial, setMyInitial] = useState("");
const [isInitial, setIsInitial] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
const [isSigned, setIsSigned] = useState(false);
const [isExpired, setIsExpired] = useState(false);
const [alreadySign, setAlreadySign] = useState(false);
const [containerWH, setContainerWH] = useState({});
const [validateAlert, setValidateAlert] = useState(false);
const [widgetsTour, setWidgetsTour] = useState(false);
const [minRequiredCount, setminRequiredCount] = useState();
const [sendInOrder, setSendInOrder] = useState(false);
@@ -148,20 +148,14 @@ function PdfRequestFiles(
const [isredirectCanceled, setIsredirectCanceled] = useState(true);
const [isDragging, setIsDragging] = useState(false);
const [dragKey, setDragKey] = useState();
const [isAutoSign, setIsAutoSign] = useState(false);
const [signBtnPosition, setSignBtnPosition] = useState([]);
const [xySignature, setXYSignature] = useState({});
const [zIndex, setZIndex] = useState(1);
const [fontSize, setFontSize] = useState();
const [fontColor, setFontColor] = useState();
const [widgetType, setWidgetType] = useState("");
const [isTextSetting, setIsTextSetting] = useState(false);
const [isPageCopy, setIsPageCopy] = useState(false);
const [assignedWidgetId, setAssignedWidgetId] = useState([]);
const [saveSignCheckbox, setSaveSignCheckbox] = useState({
isVisible: false,
signId: ""
});
const [showSignPagenumber, setShowSignPagenumber] = useState([]);
const [owner, setOwner] = useState({});
const [, drop] = useDrop({
@@ -195,6 +189,7 @@ function PdfRequestFiles(
}
let getDocumentId = getDocId || documentId;
useEffect(() => {
dispatch(resetWidgetState([]));
if (getDocumentId) {
setDocumentId(getDocumentId);
getDocumentDetails(getDocumentId);
@@ -480,21 +475,28 @@ function PdfRequestFiles(
} else {
setRequestSignTour(false);
}
setSaveSignCheckbox((prev) => ({ ...prev, isVisible: true }));
dispatch(
setSaveSignCheckbox({
...saveSignCheckbox,
isVisible: true
})
);
//function to get default signatur of current user from `contracts_Signature` class
const defaultSignRes = await getDefaultSignature(
jsonSender?.objectId
);
if (defaultSignRes?.status === "success") {
setSaveSignCheckbox((prev) => ({
...prev,
isVisible: true,
signId: defaultSignRes?.res?.id
}));
dispatch(
setSaveSignCheckbox({
...saveSignCheckbox,
isVisible: true,
signId: defaultSignRes?.res?.id
})
);
const sign = defaultSignRes?.res?.defaultSignature || "";
const initials = defaultSignRes?.res?.defaultInitial || "";
setDefaultSignImg(sign);
setMyInitial(initials);
dispatch(setDefaultSignImg(sign));
dispatch(setMyInitial(initials));
}
} else if (res?.length === 0) {
const res = await contactBook(currUserId);
@@ -1010,7 +1012,6 @@ function PdfRequestFiles(
}
}
}
setIsSignPad(false);
} else {
setIsAlert({
isShow: true,
@@ -1031,6 +1032,7 @@ function PdfRequestFiles(
const handleSignPdf = async () => {
await embedWidgetsData();
};
//function for save x and y position and show signature tab on that position
const handleTabDrag = (key) => {
setDragKey(key);
@@ -1113,7 +1115,7 @@ function PdfRequestFiles(
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const updateSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (position.key === currWidgetsDetails?.key) {
return {
...position,
options: {
@@ -1144,7 +1146,6 @@ function PdfRequestFiles(
return obj;
});
setSignerPos(newUpdateSigner);
setFontSize();
setFontColor();
handleTextSettingModal(false);
@@ -1183,127 +1184,6 @@ function PdfRequestFiles(
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
}
//function for image upload or update
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
const file = event.target.files[0];
compressedFileSize(file, setImgWH, setImage);
}
};
//function for upload stamp image
const saveImage = () => {
const widgetsType = currWidgetsDetails?.type;
//get current signers placeholder position data
const currentSigner = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
//get current pagenumber placeholder index
const getIndex = currentSigner[0].placeHolder.findIndex((object) => {
return object.pageNumber === pageNumber;
});
//get current signer placeholder position data
const placeholderPosition = currentSigner[0].placeHolder;
//`isApplyAll` is used when user edit stamp then updated signature apply all existing drawn signatures
const isApplyAll = true;
//function of save image and get updated position with image url
const getUpdatePosition = onSaveImage(
placeholderPosition,
getIndex,
signKey,
imgWH,
image,
isAutoSign,
widgetsType,
isApplyAll
);
//replace updated placeholder position with old data
placeholderPosition.splice(
0,
placeholderPosition.length,
...getUpdatePosition
);
//get current signers placeholder position data index number in array
const indexofSigner = signerPos.findIndex((object) => {
return object.signerObjId === signerObjectId;
});
//update current signers data with new placeholder position array data
setSignerPos((prevState) => {
const newState = [...prevState]; // Create a copy of the state
newState.splice(indexofSigner, 1, ...currentSigner); // Modify the copy
return newState; // Update the state with the modified copy
});
setIsAutoSign(false);
};
//function for save button to save signature or image url
const saveSign = (type, isDefaultSign, width, height, typedSignature) => {
const widgetsType = currWidgetsDetails?.type;
const isTypeText = width && height ? true : false;
const signatureImg = isDefaultSign
? isDefaultSign === "initials"
? myInitial
: defaultSignImg
: signature;
let imgWH = { width: width ? width : "", height: height ? height : "" };
setIsSignPad(false);
setIsImageSelect(false);
setImage();
//get current signers placeholder position data
const currentSigner = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
//get current pagenumber placeholder index
const getIndex = currentSigner[0].placeHolder.findIndex((object) => {
return object.pageNumber === pageNumber;
});
//set default signature image width and height
if (isDefaultSign) {
const img = new Image();
img.src = defaultSignImg;
if (img.complete) {
imgWH = { width: img.width, height: img.height };
}
}
//get current signer placeholder position data
const placeholderPosition = currentSigner[0].placeHolder;
//`isApplyAll` is used when user edit signature/initial then updated signature apply all existing drawn signatures
const isApplyAll = true;
//function of save signature image and get updated position with signature image url
const getUpdatePosition = onSaveSign(
type,
placeholderPosition,
getIndex,
signKey,
signatureImg,
imgWH,
isDefaultSign,
isTypeText,
typedSignature,
isAutoSign,
widgetsType,
isApplyAll
);
const updateSignerData = currentSigner.map((obj) => {
if (obj.signerObjId === signerObjectId) {
return { ...obj, placeHolder: getUpdatePosition };
}
return obj;
});
const index = signerPos.findIndex(
(data) => data.signerObjId === signerObjectId
);
setSignerPos((prevState) => {
const newState = [...prevState];
newState.splice(index, 1, ...updateSignerData);
return newState;
});
setIsAutoSign(false);
};
//function for set decline true on press decline button
const declineDoc = async (reason) => {
const senderUser = localStorage.getItem(
@@ -1675,7 +1555,8 @@ function PdfRequestFiles(
pageNumber,
containerWH
);
let dropData = [];
let dropData = [],
dropObj;
let placeHolder;
const dragTypeValue = item?.text ? item.text : monitor.type;
const widgetWidth =
@@ -1689,7 +1570,7 @@ function PdfRequestFiles(
if (item === "onclick") {
const divHeight = divRef.current.getBoundingClientRect().height;
// `getBoundingClientRect()` is used to get accurate measurement height of the div
const dropObj = {
dropObj = {
//onclick put placeholder center on pdf
xPosition: widgetWidth / 4 + containerWH.width / 2,
yPosition: widgetHeight + divHeight / 2,
@@ -1717,7 +1598,7 @@ function PdfRequestFiles(
const y = offset.y - containerRect.top;
const getXPosition = signBtnPosition[0] ? x - signBtnPosition[0].xPos : x;
const getYPosition = signBtnPosition[0] ? y - signBtnPosition[0].yPos : y;
const dropObj = {
dropObj = {
xPosition: getXPosition / (containerScale * scale),
yPosition: getYPosition / (containerScale * scale),
isStamp:
@@ -1733,7 +1614,6 @@ function PdfRequestFiles(
dropData.push(dropObj);
placeHolder = { pageNumber: pageNumber, pos: dropData };
}
setSelectWidgetId(key);
if (uniqueId) {
let filterSignerPos, currentPagePosition;
filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
@@ -1782,9 +1662,7 @@ function PdfRequestFiles(
setFontSize(12);
setFontColor("black");
}
setWidgetType(dragTypeValue);
setSignKey(key);
setCurrWidgetsDetails({});
setCurrWidgetsDetails(dropObj);
}
};
@@ -1875,6 +1753,7 @@ function PdfRequestFiles(
setShowSignPagenumber(sortedPagenumber);
}
};
return (
<DndProvider backend={HTML5Backend}>
<Title
@@ -2057,10 +1936,12 @@ function PdfRequestFiles(
setXyPosition={setSignerPos}
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
signKey={currWidgetsDetails?.key}
Id={uniqueId}
widgetType={widgetType}
widgetType={currWidgetsDetails?.type}
setUniqueId={setUniqueId}
tempSignerId={tempSignerId}
setTempSignerId={setTempSignerId}
/>
<div className=" w-full md:w-[95%] ">
{/* this modal is used show this document is already sign */}
@@ -2197,37 +2078,6 @@ function PdfRequestFiles(
)}
</div>
</ModalUi>
{/* this component is used for signature pad modal */}
{currentSigner && isSignPad && (
<SignPad
saveSignCheckbox={saveSignCheckbox}
setSaveSignCheckbox={setSaveSignCheckbox}
signatureTypes={signatureType}
isSignPad={isSignPad}
isStamp={isStamp}
setIsImageSelect={setIsImageSelect}
setIsSignPad={setIsSignPad}
setImage={setImage}
isImageSelect={isImageSelect}
imageRef={imageRef}
onImageChange={onImageChange}
setSignature={setSignature}
image={image}
onSaveImage={saveImage}
onSaveSign={saveSign}
defaultSign={defaultSignImg}
myInitial={myInitial}
setDefaultSignImg={setDefaultSignImg}
setMyInitial={setMyInitial}
isInitial={isInitial}
setIsInitial={setIsInitial}
setIsStamp={setIsStamp}
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
setIsAutoSign={setIsAutoSign}
isAutoSign={isAutoSign}
/>
)}
{/* pdf header which contain funish back button */}
<Header
isPdfRequestFiles={
@@ -2269,9 +2119,6 @@ function PdfRequestFiles(
pageNumber={pageNumber}
pdfOriginalWH={pdfOriginalWH}
pdfNewWidth={pdfNewWidth}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
setSignKey={setSignKey}
pdfDetails={pdfDetails}
signerPos={signerPos}
successEmail={false}
@@ -2285,11 +2132,7 @@ function PdfRequestFiles(
pdfLoad={pdfLoad}
setSignerPos={setSignerPos}
containerWH={containerWH}
setIsInitial={setIsInitial}
setValidateAlert={setValidateAlert}
unSignedWidgetId={unSignedWidgetId}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setCurrWidgetsDetails={setCurrWidgetsDetails}
divRef={divRef}
setIsResize={setIsResize}
@@ -2307,9 +2150,10 @@ function PdfRequestFiles(
setUniqueId={setUniqueId}
handleDeleteSign={handleDeleteSign}
handleTextSettingModal={handleTextSettingModal}
setWidgetType={setWidgetType}
assignedWidgetId={assignedWidgetId}
setRequestSignTour={setRequestSignTour}
currWidgetsDetails={currWidgetsDetails}
setTempSignerId={setTempSignerId}
/>
)}
</div>
@@ -2372,8 +2216,6 @@ function PdfRequestFiles(
!alreadySign &&
currentSigner && (
<DefaultSignature
defaultSignImg={defaultSignImg}
myInitial={myInitial}
userObjectId={signerObjectId}
setIsLoading={setIsLoading}
xyPosition={signerPos}
@@ -2407,23 +2249,22 @@ function PdfRequestFiles(
</div>
</div>
)}
<ModalUi
isOpen={validateAlert}
title={t("validation-alert")}
handleClose={() => setValidateAlert(false)}
>
<div className="h-[100%] p-[20px]">
<p>{t("validation-alert-1")}</p>
<div className="h-[1px] bg-[#9f9f9f] w-full my-[15px]"></div>
<button
onClick={() => setValidateAlert(false)}
type="button"
className="op-btn op-btn-ghost"
>
{t("close")}
</button>
</div>
</ModalUi>
{currentSigner && isShowModal[currWidgetsDetails?.key] && (
<WidgetsValueModal
key={currWidgetsDetails?.key}
xyPosition={signerPos}
pageNumber={pageNumber}
setXyPosition={setSignerPos}
uniqueId={uniqueId}
setPageNumber={setPageNumber}
finishDocument={handleSignPdf}
setCurrWidgetsDetails={setCurrWidgetsDetails}
currWidgetsDetails={currWidgetsDetails}
index={pageNumber}
setUniqueId={setUniqueId}
tempSignerId={tempSignerId}
/>
)}
<DownloadPdfZip
setIsDownloadModal={setIsDownloadModal}
isDownloadModal={isDownloadModal}
+46 -33
View File
@@ -11,11 +11,11 @@ import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrop } from "react-dnd";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import WidgetComponent from "../components/pdf/WidgetComponent";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import { useLocation, useParams } from "react-router";
import SignerListPlace from "../components/pdf/SignerListPlace";
import Header from "../components/pdf/PdfHeader";
import { RWebShare } from "react-web-share";
import ShareButton from "../primitives/ShareButton";
import {
replaceMailVaribles,
pdfNewWidthFun,
@@ -68,10 +68,12 @@ import LottieWithLoader from "../primitives/DotLottieReact";
import Alert from "../primitives/Alert";
import AsyncSelect from "react-select/async";
import AddContact from "../primitives/AddContact";
import WidgetsValueModal from "../components/pdf/WidgetsValueModal.jsx";
function PlaceHolderSign() {
const { t } = useTranslation();
const copyUrlRef = useRef(null);
const isShowModal = useSelector((state) => state.widget.isShowModal);
const appName =
"OpenSign™";
const editorRef = useRef();
@@ -121,7 +123,8 @@ function PlaceHolderSign() {
const [selectedEmail, setSelectedEmail] = useState(false);
const [isResize, setIsResize] = useState(false);
const [zIndex, setZIndex] = useState(1);
const [signKey, setSignKey] = useState();
// tempSignerId is used to temporarily store the currently selected signer's unique ID, When editing a text widget, it automatically attaches a prefill user, and since prefill users are not shown in the signer list, the selected signer from before editing would be lost. To handle this, we store the currently selected signer's unique ID in tempSignerId before entering the text widget edit mode. Once the text widget settings are completed,
// we restore the original selected signer by setting tempSignerId back to uniqueId.This ensures that the correct signer remains selected and visible in the UI even after interacting with a prefill-only widget like the text widget.
const [tempSignerId, setTempSignerId] = useState("");
const [blockColor, setBlockColor] = useState("");
const [isTextSetting, setIsTextSetting] = useState(false);
@@ -134,14 +137,11 @@ function PlaceHolderSign() {
const [isDontShow, setIsDontShow] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const [showDropdown, setShowDropdown] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [isUiLoading, setIsUiLoading] = useState(false);
const [isRadio, setIsRadio] = useState(false);
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isCheckbox, setIsCheckbox] = useState(false);
const [isNameModal, setIsNameModal] = useState(false);
const [widgetName, setWidgetName] = useState(false);
const [mailStatus, setMailStatus] = useState("");
const [isCurrUser, setIsCurrUser] = useState(false);
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
@@ -513,7 +513,8 @@ function PlaceHolderSign() {
pageNumber,
containerWH
);
let dropData = [];
let dropData = [],
dropObj;
let placeHolder;
const dragTypeValue = item?.text ? item.text : monitor.type;
const widgetWidth =
@@ -524,7 +525,7 @@ function PlaceHolderSign() {
if (item === "onclick") {
const divHeight = divRef.current.getBoundingClientRect().height;
// `getBoundingClientRect()` is used to get accurate measurement height of the div
const dropObj = {
dropObj = {
//onclick put placeholder center on pdf
xPosition: widgetWidth / 4 + containerWH.width / 2,
yPosition: widgetHeight + divHeight / 2,
@@ -556,7 +557,7 @@ function PlaceHolderSign() {
const getYPosition = signBtnPosition[0]
? y - signBtnPosition[0].yPos
: y;
const dropObj = {
dropObj = {
xPosition: getXPosition / (containerScale * scale),
yPosition: getYPosition / (containerScale * scale),
isStamp:
@@ -572,7 +573,6 @@ function PlaceHolderSign() {
dropData.push(dropObj);
placeHolder = { pageNumber: pageNumber, pos: dropData };
}
setSelectWidgetId(key);
if (signer) {
let filterSignerPos, currentPagePosition;
if (dragTypeValue === textWidget) {
@@ -664,10 +664,7 @@ function PlaceHolderSign() {
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
}
setWidgetType(dragTypeValue);
setSignKey(key);
setCurrWidgetsDetails({});
setWidgetName(dragTypeValue);
setCurrWidgetsDetails(dropObj);
}
}
};
@@ -966,7 +963,7 @@ function PlaceHolderSign() {
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
setSignerExistModal(true);
setSelectWidgetId(IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key);
setCurrWidgetsDetails(IsSignerNotExist[0]?.placeHolder?.[0]?.pos);
} else {
saveDocumentDetails();
}
@@ -1144,9 +1141,13 @@ function PlaceHolderSign() {
<i className="fa-light fa-copy" />
<span className=" hidden md:block ml-1 ">{t("copy-link")}</span>
</button>
<RWebShare data={{ url: data.url, title: t("sign-url") }}>
<ShareButton
title={t("sign-url")}
text={t("sign-url")}
url={data.url}
>
<i className="fa-light fa-share-from-square op-link op-link-secondary no-underline"></i>
</RWebShare>
</ShareButton>
</div>
</div>
);
@@ -1428,21 +1429,21 @@ function PlaceHolderSign() {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (widgetType === radioButtonWidget) {
if (position.key === currWidgetsDetails?.key) {
if (currWidgetsDetails?.type === radioButtonWidget) {
if (addOption) {
return {
...position,
Height: position.Height
? position.Height + 15
: defaultWidthHeight(widgetType).height + 15
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
};
} else if (deleteOption) {
return {
...position,
Height: position.Height
? position.Height - 15
: defaultWidthHeight(widgetType).height - 15
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
};
} else {
return {
@@ -1463,20 +1464,20 @@ function PlaceHolderSign() {
}
};
}
} else if (widgetType === "checkbox") {
} else if (currWidgetsDetails?.type === "checkbox") {
if (addOption) {
return {
...position,
Height: position.Height
? position.Height + 15
: defaultWidthHeight(widgetType).height + 15
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
};
} else if (deleteOption) {
return {
...position,
Height: position.Height
? position.Height - 15
: defaultWidthHeight(widgetType).height - 15
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
};
} else {
return {
@@ -1567,7 +1568,7 @@ function PlaceHolderSign() {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (position.key === currWidgetsDetails?.key) {
if (position.type === textInputWidget) {
return {
...position,
@@ -2367,9 +2368,9 @@ function PlaceHolderSign() {
setXyPosition={setSignerPos}
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
signKey={currWidgetsDetails?.key}
Id={uniqueId}
widgetType={widgetType}
widgetType={currWidgetsDetails?.type}
setUniqueId={setUniqueId}
tempSignerId={tempSignerId}
setTempSignerId={setTempSignerId}
@@ -2474,17 +2475,13 @@ function PlaceHolderSign() {
setZIndex={setZIndex}
setIsPageCopy={setIsPageCopy}
signersdata={signersdata}
setSignKey={setSignKey}
handleLinkUser={handleLinkUser}
setUniqueId={setUniqueId}
isDragging={isDragging}
setShowDropdown={setShowDropdown}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={setCurrWidgetsDetails}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={setIsNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
@@ -2499,6 +2496,7 @@ function PlaceHolderSign() {
setFontColor={setFontColor}
unSignedWidgetId={unSignedWidgetId}
divRef={divRef}
currWidgetsDetails={currWidgetsDetails}
/>
)}
</div>
@@ -2536,7 +2534,6 @@ function PlaceHolderSign() {
handleDeleteUser={handleDeleteUser}
uniqueId={uniqueId}
setSignerPos={setSignerPos}
setSelectWidgetId={setSelectWidgetId}
/>
</div>
) : (
@@ -2582,6 +2579,22 @@ function PlaceHolderSign() {
</div>
</div>
)}
{isShowModal[currWidgetsDetails?.key] && (
<WidgetsValueModal
key={currWidgetsDetails?.key}
xyPosition={signerPos}
pageNumber={pageNumber}
setXyPosition={setSignerPos}
uniqueId={uniqueId}
setPageNumber={setPageNumber}
setCurrWidgetsDetails={setCurrWidgetsDetails}
currWidgetsDetails={currWidgetsDetails}
index={pageNumber}
isSave={true}
tempSignerId={tempSignerId}
setUniqueId={setUniqueId}
/>
)}
<ModalUi
isOpen={isAlreadyPlace.status}
title={t("document-alert")}
@@ -2610,7 +2623,7 @@ function PlaceHolderSign() {
)}
<WidgetNameModal
signatureType={signatureType}
widgetName={widgetName}
widgetName={currWidgetsDetails?.options?.name}
defaultdata={currWidgetsDetails}
isOpen={isNameModal}
handleClose={handleNameModal}
+9
View File
@@ -296,6 +296,15 @@ const Preferences = () => {
const updateRes = JSON.parse(JSON.stringify(updateTenant));
setRequestBody(updateRes?.RequestBody);
setRequestSubject(updateRes?.RequestSubject);
let extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser && extUser?.objectId) {
extUser.TenantId.RequestBody = updateRes?.RequestBody;
extUser.TenantId.RequestBody = updateRes?.RequestSubject;
const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
}
setIsAlert({ type: "success", msg: t("saved-successfully") });
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
}
+66 -183
View File
@@ -8,7 +8,6 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrop } from "react-dnd";
import SignPad from "../components/pdf/SignPad";
import EmailComponent from "../components/pdf/EmailComponent";
import WidgetComponent from "../components/pdf/WidgetComponent";
import {
@@ -18,8 +17,6 @@ import {
multiSignEmbed,
calculateInitialWidthHeight,
defaultWidthHeight,
onSaveImage,
onSaveSign,
contractUsers,
contactBook,
randomId,
@@ -40,18 +37,17 @@ import {
convertBase64ToFile,
generatePdfName,
handleRemoveWidgets,
compressedFileSize,
addWidgetSelfsignOptions
} from "../constant/Utils";
import { useParams } from "react-router";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import Signedby from "../components/pdf/Signedby";
import Header from "../components/pdf/PdfHeader";
import RenderPdf from "../components/pdf/RenderPdf";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import Title from "../components/Title";
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import TextFontSetting from "../components/pdf/TextFontSetting";
import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom";
@@ -63,43 +59,44 @@ import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import HandleError from "../primitives/HandleError";
import LoaderWithMsg from "../primitives/LoaderWithMsg";
import {
setSaveSignCheckbox,
setMyInitial,
setDefaultSignImg,
setIsShowModal,
resetWidgetState
} from "../redux/reducers/widgetSlice.js";
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
//For signYourself inProgress section signer can add sign and complete doc sign.
function SignYourSelf() {
const { t } = useTranslation();
const { docId } = useParams();
const dispatch = useDispatch();
const isShowModal = useSelector((state) => state.widget.isShowModal);
const saveSignCheckbox = useSelector(
(state) => state.widget.saveSignCheckbox
);
const appName =
"OpenSign™";
const divRef = useRef(null);
const nodeRef = useRef(null);
const imageRef = useRef(null);
const pdfRef = useRef();
const numPages = 1;
const [pdfDetails, setPdfDetails] = useState([]);
const [isSignPad, setIsSignPad] = useState(false);
const [allPages, setAllPages] = useState(null);
const [pdfUrl, setPdfUrl] = useState();
const [xyPosition, setXyPosition] = useState([]);
const [defaultSignImg, setDefaultSignImg] = useState();
const [pageNumber, setPageNumber] = useState(1);
const [image, setImage] = useState(null);
const [isImageSelect, setIsImageSelect] = useState(false);
const [signature, setSignature] = useState();
const [isStamp, setIsStamp] = useState(false);
const [isEmail, setIsEmail] = useState(false);
const [signBtnPosition, setSignBtnPosition] = useState([]);
const [xySignature, setXYSignature] = useState({});
const [dragKey, setDragKey] = useState();
const [fontSize, setFontSize] = useState();
const [fontColor, setFontColor] = useState();
const [signKey, setSignKey] = useState();
const [imgWH, setImgWH] = useState({});
const [pdfNewWidth, setPdfNewWidth] = useState();
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
const [successEmail, setSuccessEmail] = useState(false);
const [myInitial, setMyInitial] = useState("");
const [isInitial, setIsInitial] = useState(false);
const [isUiLoading, setIsUiLoading] = useState(false);
const [validateAlert, setValidateAlert] = useState(false);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: t("loading-mssg")
@@ -113,7 +110,6 @@ function SignYourSelf() {
const [contractName, setContractName] = useState("");
const [containerWH, setContainerWH] = useState({});
const [isPageCopy, setIsPageCopy] = useState(false);
const [selectWidgetId, setSelectWidgetId] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
status: false
@@ -121,7 +117,6 @@ function SignYourSelf() {
const [isTextSetting, setIsTextSetting] = useState(false);
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
const [isCheckbox, setIsCheckbox] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [pdfLoad, setPdfLoad] = useState(false);
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isDontShow, setIsDontShow] = useState(false);
@@ -142,10 +137,7 @@ function SignYourSelf() {
const [isDownloadModal, setIsDownloadModal] = useState(false);
const [isResize, setIsResize] = useState(false);
const [isUploadPdf, setIsUploadPdf] = useState(false);
const [saveSignCheckbox, setSaveSignCheckbox] = useState({
isVisible: false,
signId: ""
});
const [owner, setOwner] = useState({});
const [, drop] = useDrop({
accept: "BOX",
@@ -173,6 +165,7 @@ function SignYourSelf() {
const jsonSender = JSON.parse(senderUser);
useEffect(() => {
dispatch(resetWidgetState([]));
if (documentId) {
getDocumentDetails(true);
}
@@ -243,7 +236,6 @@ function SignYourSelf() {
});
} else {
setIsUiLoading(false);
setIsSignPad(false);
setIsEmail(true);
setXyPosition([]);
setSignBtnPosition([]);
@@ -266,13 +258,15 @@ function SignYourSelf() {
//function to get default signatur eof current user from `contracts_Signature` class
const defaultSignRes = await getDefaultSignature(jsonSender.objectId);
if (defaultSignRes?.status === "success") {
setSaveSignCheckbox((prev) => ({
...prev,
isVisible: true,
signId: defaultSignRes?.res?.id
}));
setDefaultSignImg(defaultSignRes?.res?.defaultSignature);
setMyInitial(defaultSignRes?.res?.defaultInitial);
dispatch(
setSaveSignCheckbox({
...saveSignCheckbox,
isVisible: true,
signId: defaultSignRes?.res?.id
})
);
dispatch(setDefaultSignImg(defaultSignRes?.res?.defaultSignature));
dispatch(setMyInitial(defaultSignRes?.res?.defaultInitial));
}
const contractUsersRes = await contractUsers();
if (contractUsersRes === "Error: Something went wrong!") {
@@ -281,7 +275,8 @@ function SignYourSelf() {
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
setContractName("_Users");
setSignerUserId(contractUsersRes[0].objectId);
setSaveSignCheckbox((prev) => ({ ...prev, isVisible: true }));
dispatch(setSaveSignCheckbox({ ...saveSignCheckbox, isVisible: true }));
const tourstatuss =
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
@@ -293,12 +288,9 @@ function SignYourSelf() {
setCheckTourStatus(checkTourRecipients[0].signyourself);
}
} else {
setCheckTourStatus(true);
setCheckTourStatus(false);
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
setIsLoading({ isLoad: false });
} else if (contractUsersRes.length === 0) {
const contractContactBook = await contactBook(jsonSender.objectId);
if (contractContactBook && contractContactBook.length > 0) {
@@ -436,20 +428,7 @@ function SignYourSelf() {
const xyPos = { pageNumber: pageNumber, pos: dropData };
setXyPosition((prev) => [...prev, xyPos]);
}
if (
dragTypeValue === "signature" ||
dragTypeValue === "stamp" ||
dragTypeValue === "image" ||
dragTypeValue === "initials"
) {
setIsSignPad(true);
}
if (dragTypeValue === "stamp" || dragTypeValue === "image") {
setIsStamp(true);
} else if (dragTypeValue === "initials") {
setIsInitial(true);
} else if (dragTypeValue === "checkbox") {
if (dragTypeValue === "checkbox") {
setIsCheckbox(true);
} else if (
[
@@ -464,9 +443,10 @@ function SignYourSelf() {
setFontSize(12);
setFontColor("black");
}
setWidgetType(dragTypeValue);
setSelectWidgetId(key);
setSignKey(key);
dispatch(setIsShowModal({ [key]: true }));
// if (dragTypeValue !== "checkbox") {
setCurrWidgetsDetails(dropObj);
// }
};
//`handleResend` function is used to resend otp for email verification
@@ -655,7 +635,6 @@ function SignYourSelf() {
isSignYourSelfFlow,
scale
);
// console.log("pdf", pdfBytes);
//function for call to embed signature in pdf and get digital signature pdf
if (!pdfBytes?.error) {
await signPdfFun(pdfBytes, documentId);
@@ -826,53 +805,7 @@ function SignYourSelf() {
setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset);
}
//function for image upload or update
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
const file = event.target.files[0];
compressedFileSize(file, setImgWH, setImage);
}
};
//function for upload stamp or image
const saveImage = () => {
const getImage = onSaveImage(xyPosition, index, signKey, imgWH, image);
setXyPosition(getImage);
};
//function for save button to save signature or image url
const saveSign = (type, isDefaultSign, width, height, typedSignature) => {
const isTypeText = width && height ? true : false;
const signatureImg = isDefaultSign
? isDefaultSign === "initials"
? myInitial
: defaultSignImg
: signature;
let imgWH = { width: width ? width : "", height: height ? height : "" };
setIsSignPad(false);
setIsImageSelect(false);
setImage();
if (isDefaultSign) {
const img = new Image();
img.src = defaultSignImg;
if (img.complete) {
imgWH = { width: img.width, height: img.height };
}
}
const getUpdatePosition = onSaveSign(
type,
xyPosition,
index,
signKey,
signatureImg,
imgWH,
isDefaultSign,
isTypeText,
typedSignature
);
setXyPosition(getUpdatePosition);
};
//function for capture position on hover or touch widgets button
const handleDivClick = (e) => {
const isTouchEvent = e.type.startsWith("touch");
@@ -932,7 +865,7 @@ function SignYourSelf() {
selector: '[data-tut="addWidgets"]',
content: () => (
<TourContentWithBtn
message={t("tour-mssg.signyour-self-2")}
message={t("tour-mssg.signyour-self-1")}
isChecked={handleDontShow}
/>
),
@@ -970,14 +903,10 @@ function SignYourSelf() {
} else {
updatedTourStatus = [{ signyourself: true }];
}
await axios
.put(
`${localStorage.getItem(
"baseUrl"
)}classes/contracts${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
try {
await axios.put(
`${localStorage.getItem("baseUrl")}classes/contracts${contractName}/${signerUserId}`,
{ TourStatus: updatedTourStatus },
{
headers: {
"Content-Type": "application/json",
@@ -985,14 +914,11 @@ function SignYourSelf() {
sessionToken: localStorage.getItem("accesstoken")
}
}
)
.then(() => {
// const json = Listdata.data;
})
.catch((err) => {
console.log("axois err ", err);
alert(t("something-went-wrong-mssg"));
});
);
} catch (err) {
console.log("axois err ", err);
alert(t("something-went-wrong-mssg"));
}
}
};
@@ -1015,20 +941,20 @@ function SignYourSelf() {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (position.key === currWidgetsDetails?.key) {
if (addOption) {
return {
...position,
Height: position.Height
? position.Height + 15
: defaultWidthHeight(widgetType).height + 15
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
};
} else if (deleteOption) {
return {
...position,
Height: position.Height
? position.Height - 15
: defaultWidthHeight(widgetType).height - 15
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
};
} else {
return {
@@ -1078,7 +1004,7 @@ function SignYourSelf() {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (position.key === currWidgetsDetails?.key) {
return {
...position,
options: {
@@ -1195,7 +1121,7 @@ function SignYourSelf() {
/>
)}
{/* this component used for UI interaction and show their functionality */}
{pdfLoad && !checkTourStatus && (
{pdfLoad && !checkTourStatus && !isCompleted && (
<Tour
onRequestClose={closeTour}
steps={tourConfig}
@@ -1292,39 +1218,9 @@ function SignYourSelf() {
setXyPosition={setXyPosition}
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
widgetType={widgetType}
signKey={currWidgetsDetails?.key}
widgetType={currWidgetsDetails?.type}
/>
{/* this is modal of signature pad */}
{isSignPad && (
<SignPad
saveSignCheckbox={saveSignCheckbox}
setSaveSignCheckbox={setSaveSignCheckbox}
signatureTypes={signatureTypes}
isSignPad={isSignPad}
isStamp={isStamp}
setIsImageSelect={setIsImageSelect}
setIsSignPad={setIsSignPad}
setImage={setImage}
isImageSelect={isImageSelect}
imageRef={imageRef}
onImageChange={onImageChange}
setSignature={setSignature}
image={image}
onSaveImage={saveImage}
onSaveSign={saveSign}
defaultSign={defaultSignImg}
myInitial={myInitial}
setDefaultSignImg={setDefaultSignImg}
setMyInitial={setMyInitial}
isInitial={isInitial}
setIsInitial={setIsInitial}
setIsStamp={setIsStamp}
widgetType={widgetType}
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
)}
{/*render email component to send email after finish signature on document */}
<EmailComponent
isEmail={isEmail}
@@ -1372,10 +1268,7 @@ function SignYourSelf() {
handleTabDrag={handleTabDrag}
handleStop={handleStop}
isDragging={isDragging}
setIsSignPad={setIsSignPad}
setIsStamp={setIsStamp}
handleDeleteSign={handleDeleteSign}
setSignKey={setSignKey}
pdfDetails={pdfDetails}
setIsDragging={setIsDragging}
xyPosition={xyPosition}
@@ -1389,13 +1282,8 @@ function SignYourSelf() {
index={index}
containerWH={containerWH}
setIsPageCopy={setIsPageCopy}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={setCurrWidgetsDetails}
setValidateAlert={setValidateAlert}
handleTextSettingModal={handleTextSettingModal}
setScale={setScale}
scale={scale}
@@ -1407,6 +1295,7 @@ function SignYourSelf() {
isResize={isResize}
setIsResize={setIsResize}
divRef={divRef}
currWidgetsDetails={currWidgetsDetails}
/>
)}
</div>
@@ -1424,7 +1313,6 @@ function SignYourSelf() {
isSignYourself={true}
addPositionOfSignature={addPositionOfSignature}
isMailSend={false}
// setSelectWidgetId={setSelectWidgetId}
/>
</div>
) : (
@@ -1437,24 +1325,19 @@ function SignYourSelf() {
</div>
</div>
)}
<ModalUi
isOpen={validateAlert}
title={t("validation-alert")}
handleClose={() => setValidateAlert(false)}
>
<div className="p-[20px] h-full">
<p>{t("validate-alert-mssg")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => setValidateAlert(false)}
type="button"
className="op-btn op-btn-ghost shadow-md"
>
{t("close")}
</button>
</div>
</ModalUi>
{!isCheckbox && isShowModal[currWidgetsDetails?.key] && (
<WidgetsValueModal
key={currWidgetsDetails?.key}
xyPosition={xyPosition} //placeholder details
pageNumber={pageNumber} //current page number
setXyPosition={setXyPosition} //placeholder details state
setPageNumber={setPageNumber}
setCurrWidgetsDetails={setCurrWidgetsDetails}
currWidgetsDetails={currWidgetsDetails}
index={index}
isSave={true}
/>
)}
<RotateAlert
showRotateAlert={showRotateAlert.status}
setShowRotateAlert={setShowRotateAlert}
+19 -43
View File
@@ -7,7 +7,7 @@ import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrop } from "react-dnd";
import WidgetComponent from "../components/pdf/WidgetComponent";
import Tour from "reactour";
import Tour from "../primitives/Tour";
import SignerListPlace from "../components/pdf/SignerListPlace";
import Header from "../components/pdf/PdfHeader";
import WidgetNameModal from "../components/pdf/WidgetNameModal";
@@ -100,10 +100,8 @@ const TemplatePlaceholder = () => {
const [isSigners, setIsSigners] = useState(false);
const [zIndex, setZIndex] = useState(1);
const [showDropdown, setShowDropdown] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [isRadio, setIsRadio] = useState(false);
const [blockColor, setBlockColor] = useState("");
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isNameModal, setIsNameModal] = useState(false);
const [isTextSetting, setIsTextSetting] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
@@ -122,13 +120,11 @@ const TemplatePlaceholder = () => {
const [isCreateDoc, setIsCreateDoc] = useState(false);
const [isEditTemplate, setIsEditTemplate] = useState(false);
const [isPageCopy, setIsPageCopy] = useState(false);
const [signKey, setSignKey] = useState();
const [IsReceipent, setIsReceipent] = useState(true);
const [isDontShow, setIsDontShow] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
const [isCheckbox, setIsCheckbox] = useState(false);
const [widgetName, setWidgetName] = useState(false);
const [isAddRole, setIsAddRole] = useState(false);
const [fontSize, setFontSize] = useState();
const [fontColor, setFontColor] = useState();
@@ -137,7 +133,6 @@ const TemplatePlaceholder = () => {
const [signatureType, setSignatureType] = useState([]);
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const [updatedPdfUrl, setUpdatedPdfUrl] = useState("");
const [tempSignerId, setTempSignerId] = useState("");
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
const [owner, setOwner] = useState({});
useEffect(() => {
@@ -353,9 +348,6 @@ const TemplatePlaceholder = () => {
//function for setting position after drop signature button over pdf
const addPositionOfSignature = (item, monitor) => {
if (item && item.text) {
setWidgetName(item.text);
}
getSignerPos(item, monitor);
};
@@ -378,13 +370,14 @@ const TemplatePlaceholder = () => {
const widgetHeight =
defaultWidthHeight(dragTypeValue).height * containerScale;
let dropData = [],
dropObj,
currentPagePosition,
filterSignerPos;
let placeHolder;
if (item === "onclick") {
// `getBoundingClientRect()` is used to get accurate measurement height of the div
const divHeight = divRef.current.getBoundingClientRect().height;
const dropObj = {
dropObj = {
//onclick put placeholder center on pdf
xPosition: widgetWidth / 4 + containerWH.width / 2,
yPosition: widgetHeight + divHeight / 2,
@@ -414,7 +407,7 @@ const TemplatePlaceholder = () => {
const getYPosition = signBtnPosition[0]
? y - signBtnPosition[0].yPos
: y;
const dropObj = {
dropObj = {
xPosition: getXPosition / (containerScale * scale),
yPosition: getYPosition / (containerScale * scale),
isStamp:
@@ -521,10 +514,7 @@ const TemplatePlaceholder = () => {
setFontSize(12);
setFontColor("black");
}
setCurrWidgetsDetails({});
setWidgetType(dragTypeValue);
setSignKey(key);
setSelectWidgetId(key);
setCurrWidgetsDetails(dropObj);
} else {
setIsReceipent(false);
}
@@ -988,14 +978,14 @@ const TemplatePlaceholder = () => {
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourFirst"]',
selector: '[data-tut="nonpresentmask"]',
content: () => (
<TourContentWithBtn
message={t("tour-mssg.template-placeholder-2")}
isChecked={handleDontShow}
/>
),
position: "top",
position: "center",
style: { fontSize: "13px" },
action: () => handleCloseRoleModal()
},
@@ -1318,21 +1308,21 @@ const TemplatePlaceholder = () => {
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (widgetType === radioButtonWidget) {
if (position.key === currWidgetsDetails?.key) {
if (currWidgetsDetails?.type === radioButtonWidget) {
if (addOption) {
return {
...position,
Height: position.Height
? position.Height + 15
: defaultWidthHeight(widgetType).height + 15
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
};
} else if (deleteOption) {
return {
...position,
Height: position.Height
? position.Height - 15
: defaultWidthHeight(widgetType).height - 15
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
};
} else {
return {
@@ -1354,20 +1344,20 @@ const TemplatePlaceholder = () => {
}
};
}
} else if (widgetType === "checkbox") {
} else if (currWidgetsDetails?.type === "checkbox") {
if (addOption) {
return {
...position,
Height: position.Height
? position.Height + 15
: defaultWidthHeight(widgetType).height + 15
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
};
} else if (deleteOption) {
return {
...position,
Height: position.Height
? position.Height - 15
: defaultWidthHeight(widgetType).height - 15
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
};
} else {
return {
@@ -1459,7 +1449,7 @@ const TemplatePlaceholder = () => {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
if (position.key === currWidgetsDetails?.key) {
if (position.type === textInputWidget) {
return {
...position,
@@ -1538,13 +1528,6 @@ const TemplatePlaceholder = () => {
setShowDropdown(false);
setIsRadio(false);
setIsCheckbox(false);
//condition for text widget type after set all values for text widget
//change setUniqueId which is set in tempsignerId
//because textwidget do not have signer user so for selected signers we have to do
if (currWidgetsDetails.type === textWidget) {
setUniqueId(tempSignerId);
setTempSignerId("");
}
};
const clickOnZoomIn = () => {
@@ -1791,12 +1774,10 @@ const TemplatePlaceholder = () => {
setXyPosition={setSignerPos}
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
signKey={currWidgetsDetails?.key}
Id={uniqueId}
widgetType={widgetType}
widgetType={currWidgetsDetails?.type}
setUniqueId={setUniqueId}
tempSignerId={tempSignerId}
setTempSignerId={setTempSignerId}
/>
{/* pdf header which contain funish back button */}
<Header
@@ -1853,14 +1834,10 @@ const TemplatePlaceholder = () => {
setUniqueId={setUniqueId}
signersdata={signersdata}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
isDragging={isDragging}
setShowDropdown={setShowDropdown}
setCurrWidgetsDetails={setCurrWidgetsDetails}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setIsCheckbox={setIsCheckbox}
handleNameModal={setIsNameModal}
pdfOriginalWH={pdfOriginalWH}
@@ -1874,9 +1851,9 @@ const TemplatePlaceholder = () => {
setFontColor={setFontColor}
isResize={isResize}
divRef={divRef}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
unSignedWidgetId={unSignedWidgetId}
currWidgetsDetails={currWidgetsDetails}
/>
)}
</div>
@@ -1913,7 +1890,6 @@ const TemplatePlaceholder = () => {
setBlockColor={setBlockColor}
setSignerPos={setSignerPos}
uniqueId={uniqueId}
setSelectWidgetId={setSelectWidgetId}
isTemplateFlow={true}
/>
</div>
@@ -1991,7 +1967,7 @@ const TemplatePlaceholder = () => {
)}
<WidgetNameModal
signatureType={signatureType}
widgetName={widgetName}
widgetName={currWidgetsDetails?.options?.name}
defaultdata={currWidgetsDetails}
isOpen={isNameModal}
handleClose={handleNameModal}
@@ -10,8 +10,8 @@ import {
} from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
import { RWebShare } from "react-web-share";
import Tour from "reactour";
import ShareButton from "./ShareButton";
import Tour from "../primitives/Tour";
import Parse from "parse";
import {
copytoData,
@@ -398,7 +398,7 @@ const ReportTable = (props) => {
setIsResendMail({ [item.objectId]: true });
} else if (act.action === "bulksend") {
handleBulkSend(item);
} else if (act.action === "sharewith") {
} else if (act.action === "sharewithteam") {
if (item?.SharedWith && item?.SharedWith.length > 0) {
// below code is used to get existing sharewith teams and formated them as per react-select
const formatedList = item?.SharedWith.map((x) => ({
@@ -1411,7 +1411,7 @@ const ReportTable = (props) => {
onRequestClose={closeTour}
steps={props.tourData}
isOpen={isTour}
// rounded={5}
rounded={5}
closeWithMask={false}
/>
</>
@@ -2183,17 +2183,15 @@ const ReportTable = (props) => {
{share.email}
</span>
<div className="flex items-center gap-2">
<RWebShare
data={{
url: share.url,
title: "Sign url"
}}
<ShareButton
title={t("sign-url")}
text={t("sign-url")}
url={share.url}
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm "
>
<button className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm ">
<i className="fa-light fa-share-from-square"></i>{" "}
{t("btnLabel.Share")}
</button>
</RWebShare>
<i className="fa-light fa-share-from-square"></i>
{t("btnLabel.Share")}
</ShareButton>
<button
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
onClick={() => copytoclipboard(share)}
@@ -0,0 +1,178 @@
import { useCallback, useState } from "react";
import ModalUi from "./ModalUi";
function useShare({ title, text, url }) {
const [error, setError] = useState(null);
const isSupported = typeof navigator !== "undefined" && !!navigator.share;
const share = useCallback(async () => {
if (!isSupported) {
setError(new Error("Web Share API not supported"));
return;
}
try {
await navigator.share({ title, text, url });
} catch (err) {
// User may have cancelled, or another error occurred
setError(err);
}
}, [isSupported, title, text, url]);
return { share, isSupported, error };
}
/**
* A customizable ShareButton component.
* If `children` are provided, they are used as the trigger element;
* otherwise, a default share button is rendered.
*
* @param {object} props
* @param {string} props.title - Title for sharing
* @param {string} props.text - Text for sharing
* @param {string} props.url - URL to share
* @param {string} [props.className] - Optional styling class
* @param {React.ReactNode} [props.children] - Custom trigger element
*/
export default function ShareButton({ title, text, url, className, children }) {
const { share, isSupported, error } = useShare({ title, text, url });
const [isPopupOpen, setPopupOpen] = useState(false);
// Native Web Share API supported
if (isSupported) {
return (
<button
onClick={share}
className={className}
aria-label="Share this page"
>
{children || "🔗 Share"}
</button>
);
}
// Fallback: trigger opens popup
const subject = encodeURIComponent(title || text);
const body = encodeURIComponent(`${text}\n\n${url}`);
return (
<>
{/* React Fragment */}
<button
onClick={() => setPopupOpen(true)}
className={className}
aria-label="Open share options"
>
{children || "🔗 Share"}
</button>
{isPopupOpen && (
<ModalUi
isOpen
title={
<>
<i class="fa-solid fa-share-from-square"></i> Share
</>
}
handleClose={() => setPopupOpen(false)}
>
{error && <p style={{ color: "red" }}>Error: {error.message}</p>}
<div className="grid grid-cols-1 md:grid-cols-2 justify-items-start text-lg p-[20px]">
{/* Copy Link */}
<button
onClick={() => navigator.clipboard.writeText(url)}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-solid fa-clipboard fa-lg"></i> Copy to clipboard
</button>
{/* Twitter */}
<button
onClick={() =>
window.open(
`https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(url)}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-brands fa-square-x-twitter fa-lg"></i> Share on
Twitter
</button>
{/* Facebook */}
<button
onClick={() =>
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-brands fa-square-facebook fa-lg"></i> Share on
Facebook
</button>
{/* WhatsApp */}
<button
onClick={() =>
window.open(
`https://wa.me/?text=${encodeURIComponent(text + " " + url)}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-brands fa-square-whatsapp fa-lg"></i> Share on
WhatsApp
</button>
{/* Gmail (Web) */}
<button
onClick={() =>
window.open(
`https://mail.google.com/mail/?view=cm&fs=1&su=${subject}&body=${body}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-solid fa-envelope fa-lg"></i> Share via Gmail
</button>
{/* Microsoft Teams */}
<button
onClick={() =>
window.open(
`https://teams.microsoft.com/l/share?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-brands fa-microsoft fa-lg"></i> Share on Teams
</button>
{/* Outlook Web */}
<button
onClick={() =>
window.open(
`https://outlook.live.com/owa/?path=/mail/action/compose&subject=${subject}&body=${body}`,
"_blank",
"noopener"
)
}
className="m-2 op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm w-[190px]"
>
<i className="fa-solid fa-envelope-open-text fa-lg"></i> Share via
Outlook
</button>
</div>
</ModalUi>
)}
</>
);
}
+33
View File
@@ -0,0 +1,33 @@
import ReactTour from "reactour";
export default function Tour({
steps,
isOpen,
rounded,
className,
showNumber,
closeWithMask,
onRequestClose,
showNavigation,
showCloseButton,
showNavigationNumber,
disableKeyboardNavigation
}) {
const radius = rounded || 5;
return (
<ReactTour
className={className}
steps={steps}
isOpen={isOpen}
rounded={radius}
scrollOffset={-100}
closeWithMask={closeWithMask}
disableKeyboardNavigation={disableKeyboardNavigation}
showCloseButton={showCloseButton}
onRequestClose={onRequestClose}
showNumber={showNumber}
showNavigation={showNavigation}
showNavigationNumber={showNavigationNumber}
/>
);
}
@@ -0,0 +1,52 @@
// reducers/widgetSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
isShowModal: false,
saveSignCheckbox: {
isVisible: false,
signId: ""
},
signatureTypes: null,
defaultSignImg: null,
myInitial: null,
lastIndex: ""
};
const widgetSlice = createSlice({
name: "widget",
initialState,
reducers: {
setIsShowModal: (state, action) => {
state.isShowModal = action.payload;
},
setSaveSignCheckbox: (state, action) => {
state.saveSignCheckbox = action.payload;
},
setSignatureTypes: (state, action) => {
state.signatureTypes = action.payload;
},
setDefaultSignImg: (state, action) => {
state.defaultSignImg = action.payload;
},
setMyInitial: (state, action) => {
state.myInitial = action.payload;
},
setLastIndex: (state, action) => {
state.lastIndex = action.payload;
},
resetWidgetState: () => initialState
}
});
export const {
setIsShowModal,
setSaveSignCheckbox,
setSignatureTypes,
setMyInitial,
resetWidgetState,
setDefaultSignImg,
setLastIndex
} = widgetSlice.actions;
export default widgetSlice.reducer;
+4 -6
View File
@@ -1,18 +1,16 @@
// import { createStore, applyMiddleware } from "redux";
// import thunk from "redux-thunk";
// import reducers from "./reducers";
// export const store = createStore(reducers, applyMiddleware(thunk));
import { configureStore } from "@reduxjs/toolkit";
import infoReducer from "./reducers/infoReducer";
import ShowTenant from "./reducers/ShowTenant";
import TourStepsReducer from "./reducers/TourStepsReducer";
import showHeader from "./reducers/showHeader";
import widgetReducer from "./reducers/widgetSlice";
export const store = configureStore({
reducer: {
appInfo: infoReducer,
TourSteps: TourStepsReducer,
ShowTenant,
showHeader
showHeader,
widget: widgetReducer
}
});
+25 -3
View File
@@ -18,7 +18,6 @@
-moz-user-select: none;
user-select: none;
}
.radioButton {
width: 100%;
height: 100%;
@@ -31,14 +30,28 @@
}
.signatureCanvas {
width: 460px;
height: 184px;
width: 440px;
height: 167px;
}
.tabWidth{
width: 440px;
}
.intialSignatureCanvas {
width: 150px;
height: 150px;
}
.checked-radio::after {
content: "";
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
border-radius: 9999px;
background-color: #111111; /* blue-500 */
}
.intialSignature {
border: 2px solid #888;
@@ -388,6 +401,9 @@ option {
width: 300px;
height: 120px;
}
.tabWidth{
width: 300px;
}
.penContainerDefault {
width: 300px;
@@ -411,6 +427,9 @@ option {
width: 280px;
height: 112px;
}
.tabWidth{
width: 280px;
}
.penContainerDefault {
width: 280px;
@@ -432,6 +451,9 @@ option {
width: 230px;
height: 92px;
}
.tabWidth{
width: 230px;
}
.penContainerDefault {
width: 230px;
@@ -36,17 +36,24 @@ export default async function GetTemplate(request) {
let teamsArr = [];
_extUser?.TeamIds?.forEach(x => (teamsArr = [...teamsArr, ...x.Ancestors]));
// Create the first query
const sharedWithQuery = new Parse.Query('contracts_Template');
sharedWithQuery.containedIn('SharedWith', teamsArr);
const sharedWithTeamQuery = new Parse.Query('contracts_Template');
sharedWithTeamQuery.containedIn('SharedWith', teamsArr);
// Create the second query
const sharedWithJsersQuery = new Parse.Query('contracts_Template');
sharedWithJsersQuery.equalTo('SharedWithUsers', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
// Create the third query
const createdByQuery = new Parse.Query('contracts_Template');
createdByQuery.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
template = Parse.Query.or(sharedWithQuery, createdByQuery);
template = Parse.Query.or(sharedWithTeamQuery, sharedWithJsersQuery, createdByQuery);
template.equalTo('objectId', templateId);
template.notEqualTo('IsArchive', true);
template.include('ExtUserPtr');
@@ -47,6 +47,13 @@ export default async function getReport(request) {
objectId: extUser.id,
},
},
{
SharedWithUsers: {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
},
},
],
});
} else {
@@ -1,6 +1,15 @@
export default function reportJson(id, userId) {
const currentUserId = userId;
const commanKeys = [
'URL',
'Name',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'TemplateId',
];
switch (id) {
// draft documents report
case 'ByHuevtCFY':
@@ -14,19 +23,7 @@ export default function reportJson(id, userId) {
SignedUrl: { $exists: false },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'IsSignyourself',
'TemplateId',
],
keys: [...commanKeys, 'Note', 'Folder.Name', 'IsSignyourself'],
};
// Need your sign report
case '4Hhwbp482K':
@@ -48,20 +45,13 @@ export default function reportJson(id, userId) {
},
},
keys: [
'Name',
...commanKeys,
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Signers.UserId',
'AuditTrail',
'Placeholders',
'SignedUrl',
'TemplateId',
'ExpiryDate',
],
};
@@ -80,22 +70,15 @@ export default function reportJson(id, userId) {
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
'Name',
...commanKeys,
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'SendMail',
'Placeholders',
'SignedUrl',
'TemplateId',
'RequestBody',
'RequestSubject',
'ExtUserPtr.TenantId.RequestBody',
@@ -133,20 +116,13 @@ export default function reportJson(id, userId) {
],
},
keys: [
'Name',
...commanKeys,
'Note',
'Folder.Name',
'URL',
'SignedUrl',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'TimeToCompleteDays',
'Placeholders',
'IsSignyourself',
'IsCompleted',
'TemplateId',
],
};
// declined documents report
@@ -159,20 +135,7 @@ export default function reportJson(id, userId) {
IsDeclined: true,
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'DeclineReason',
'SignedUrl',
'TemplateId',
],
keys: [...commanKeys, 'Note', 'Folder.Name', 'DeclineReason', 'SignedUrl'],
};
// Expired Documents report
case 'zNqBHXHsYH':
@@ -187,20 +150,7 @@ export default function reportJson(id, userId) {
ExpiryDate: { $lt: { __type: 'Date', iso: new Date().toISOString() } },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'SignedUrl',
'TemplateId',
'ExpiryDate',
],
keys: [...commanKeys, 'Note', 'Folder.Name', 'SignedUrl', 'ExpiryDate'],
};
// Recently sent for signatures report show on dashboard
case 'd9k3UfYHBc':
@@ -217,20 +167,13 @@ export default function reportJson(id, userId) {
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
'Name',
...commanKeys,
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'Placeholders',
'SignedUrl',
'TemplateId',
'RequestBody',
'RequestSubject',
'ExtUserPtr.TenantId.RequestBody',
@@ -257,18 +200,11 @@ export default function reportJson(id, userId) {
},
},
keys: [
'Name',
'URL',
'ExtUserPtr.Name',
...commanKeys,
'ExtUserPtr.Email',
'Signers.Name',
'Signers.UserId',
'AuditTrail',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'SignedUrl',
'TemplateId',
'ExpiryDate',
],
};
@@ -284,18 +220,7 @@ export default function reportJson(id, userId) {
SignedUrl: { $exists: false },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'TemplateId',
],
keys: [...commanKeys, 'Note', 'Folder.Name'],
};
// contact book report
case 'contacts':
@@ -315,15 +240,9 @@ export default function reportJson(id, userId) {
reportClass: 'contracts_Template',
params: { Type: { $ne: 'Folder' }, IsArchive: { $ne: true } },
keys: [
'Name',
...commanKeys,
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'IsPublic',
'SharedWith.Name',
'SendinOrder',
+1515 -1492
View File
File diff suppressed because it is too large Load Diff
+8 -7
View File
@@ -1,6 +1,6 @@
{
"name": "open_sign_server",
"version": "1.4.0",
"version": "2.21.1",
"description": "An example Parse API server using the parse-server module",
"repository": {
"type": "git",
@@ -18,8 +18,8 @@
"watch": "nodemon index.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.802.0",
"@aws-sdk/s3-request-presigner": "^3.802.0",
"@aws-sdk/client-s3": "^3.812.0",
"@aws-sdk/s3-request-presigner": "^3.812.0",
"@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^4.1.0",
"@pdf-lib/fontkit": "^1.1.1",
@@ -35,12 +35,12 @@
"form-data": "^4.0.2",
"generate-api-key": "^1.0.2",
"googleapis": "^148.0.0",
"mailgun.js": "^11.1.0",
"mailgun.js": "^12.0.1",
"mongodb": "^6.16.0",
"multer": "^1.4.5-lts.2",
"multer": "^2.0.0",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^6.10.1",
"nodemailer": "^7.0.3",
"parse": "^6.1.1",
"parse-dbtool": "^1.2.0",
"parse-server": "^8.2.0",
@@ -48,13 +48,14 @@
"pdf-lib": "^1.17.1",
"posthog-node": "^4.17.1",
"qrcode": "^1.5.4",
"rate-limiter-flexible": "^7.1.1",
"speakeasy": "^2.0.0",
"ws": "^8.18.2"
},
"type": "module",
"devDependencies": {
"@babel/eslint-parser": "^7.27.1",
"eslint": "^9.25.1",
"eslint": "^9.27.0",
"jasmine": "^5.7.1",
"mongodb-runner": "^5.8.3",
"nodemon": "^3.1.10",