mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
Merge pull request #1644 from OpenSignLabs/updates-13949958694
Merge pull request #395 from OpenSignLabs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:18
|
||||
FROM node:22.14.0
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /usr/src/app
|
||||
@@ -16,8 +16,7 @@ COPY apps/OpenSign/.husky .
|
||||
|
||||
# Define environment variables if needed
|
||||
ENV NODE_ENV=production
|
||||
ENV REACT_APP_DEPLOYMENT=free_selfhost
|
||||
|
||||
ENV GENERATE_SOURCEMAP=false
|
||||
# build
|
||||
RUN npm run build
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Storage": "Speicher",
|
||||
"Signing certificate": "Signierzertifikat",
|
||||
"Teams": "Teams",
|
||||
"General": "Allgemein",
|
||||
"Teams-Children": {
|
||||
"Organizations": "Organisationen",
|
||||
"OrgAdmins": "OrgAdmins"
|
||||
@@ -149,7 +150,8 @@
|
||||
"Copy Public URL": "Öffentliche URL kopieren",
|
||||
"extend-expiry-date": "Ablaufdatum verlängern",
|
||||
"Duplicate Template": "Vorlage duplizieren",
|
||||
"Duplicate": "Duplikat"
|
||||
"Duplicate": "Duplikat",
|
||||
"daily-mail-quota": "Tägliches E-Mail-Kontingent"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nr.",
|
||||
@@ -169,7 +171,7 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Erstellungsdatum",
|
||||
"Type": "Typ",
|
||||
"Type": "Type",
|
||||
"Logs": "Protokolle",
|
||||
"Expiry-date": "Ablaufdatum"
|
||||
},
|
||||
@@ -604,6 +606,7 @@
|
||||
"placeholder-sign-4": "Ziehen Sie ein Feld in das Dokument oder klicken Sie darauf, um es hinzuzufügen.",
|
||||
"placeholder-sign-5": "Der PDF-Inhaltsbereich zeigt bereits die vorhandenen Platzhalter der Vorlage an. Diese Platzhalter entsprechen der Farbe des Empfängernamens, um sie leicht erkennbar zu machen.",
|
||||
"placeholder-sign-6": "Mit einem Klick auf 'Weiter' wird das Dokument gespeichert. Im nächsten Schritt können Sie die E-Mails, die an die Empfänger versendet werden sollen, anpassen oder die Signaturlinks kopieren und diese selbst mit den Empfängern teilen.",
|
||||
"report-1":"Klicken Sie auf die Schaltfläche „Hinzufügen“, um eine neue Vorlage zu erstellen. Vorlagen sind wiederverwendbare Dokumente, mit denen schnell neue Dokumente mit derselben Struktur und unterschiedlichen Unterzeichnern erstellt werden können. Eine HR-Vorlage für die Einarbeitung könnte beispielsweise vordefinierte Rollen wie „Personalleiter“ und „Neuer Mitarbeiter“ enthalten. Bei jeder Verwendung der Vorlage können Sie die Rolle „Neuer Mitarbeiter“ verschiedenen neuen Mitarbeitern zuweisen, während die Rolle „Personalleiter“ unverändert bleibt. So wird ein nahtloser Einarbeitungsprozess für jeden neuen Mitarbeiter ermöglicht.",
|
||||
"redirect": "Klicken Sie auf die Schaltfläche 'Verwenden', um ein neues Dokument aus einer bestehenden Vorlage zu erstellen.",
|
||||
"bulksend": "Um schnell mehrere Dokumente mithilfe einer vorhandenen Vorlage zu versenden, indem Sie einfach die E-Mail-Adressen der Empfänger erstellen, klicken Sie auf die Schaltfläche ‚Massenversand‘",
|
||||
"option": "Dieses Menü zeigt weitere Optionen wie Bearbeiten und Löschen. Verwenden Sie die Schaltfläche 'Bearbeiten', um Unterzeichnerrollen hinzuzufügen, Felder zu ändern und Ihre Vorlage zu aktualisieren. Änderungen gelten für alle zukünftigen Dokumente, die aus dieser Vorlage erstellt werden, wirken sich jedoch nicht auf vorhandene Dokumente aus. Verwenden Sie die Schaltfläche 'Löschen', um die Vorlage zu entfernen.",
|
||||
@@ -848,5 +851,10 @@
|
||||
"draft-template-info-p1": "Um Ihre Vorlage öffentlich zu machen, muss sie entweder eine einzelne Rolle enthalten oder, wenn sie mehrere Rollen umfasst, müssen alle zusätzlichen Rollen bereits den Unterzeichnern zugewiesen sein. Die nicht zugewiesene öffentliche Rolle muss leer bleiben und an erster Stelle stehen.",
|
||||
"visit-below-link": "Besuchen Sie den untenstehenden Link, um mehr zu erfahren -",
|
||||
"storage-help": "Durch die Aktivierung von BYOC können Sie Ihren eigenen S3-Speicher verbinden, sodass Ihre Dateien vollständig unter Ihrer Kontrolle bleiben und keine externen Kopien gespeichert werden. Wenn Ihnen Datenautonomie wichtig ist, erwägen Sie ein Upgrade auf Teams, um diese Funktion freizuschalten.",
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com."
|
||||
"daily-quota-reached": "Sie haben Ihr tägliches Kontingent erreicht. Für Unterstützung kontaktieren Sie bitte quotas@opensignlabs.com.",
|
||||
"enabled-signature-type": "Aktivierte Signaturtypen",
|
||||
"enabled-signature-type-help": "Die Einstellung 'Aktivierte Signaturtypen' bestimmt, welche Signaturoptionen in Ihrer Organisation verfügbar sind. Wenn Sie beispielsweise die Option 'Zeichnen' deaktivieren, wird sie den Mitgliedern Ihrer Organisation im Signatur-Widget nicht angezeigt, während die anderen drei Optionen weiterhin zugänglich bleiben.",
|
||||
"indexing-public-profile": "Erlaube die Indexierung des öffentlichen Profils durch Suchmaschinen",
|
||||
"user-created-successfully": "Benutzer erfolgreich erstellt.",
|
||||
"only-15-reminder-allowed": "Sie können bis zu 15 automatische Erinnerungen festlegen. Wenn zum Beispiel 'TimeToComplete' auf 15 Tage und 'RemindOnceInEvery' auf 1 Tag eingestellt ist, erreichen Sie das maximale Limit von 15 Erinnerungen. Passen Sie Ihre Einstellungen entsprechend an."
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Storage": "Storage",
|
||||
"Signing certificate": "Signing certificate",
|
||||
"Teams": "Teams",
|
||||
"General": "General",
|
||||
"Teams-Children": {
|
||||
"Organizations": "Organizations",
|
||||
"OrgAdmins": "OrgAdmins"
|
||||
@@ -149,7 +150,8 @@
|
||||
"Copy Public URL": "Copy public URL",
|
||||
"extend-expiry-date": "Extend expiry date",
|
||||
"Duplicate Template": "Duplicate template",
|
||||
"Duplicate": "Duplicate"
|
||||
"Duplicate": "Duplicate",
|
||||
"daily-mail-quota": "Daily Email Quota"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Sr.No",
|
||||
@@ -849,5 +851,10 @@
|
||||
"draft-template-info-p1": "To make your template public, it must either contain a single role, or, if it includes multiple roles, all additional roles must already be assigned to signers. The unassigned public role should remain empty and must be placed in the first position.",
|
||||
"visit-below-link": "Visit below link to know more -",
|
||||
"storage-help": "Enabling BYOC lets you connect your own S3 storage so your files remain entirely under your control—no external copies retained. If data autonomy matters to you, consider upgrading to Teams to unlock this feature.",
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com."
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com.",
|
||||
"enabled-signature-type": "Enabled Signature Types",
|
||||
"enabled-signature-type-help": "The 'Enabled Signature Types' setting determines which signature options are available across your organization. For example, if you disable the 'Draw' option, members of your organization will not see it in the signature widget, while the other three options will remain accessible.",
|
||||
"indexing-public-profile": "Allow indexing of public profile by search engines",
|
||||
"user-created-successfully": "user created successfully.",
|
||||
"only-15-reminder-allowed": "You can set up to 15 automatic reminders. For example, if 'TimeToComplete' is 15 days and 'RemindOnceInEvery' is 1 day, you'll reach the maximum limit of 15 reminders. Adjust your settings accordingly."
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Storage": "Almacenamiento",
|
||||
"Signing certificate": "Certificado de firma",
|
||||
"Teams": "Equipos",
|
||||
"General": "General",
|
||||
"Teams-Children": {
|
||||
"Organizations": "Organizaciones",
|
||||
"OrgAdmins": "OrgAdmins"
|
||||
@@ -149,7 +150,8 @@
|
||||
"Copy Public URL": "Copiar URL pública",
|
||||
"extend-expiry-date": "Date d'expiration",
|
||||
"Duplicate Template": "Plantilla duplicada",
|
||||
"Duplicate": "Duplicada"
|
||||
"Duplicate": "Duplicada",
|
||||
"daily-mail-quota": "Cuota diaria de correos electrónicos"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
@@ -765,8 +767,6 @@
|
||||
"term-cond-p3": "Recibirá y firmará documentos electrónicamente a través de {{appName}}.",
|
||||
"term-cond-p4": "Su firma electrónica es jurídicamente vinculante y equivalente a una firma manuscrita.",
|
||||
"term-cond-h2": "2. Consentimiento para el uso de registros y firmas electrónicas",
|
||||
"js-snippet-msg-2": "Administrar plantillas",
|
||||
"js-snippet-msg-3": "página.",
|
||||
"term-cond-p5": "Al aceptar esta Divulgación:",
|
||||
"term-cond-p6": "Usted acepta realizar transacciones electrónicas con el Remitente utilizando {{appName}} y comprende que este consentimiento es válido hasta que se retire.",
|
||||
"term-cond-p7": "Usted acepta revisar, firmar y devolver documentos electrónicamente utilizando {{appName}}.",
|
||||
@@ -802,6 +802,8 @@
|
||||
"term-cond-p30": " o correo electrónico",
|
||||
"js-snippet-msg": "Para integrar plantillas {{appName}} en sus sitios web HTML o páginas de destino, puede utilizar el siguiente código:",
|
||||
"js-snippet-msg-1": "Puede obtener el TemplateId en la página Administrar plantillas",
|
||||
"js-snippet-msg-2": "Administrar plantillas",
|
||||
"js-snippet-msg-3": "página.",
|
||||
"agrrement-alert": "Para continuar, debe consentir la divulgación de registros y firmas electrónicas.",
|
||||
"webhook-already-exists": "¡La URL ya existe! Pruebe con uno diferente.",
|
||||
"webhook-must-be-secure": "La URL del webhook debe ser segura y utilizar https://",
|
||||
@@ -848,7 +850,11 @@
|
||||
"agreement-note": "Nota: Aceptar esto no significa que esté firmando el documento de inmediato. Esto solo le permite revisar el documento electrónicamente. Tendrá la oportunidad de leerlo en su totalidad y decidir si desea firmarlo después.",
|
||||
"draft-template-info-p1": "Para hacer que tu plantilla sea pública, debe contener un único rol o, si incluye múltiples roles, todos los roles adicionales deben estar ya asignados a firmantes. El rol público no asignado debe permanecer vacío y debe estar en la primera posición.",
|
||||
"visit-below-link": "Visita el siguiente enlace para saber más -",
|
||||
"upgrade-to-team-plan": "Actualizar a team plan",
|
||||
"storage-help": "Habilitar BYOC te permite conectar tu propio almacenamiento S3 para que tus archivos permanezcan completamente bajo tu control, sin copias externas retenidas. Si la autonomía de los datos es importante para ti, considera actualizar a Teams para desbloquear esta función.",
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com."
|
||||
"daily-quota-reached": "Ha alcanzado su cuota diaria. Para obtener ayuda, comuníquese con quotas@opensignlabs.com.",
|
||||
"enabled-signature-type": "Tipos de firma habilitados",
|
||||
"enabled-signature-type-help": "La configuración de 'Tipos de firma habilitados' determina qué opciones de firma están disponibles en su organización. Por ejemplo, si desactiva la opción 'Dibujar', los miembros de su organización no la verán en el widget de firma, mientras que las otras tres opciones seguirán siendo accesibles.",
|
||||
"indexing-public-profile": "Permitir la indexación del perfil público por los motores de búsqueda",
|
||||
"user-created-successfully": "Usuario creado con éxito.",
|
||||
"only-15-reminder-allowed": "Puede configurar hasta 15 recordatorios automáticos. Por ejemplo, si 'TimeToComplete' es de 15 días y 'RemindOnceInEvery' es de 1 día, alcanzará el límite máximo de 15 recordatorios. Ajuste su configuración en consecuencia."
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Storage": "Stockage",
|
||||
"Signing certificate": "Certificat de signature",
|
||||
"Teams": "Équipes",
|
||||
"General": "Général",
|
||||
"Teams-Children": {
|
||||
"Organizations": "Organisations",
|
||||
"OrgAdmins": "OrgAdmins"
|
||||
@@ -170,7 +171,8 @@
|
||||
"Copy Public URL": "Copier l'URL publique",
|
||||
"extend-expiry-date": "Prolonger la date d'expiration",
|
||||
"Duplicate Template": "dupliquer le modèle",
|
||||
"Duplicate": "Double"
|
||||
"Duplicate": "Double",
|
||||
"daily-mail-quota": "Quota d'e-mails quotidien"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
@@ -450,6 +452,7 @@
|
||||
"add-recipients": "Ajouter des destinataires",
|
||||
"loading-mssg": "Cela pourrait prendre du temps",
|
||||
"send-mail": "Envoyer un mail",
|
||||
"signature-field-widget": "Au moins un champ de signature doit être ajouté pour chaque utilisateur. Vous n'avez pas ajouté de champs de signature pour {{signersName}}",
|
||||
"placeholder-alert-1": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
|
||||
"placeholder-alert-2": "Veuillez confirmer que vous avez rempli le champ de texte.",
|
||||
"placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ",
|
||||
@@ -557,7 +560,8 @@
|
||||
"do-not-access-contact-admin": "Vous n'y avez pas accès, veuillez contacter l'administrateur.",
|
||||
"filed-required-correctly": "Veuillez remplir correctement les informations requises.",
|
||||
"admin-created": "Administrateur créé",
|
||||
"invalid-masterkey": "Clé principale invalide", "master-key": "La clef maitresse",
|
||||
"invalid-masterkey": "Clé principale invalide",
|
||||
"master-key": "La clef maitresse",
|
||||
"profile-update-alert": "Mise à jour du profil réussie.",
|
||||
"date": "Date",
|
||||
"report-not-found": "Rapport introuvable",
|
||||
@@ -847,5 +851,10 @@
|
||||
"draft-template-info-p1": "Pour rendre votre modèle public, il doit contenir un seul rôle ou, s'il inclut plusieurs rôles, tous les rôles supplémentaires doivent déjà être attribués aux signataires. Le rôle public non attribué doit rester vide et être placé en première position.",
|
||||
"visit-below-link": "Visitez le lien ci-dessous pour en savoir plus -",
|
||||
"storage-help": "Activer BYOC vous permet de connecter votre propre stockage S3 afin que vos fichiers restent entièrement sous votre contrôle, sans copie externe conservée. Si l'autonomie des données est importante pour vous, envisagez de passer à l'offre Teams pour débloquer cette fonctionnalité.",
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com."
|
||||
"daily-quota-reached": "Vous avez atteint votre quota quotidien. Pour obtenir de l'aide, veuillez contacter quotas@opensignlabs.com.",
|
||||
"enabled-signature-type": "Types de signature activés",
|
||||
"enabled-signature-type-help": "Le paramètre 'Types de signature activés' détermine quelles options de signature sont disponibles dans votre organisation. Par exemple, si vous désactivez l'option 'Dessiner', les membres de votre organisation ne la verront pas dans le widget de signature, tandis que les trois autres options resteront accessibles.",
|
||||
"indexing-public-profile": "Autoriser l'indexation du profil public par les moteurs de recherche",
|
||||
"user-created-successfully": "Utilisateur créé avec succès.",
|
||||
"only-15-reminder-allowed": "Vous pouvez définir jusqu'à 15 rappels automatiques. Par exemple, si 'TimeToComplete' est de 15 jours et 'RemindOnceInEvery' est de 1 jour, vous atteindrez la limite maximale de 15 rappels. Ajustez vos paramètres en conséquence."
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Storage": "Archiviazione",
|
||||
"Signing certificate": "Certificato di firma",
|
||||
"Teams": "Squadre",
|
||||
"General": "Generale",
|
||||
"Teams-Children": {
|
||||
"Organizations": "Organizzazioni",
|
||||
"OrgAdmins": "OrgAdmins"
|
||||
@@ -149,7 +150,8 @@
|
||||
"Copy Public URL": "Copia URL pubblico",
|
||||
"extend-expiry-date": "Estendi data di scadenza",
|
||||
"Duplicate Template": "Duplica modello",
|
||||
"Duplicate": "Duplica"
|
||||
"Duplicate": "Duplica",
|
||||
"daily-mail-quota": "Quota e-mail giornaliera"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nr.",
|
||||
@@ -450,6 +452,7 @@
|
||||
"add-recipients": "Aggiungi destinatari",
|
||||
"loading-mssg": "Questo potrebbe richiedere del tempo",
|
||||
"send-mail": "Invia Mail",
|
||||
"signature-field-widget":"È necessario aggiungere almeno un campo firma per ogni utente. Non hai aggiunto campi firma per {{signersName}}",
|
||||
"placeholder-alert-1": "Assicurati che sia stato aggiunto almeno un widget per la firma per tutti i destinatari.",
|
||||
"placeholder-alert-2": "Conferma di aver compilato il campo di testo.",
|
||||
"placeholder-alert-3": "Sei sicuro di voler inviare questo documento per le firme?",
|
||||
@@ -848,5 +851,10 @@
|
||||
"draft-template-info-p1": "Per rendere il tuo modello pubblico, deve contenere un solo ruolo oppure, se include più ruoli, tutti i ruoli aggiuntivi devono essere già assegnati ai firmatari. Il ruolo pubblico non assegnato deve rimanere vuoto e deve essere posizionato per primo.",
|
||||
"visit-below-link": "Visita il link qui sotto per saperne di più -",
|
||||
"storage-help": "Abilitare BYOC ti consente di collegare il tuo archivio S3 in modo che i tuoi file rimangano completamente sotto il tuo controllo, senza copie esterne conservate. Se l'autonomia dei dati è importante per te, considera l'upgrade a Teams per sbloccare questa funzionalità.",
|
||||
"daily-quota-reached": "You’ve reached your daily quota. For assistance, please contact quotas@opensignlabs.com."
|
||||
"daily-quota-reached": "Hai raggiunto la tua quota giornaliera. Per assistenza, contatta quotas@opensignlabs.com.",
|
||||
"enabled-signature-type": "Tipi di firma abilitati",
|
||||
"enabled-signature-type-help": "L'impostazione 'Tipi di firma abilitati' determina quali opzioni di firma sono disponibili nella tua organizzazione. Ad esempio, se disabiliti l'opzione 'Disegna', i membri della tua organizzazione non la vedranno nel widget della firma, mentre le altre tre opzioni resteranno accessibili.",
|
||||
"indexing-public-profile": "Consenti l'indicizzazione del profilo pubblico dai motori di ricerca",
|
||||
"user-created-successfully": "Utente creato con successo.",
|
||||
"only-15-reminder-allowed": "Puoi impostare fino a 15 promemoria automatici. Ad esempio, se 'TimeToComplete' è di 15 giorni e 'RemindOnceInEvery' è di 1 giorno, raggiungerai il limite massimo di 15 promemoria. Regola le tue impostazioni di conseguenza."
|
||||
}
|
||||
|
||||
@@ -2,13 +2,8 @@ import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
import {
|
||||
copytoData,
|
||||
usertimezone
|
||||
} from "../constant/Utils";
|
||||
import {
|
||||
emailRegex,
|
||||
} from "../constant/const";
|
||||
import { copytoData, usertimezone } from "../constant/Utils";
|
||||
import { emailRegex } from "../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
@@ -46,9 +41,9 @@ const AddUser = (props) => {
|
||||
if (teamRes.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
||||
setTeamList(_teamRes);
|
||||
const allUserId =
|
||||
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
||||
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
||||
const allUserId =
|
||||
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
||||
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
||||
}
|
||||
};
|
||||
const checkUserExist = async () => {
|
||||
@@ -76,167 +71,167 @@ const AddUser = (props) => {
|
||||
setIsFormLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
|
||||
props.showAlert("danger", t("user-already-exist"));
|
||||
setIsFormLoader(false);
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
|
||||
} else {
|
||||
try {
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
const timezone = usertimezone;
|
||||
if (timezone) {
|
||||
extUser.set("Timezone", timezone);
|
||||
}
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
const timezone = usertimezone;
|
||||
if (timezone) {
|
||||
extUser.set("Timezone", timezone);
|
||||
}
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
props.showAlert("success", t("user-created-successfully"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
props.showAlert("success", t("user-created-successfully"));
|
||||
} else {
|
||||
setIsFormLoader(false);
|
||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("something-went-wrong-mssg")
|
||||
});
|
||||
} finally {
|
||||
setTimeout(
|
||||
() => props.setIsAlert({ type: "success", msg: "" }),
|
||||
1500
|
||||
);
|
||||
} else {
|
||||
props.showAlert("danger", t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,8 +254,7 @@ const AddUser = (props) => {
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
props.setIsAlert({ type: "success", msg: t("copied") });
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
|
||||
props.showAlert("success", t("copied"));
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
|
||||
@@ -270,126 +264,120 @@ const AddUser = (props) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("password")}
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
{t("password-generateed")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
placeholder={t("phone-optional")}
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Role")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
{t("Select")}
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("cancel")}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("password")}
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
{t("password-generateed")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
placeholder={t("phone-optional")}
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Role")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
{t("Select")}
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("cancel")}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
getFileName
|
||||
} from "../../constant/Utils";
|
||||
import React, { useState } from "react";
|
||||
import { getFileName } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import SignersInput from "../shared/fields/SignersInput";
|
||||
|
||||
const EditTemplate = ({
|
||||
template,
|
||||
onSuccess,
|
||||
}) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
const appName = "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const [formData, setFormData] = useState({
|
||||
Name: template?.Name || "",
|
||||
@@ -64,6 +56,13 @@ const EditTemplate = ({
|
||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||
const allowModify = formData?.AllowModifications || false;
|
||||
let reminderDate = {};
|
||||
const remindOnceInEvery = formData?.RemindOnceInEvery;
|
||||
const TimeToCompleteDays = parseInt(formData?.TimeToCompleteDays);
|
||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||
if (AutoReminder && reminderCount > 15) {
|
||||
alert(t("only-15-reminder-allowed"));
|
||||
return;
|
||||
}
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||
const ReminderDate = new Date(template?.createdAt);
|
||||
@@ -247,11 +246,7 @@ const EditTemplate = ({
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
@@ -260,11 +255,7 @@ const EditTemplate = ({
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { handleToPrint } from "../../constant/Utils";
|
||||
import { themeColor, emailRegex } from "../../constant/const";
|
||||
import { emailRegex } from "../../constant/const";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Parse from "parse";
|
||||
function EmailComponent({
|
||||
isEmail,
|
||||
pdfUrl,
|
||||
setIsEmail,
|
||||
setSuccessEmail,
|
||||
pdfDetails,
|
||||
sender,
|
||||
setIsAlert,
|
||||
extUserId,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -28,73 +22,11 @@ function EmailComponent({
|
||||
const isAndroid = /Android/i.test(navigator.userAgent);
|
||||
//function for send email
|
||||
const sendEmail = async () => {
|
||||
const pdfName = pdfDetails[0]?.Name;
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
let presignedUrl = pdfUrl;
|
||||
try {
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{
|
||||
url: pdfUrl,
|
||||
docId: docId,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
presignedUrl = axiosRes.data.result;
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
}
|
||||
for (let i = 0; i < emailList.length; i++) {
|
||||
try {
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const logo =
|
||||
`<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>`;
|
||||
const opurl =
|
||||
` <a href='www.opensignlabs.com' target=_blank>here</a>`;
|
||||
|
||||
let params = {
|
||||
extUserId: extUserId,
|
||||
pdfName: pdfName,
|
||||
url: presignedUrl,
|
||||
recipient: emailList[i],
|
||||
subject: `${sender.name} has signed the doc - ${pdfName}`,
|
||||
replyto:
|
||||
pdfDetails?.[0]?.ExtUserPtr?.Email ||
|
||||
"",
|
||||
from:
|
||||
sender.email,
|
||||
html:
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'><div>` +
|
||||
`${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` +
|
||||
`<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${pdfName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` +
|
||||
`</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${sender.email} directly. ` +
|
||||
`If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
}
|
||||
if (sendMail?.data?.result?.status === "success") {
|
||||
const params = { docId: pdfDetails?.[0]?.objectId, recipients: emailList };
|
||||
const sendmail = await Parse.Cloud.run("forwarddoc", params);
|
||||
console.log("sendmail ", sendmail);
|
||||
if (sendmail?.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setIsEmail(false);
|
||||
setTimeout(() => {
|
||||
@@ -103,8 +35,7 @@ function EmailComponent({
|
||||
setEmailList([]);
|
||||
}, 1500);
|
||||
setIsLoading(false);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
|
||||
@@ -76,6 +76,7 @@ const changeDateToMomentFormat = (format) => {
|
||||
};
|
||||
//function to get default format
|
||||
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||
|
||||
//function to convert formated date to new Date() format
|
||||
const getDefaultDate = (dateStr, format) => {
|
||||
//get valid date format for moment to convert formated date to new Date() format
|
||||
@@ -90,6 +91,7 @@ const getDefaultDate = (dateStr, format) => {
|
||||
return date;
|
||||
}
|
||||
};
|
||||
|
||||
function Placeholder(props) {
|
||||
//'isTouchDevice' is used to detect whether a device has a touchscreen or is mouse-based
|
||||
const isTouchDevice = navigator.maxTouchPoints > 0;
|
||||
@@ -437,7 +439,9 @@ function Placeholder(props) {
|
||||
|
||||
//checking widget's type and open widget copy modal for required widgets
|
||||
if (
|
||||
["signature", textWidget, "stamp", "initials"].includes(props.pos.type)
|
||||
["signature", textInputWidget, textWidget, "stamp", "initials"].includes(
|
||||
props.pos.type
|
||||
)
|
||||
) {
|
||||
props.setIsPageCopy(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
@@ -1083,7 +1087,7 @@ function Placeholder(props) {
|
||||
isSignYourself={props.isSignYourself}
|
||||
isSelfSign={props.isSelfSign}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
calculateFontsize={props.calculateFontsize}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
|
||||
@@ -30,6 +30,7 @@ function PlaceholderType(props) {
|
||||
const inputRef = useRef(null);
|
||||
const [textValue, setTextValue] = 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";
|
||||
@@ -150,6 +151,11 @@ function PlaceholderType(props) {
|
||||
if (defaultData) {
|
||||
setTextValue(defaultData);
|
||||
}
|
||||
if (props.pos?.options?.hint) {
|
||||
setHint(props.pos?.options.hint);
|
||||
} else {
|
||||
setHint(props.pos?.type);
|
||||
}
|
||||
} else if ([textInputWidget].includes(props.pos?.type)) {
|
||||
const defaultData = props.pos?.options?.defaultValue;
|
||||
if (defaultData) {
|
||||
@@ -158,7 +164,6 @@ function PlaceholderType(props) {
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos?.options?.defaultValue]);
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
style={{
|
||||
@@ -327,13 +332,20 @@ function PlaceholderType(props) {
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
widgetTypeTraslation,
|
||||
props.pos
|
||||
)}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{props.isNeedSign
|
||||
? props.pos?.options?.hint || widgetTypeTraslation
|
||||
: widgetTypeTraslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "stamp":
|
||||
@@ -346,13 +358,20 @@ function PlaceholderType(props) {
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
widgetTypeTraslation,
|
||||
props.pos
|
||||
)}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{props.isNeedSign
|
||||
? props.pos?.options?.hint || widgetTypeTraslation
|
||||
: widgetTypeTraslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
@@ -533,13 +552,20 @@ function PlaceholderType(props) {
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
widgetTypeTraslation,
|
||||
props.pos
|
||||
)}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium text-center"
|
||||
>
|
||||
{props.isNeedSign
|
||||
? props.pos?.options?.hint || widgetTypeTraslation
|
||||
: widgetTypeTraslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "name":
|
||||
@@ -548,7 +574,7 @@ function PlaceholderType(props) {
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={t("widgets-name.name")}
|
||||
placeholder={hint}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
@@ -580,7 +606,7 @@ function PlaceholderType(props) {
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={t("widgets-name.company")}
|
||||
placeholder={hint}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
@@ -611,7 +637,7 @@ function PlaceholderType(props) {
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={t("widgets-name.job title")}
|
||||
placeholder={hint}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
@@ -712,13 +738,20 @@ function PlaceholderType(props) {
|
||||
/>
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
widgetTypeTraslation,
|
||||
props.pos
|
||||
)}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium text-center"
|
||||
>
|
||||
{props.isNeedSign
|
||||
? props.pos?.options?.hint || widgetTypeTraslation
|
||||
: widgetTypeTraslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "email":
|
||||
@@ -727,7 +760,7 @@ function PlaceholderType(props) {
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={t("widgets-name.email")}
|
||||
placeholder={hint}
|
||||
rows={1}
|
||||
onKeyDown={(e) => {
|
||||
// Prevent new line on Enter key press
|
||||
@@ -848,13 +881,20 @@ function PlaceholderType(props) {
|
||||
) : (
|
||||
<div className={widgetCls}>
|
||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
null,
|
||||
props.pos
|
||||
)}
|
||||
{props.pos.type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos
|
||||
? props.calculateFontsize(props.pos)
|
||||
: "11px"
|
||||
}}
|
||||
className="font-medium"
|
||||
>
|
||||
{props.isNeedSign
|
||||
? props.pos?.options?.hint || widgetTypeTraslation
|
||||
: widgetTypeTraslation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,35 +89,6 @@ function RenderPdf(props) {
|
||||
sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
|
||||
)
|
||||
: [];
|
||||
const handleAllUserName = (Id, Role, type, pos) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="text-black text-[8px] font-bold">
|
||||
{
|
||||
props.pdfDetails[0].Signers?.find(
|
||||
(signer) => signer.objectId === data.signerObjId
|
||||
)?.Name
|
||||
}
|
||||
</div>
|
||||
{type && (
|
||||
<div
|
||||
style={{ fontSize: pos ? calculateFontsize(pos) : "11px" }}
|
||||
className="font-bold"
|
||||
>
|
||||
{type}
|
||||
</div>
|
||||
)}
|
||||
{Role && (
|
||||
<div
|
||||
style={{ fontSize: pos ? calculateFontsize(pos) : "8px" }}
|
||||
className="text-black font-medium"
|
||||
>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
return (
|
||||
checkSign.length === 0 &&
|
||||
data?.placeHolder?.map((placeData, key) => {
|
||||
@@ -148,7 +119,6 @@ function RenderPdf(props) {
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
handleUserName={handleAllUserName}
|
||||
isDragging={props.isDragging}
|
||||
pdfDetails={props.pdfDetails}
|
||||
setIsInitial={props.setIsInitial}
|
||||
@@ -185,6 +155,7 @@ function RenderPdf(props) {
|
||||
fontColor={props.fontColor}
|
||||
setFontColor={props.setFontColor}
|
||||
setRequestSignTour={props.setRequestSignTour}
|
||||
calculateFontsize={calculateFontsize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -206,73 +177,6 @@ function RenderPdf(props) {
|
||||
return `${width / 10}px`;
|
||||
}
|
||||
};
|
||||
//function for render placeholder block over pdf document
|
||||
|
||||
const handleUserName = (Id, Role, type, pos) => {
|
||||
if (Id) {
|
||||
const checkSign = props.signersdata.find((sign) => sign.Id === Id);
|
||||
if (checkSign?.Name) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
fontSize: pos ? calculateFontsize(pos) : "8px"
|
||||
}}
|
||||
className="text-black font-medium"
|
||||
>
|
||||
{checkSign?.Name}
|
||||
</div>
|
||||
{type && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: pos ? calculateFontsize(pos) : "11px"
|
||||
}}
|
||||
className=" font-bold"
|
||||
>
|
||||
{type}
|
||||
</div>
|
||||
)}
|
||||
{Role && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: pos ? calculateFontsize(pos) : "8px"
|
||||
}}
|
||||
className="text-black text-[8px] font-medium"
|
||||
>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && (
|
||||
<div
|
||||
style={{ fontSize: pos ? calculateFontsize(pos) : "11px" }}
|
||||
className=" font-bold"
|
||||
>
|
||||
{type}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{ fontSize: pos ? calculateFontsize(pos) : "8px" }}
|
||||
className="text-black font-medium"
|
||||
>
|
||||
{Role}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||
<div className="text-black text-[8px] font-medium">{Role}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
const pdfDataBase64 = `data:application/pdf;base64,${props.pdfBase64Url}`;
|
||||
//calculate render height of pdf in mobile view
|
||||
const handlePageLoadSuccess = (page) => {
|
||||
@@ -361,7 +265,6 @@ function RenderPdf(props) {
|
||||
handleLinkUser={
|
||||
props.handleLinkUser
|
||||
}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
@@ -403,6 +306,9 @@ function RenderPdf(props) {
|
||||
props.unSignedWidgetId
|
||||
}
|
||||
isFreeResize={true}
|
||||
calculateFontsize={
|
||||
calculateFontsize
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -448,7 +354,6 @@ function RenderPdf(props) {
|
||||
setWidgetType={props.setWidgetType}
|
||||
setSelectWidgetId={props.setSelectWidgetId}
|
||||
selectWidgetId={props.selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={props.setIsCheckbox}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
@@ -468,6 +373,7 @@ function RenderPdf(props) {
|
||||
setIsResize={props.setIsResize}
|
||||
isFreeResize={false}
|
||||
isOpenSignPad={true}
|
||||
calculateFontsize={calculateFontsize}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -573,7 +479,6 @@ function RenderPdf(props) {
|
||||
handleLinkUser={
|
||||
props.handleLinkUser
|
||||
}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
@@ -615,6 +520,9 @@ function RenderPdf(props) {
|
||||
props.unSignedWidgetId
|
||||
}
|
||||
isFreeResize={true}
|
||||
calculateFontsize={
|
||||
calculateFontsize
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -665,7 +573,6 @@ function RenderPdf(props) {
|
||||
setWidgetType={props.setWidgetType}
|
||||
setSelectWidgetId={props.setSelectWidgetId}
|
||||
selectWidgetId={props.selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={props.setIsCheckbox}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
@@ -686,6 +593,7 @@ function RenderPdf(props) {
|
||||
setIsResize={props.setIsResize}
|
||||
isFreeResize={false}
|
||||
isOpenSignPad={true}
|
||||
calculateFontsize={calculateFontsize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -31,7 +31,11 @@ const WidgetNameModal = (props) => {
|
||||
name: props.defaultdata?.options?.name || "",
|
||||
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||
status: props.defaultdata?.options?.status || "required",
|
||||
hint: props.defaultdata?.options?.hint || "",
|
||||
hint:
|
||||
props.defaultdata?.options?.hint ||
|
||||
(props.defaultdata?.type === textInputWidget
|
||||
? "Enter text"
|
||||
: `Enter ${props.defaultdata?.options?.name}`),
|
||||
textvalidate:
|
||||
props.defaultdata?.options?.validation?.type === "regex"
|
||||
? props.defaultdata?.options?.validation?.pattern
|
||||
@@ -113,7 +117,6 @@ const WidgetNameModal = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Update the state with the modified array
|
||||
setSignatureType((prev) =>
|
||||
@@ -272,12 +275,13 @@ const WidgetNameModal = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
{props.defaultdata?.type !== textWidget && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="hint" className="text-[13px]">
|
||||
{t("hint")}
|
||||
</label>
|
||||
<input
|
||||
maxLength={40}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="hint"
|
||||
value={formdata.hint}
|
||||
@@ -295,7 +299,7 @@ const WidgetNameModal = (props) => {
|
||||
].includes(props.defaultdata?.type) && (
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-3 mb-3">
|
||||
<div className="flex items-center gap-2 ">
|
||||
<span>{t("font-size")}:</span>
|
||||
<span className="whitespace-nowrap">{t("font-size")}: </span>
|
||||
<select
|
||||
className="ml-[7px] w-[60%] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
|
||||
@@ -7,9 +7,7 @@ import { appInfo } from "./appinfo";
|
||||
import { saveAs } from "file-saver";
|
||||
import printModule from "print-js";
|
||||
import fontkit from "@pdf-lib/fontkit";
|
||||
import {
|
||||
themeColor
|
||||
} from "./const";
|
||||
import { themeColor } from "./const";
|
||||
|
||||
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||
export const fontColorArr = ["red", "black", "blue", "yellow"];
|
||||
@@ -136,14 +134,12 @@ export const pdfNewWidthFun = (divRef) => {
|
||||
};
|
||||
|
||||
//`contractUsers` function is used to get contract_User details
|
||||
export const contractUsers = async (
|
||||
) => {
|
||||
export const contractUsers = async () => {
|
||||
try {
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const accesstoken = localStorage.getItem("accesstoken");
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": accesstoken };
|
||||
const token = { "X-Parse-Session-Token": accesstoken };
|
||||
const headers = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -495,7 +491,7 @@ export const signPdfFun = async (
|
||||
documentId,
|
||||
signerObjectId,
|
||||
objectId,
|
||||
widgets,
|
||||
widgets
|
||||
) => {
|
||||
let isCustomCompletionMail = false;
|
||||
try {
|
||||
@@ -504,10 +500,7 @@ export const signPdfFun = async (
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
return { status: "error", message: "User does not exist." };
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject
|
||||
) {
|
||||
if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
@@ -642,7 +635,13 @@ export const createDocument = async (
|
||||
...Bcc,
|
||||
...RedirectUrl
|
||||
};
|
||||
|
||||
const remindOnceInEvery = Doc?.RemindOnceInEvery;
|
||||
const TimeToCompleteDays = parseInt(Doc?.TimeToCompleteDays);
|
||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||
const AutomaticReminders = Doc.autoreminder;
|
||||
if (AutomaticReminders && reminderCount > 15) {
|
||||
return { status: "error", id: "only-15-reminder-allowed" };
|
||||
}
|
||||
try {
|
||||
const res = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Document`,
|
||||
@@ -660,7 +659,7 @@ export const createDocument = async (
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("axois err ", err);
|
||||
return { status: "error", id: "Something Went Wrong!" };
|
||||
return { status: "error", id: "something-went-wrong-mssg" };
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -985,7 +984,7 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
||||
...item,
|
||||
options: {
|
||||
...item.options,
|
||||
defaultValue: widgetData
|
||||
defaultValue: item?.options?.defaultValue || widgetData
|
||||
}
|
||||
// Width:
|
||||
// calculateInitialWidthHeight(item.type, widgetData).getWidth ||
|
||||
@@ -1005,8 +1004,7 @@ export const addInitialData = (signerPos, setXyPosition, value, userId) => {
|
||||
|
||||
//function for embed document id
|
||||
export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
// `fontBytes` is used to embed custom font in pdf
|
||||
const fontBytes = await fileasbytes(
|
||||
"https://cdn.opensignlabs.com/webfonts/times.ttf"
|
||||
@@ -1868,12 +1866,9 @@ export const contactBook = async (objectId) => {
|
||||
};
|
||||
|
||||
//function for getting document details from contract_Documents class
|
||||
export const contractDocument = async (
|
||||
documentId,
|
||||
) => {
|
||||
export const contractDocument = async (documentId) => {
|
||||
const data = { docId: documentId };
|
||||
const token =
|
||||
{ sessionToken: localStorage.getItem("accesstoken") };
|
||||
const token = { sessionToken: localStorage.getItem("accesstoken") };
|
||||
const documentDeatils = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
|
||||
headers: {
|
||||
@@ -2057,36 +2052,31 @@ export const getFileName = (fileUrl) => {
|
||||
|
||||
//fetch tenant app logo from `partners_Tenant` class by domain name
|
||||
export const getAppLogo = async () => {
|
||||
const domain = window.location.host;
|
||||
try {
|
||||
const tenant = await Parse.Cloud.run("getlogobydomain", {
|
||||
domain: domain
|
||||
});
|
||||
if (tenant) {
|
||||
localStorage.setItem("appname", "OpenSign™");
|
||||
return { logo: tenant?.logo, user: tenant?.user };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getlogo ", err);
|
||||
if (err?.message?.includes("valid JSON")) {
|
||||
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
|
||||
} else {
|
||||
return { logo: appInfo.applogo, user: "exist" };
|
||||
}
|
||||
const domain = window.location.host;
|
||||
try {
|
||||
const tenant = await Parse.Cloud.run("getlogobydomain", {
|
||||
domain: domain
|
||||
});
|
||||
if (tenant) {
|
||||
localStorage.setItem("appname", "OpenSign™");
|
||||
return { logo: tenant?.logo, user: tenant?.user };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getlogo ", err);
|
||||
if (err?.message?.includes("valid JSON")) {
|
||||
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
|
||||
} else {
|
||||
return { logo: appInfo.applogo, user: "exist" };
|
||||
}
|
||||
}
|
||||
};
|
||||
export const getTenantDetails = async (
|
||||
objectId,
|
||||
contactId
|
||||
) => {
|
||||
export const getTenantDetails = async (objectId, contactId) => {
|
||||
try {
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const accesstoken = localStorage.getItem("accesstoken");
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": accesstoken };
|
||||
const data =
|
||||
{ userId: objectId, contactId: contactId };
|
||||
const token = { "X-Parse-Session-Token": accesstoken };
|
||||
const data = { userId: objectId, contactId: contactId };
|
||||
const res = await axios.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -2173,8 +2163,7 @@ export const handleSendOTP = async (email) => {
|
||||
}
|
||||
};
|
||||
export const fetchUrl = async (url, pdfName) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
@@ -2188,11 +2177,7 @@ export const fetchUrl = async (url, pdfName) => {
|
||||
console.error("Error downloading the file:", error);
|
||||
}
|
||||
};
|
||||
export const getSignedUrl = async (
|
||||
pdfUrl,
|
||||
docId,
|
||||
templateId
|
||||
) => {
|
||||
export const getSignedUrl = async (pdfUrl, docId, templateId) => {
|
||||
//use only axios here due to public template sign
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
@@ -2253,10 +2238,7 @@ export const handleDownloadPdf = async (
|
||||
setIsDownloading && setIsDownloading("pdf");
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
try {
|
||||
const url = await getSignedUrl(
|
||||
pdfUrl,
|
||||
docId,
|
||||
);
|
||||
const url = await getSignedUrl(pdfUrl, docId);
|
||||
await fetchUrl(url, pdfName);
|
||||
setIsDownloading && setIsDownloading("");
|
||||
} catch (err) {
|
||||
@@ -2286,7 +2268,7 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{
|
||||
url: pdfUrl,
|
||||
docId: docId,
|
||||
docId: docId
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -2330,8 +2312,7 @@ export const handleDownloadCertificate = async (
|
||||
setIsDownloading,
|
||||
isZip
|
||||
) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||
try {
|
||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||
@@ -2415,14 +2396,13 @@ export const handleDownloadCertificate = async (
|
||||
export function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Escape special characters
|
||||
}
|
||||
export async function findContact(
|
||||
value,
|
||||
) {
|
||||
export async function findContact(value) {
|
||||
try {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsigners`;
|
||||
const token =
|
||||
{ "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const token = {
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
@@ -2634,23 +2614,20 @@ export function base64ToArrayBuffer(base64) {
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
export const convertBase64ToFile = async (
|
||||
pdfName,
|
||||
pdfBase64,
|
||||
) => {
|
||||
export const convertBase64ToFile = async (pdfName, pdfBase64) => {
|
||||
const fileName = sanitizeFileName(pdfName) + ".pdf";
|
||||
try {
|
||||
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
|
||||
// Save the Parse File if needed
|
||||
const pdfData = await pdfFile.save();
|
||||
const pdfUrl = pdfData.url();
|
||||
const fileRes = await getSecureUrl(pdfUrl);
|
||||
if (fileRes?.url) {
|
||||
return fileRes.url;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error in convertbase64tofile", e);
|
||||
try {
|
||||
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
|
||||
// Save the Parse File if needed
|
||||
const pdfData = await pdfFile.save();
|
||||
const pdfUrl = pdfData.url();
|
||||
const fileRes = await getSecureUrl(pdfUrl);
|
||||
if (fileRes?.url) {
|
||||
return fileRes.url;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error in convertbase64tofile", e);
|
||||
}
|
||||
};
|
||||
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
|
||||
setScale(scale + 0.1 * scale);
|
||||
@@ -2949,13 +2926,10 @@ export const flattenPdf = async (pdfFile) => {
|
||||
};
|
||||
|
||||
export const mailTemplate = (param) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const logo =
|
||||
`<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
|
||||
const appName = "OpenSign™";
|
||||
const logo = `<div style='padding:10px'><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' /></div>`;
|
||||
|
||||
const opurl =
|
||||
` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
|
||||
const opurl = ` <a href='https://www.opensignlabs.com' target=_blank>here</a>.</p></div></div></body></html>`;
|
||||
|
||||
const subject = `${param.senderName} has requested you to sign "${param.title}"`;
|
||||
const body =
|
||||
|
||||
+222
-213
@@ -36,8 +36,7 @@ function Form() {
|
||||
}
|
||||
|
||||
const Forms = (props) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const maxFileSize = 20;
|
||||
const abortController = new AbortController();
|
||||
@@ -81,6 +80,15 @@ const Forms = (props) => {
|
||||
const extUserData =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
const sendinorder =
|
||||
extUserData?.SendinOrder !== undefined && extUserData?.SendinOrder === false
|
||||
? "false"
|
||||
: "true";
|
||||
const istourenabled =
|
||||
extUserData?.IsTourEnabled !== undefined &&
|
||||
extUserData?.IsTourEnabled === false
|
||||
? "false"
|
||||
: "true";
|
||||
useEffect(() => {
|
||||
handleReset();
|
||||
return () => abortController.abort();
|
||||
@@ -92,7 +100,12 @@ const Forms = (props) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const initializeValues = async () => {
|
||||
setFormData((obj) => ({ ...obj, NotifyOnSignatures: true }));
|
||||
setFormData((obj) => ({
|
||||
...obj,
|
||||
NotifyOnSignatures: true,
|
||||
SendinOrder: sendinorder,
|
||||
IsTourEnabled: istourenabled
|
||||
}));
|
||||
};
|
||||
|
||||
function getFileAsArrayBuffer(file) {
|
||||
@@ -130,17 +143,88 @@ const Forms = (props) => {
|
||||
const name = generatePdfName(16);
|
||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||
setfileload(true);
|
||||
try {
|
||||
const res = await getFileAsArrayBuffer(files[0]);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...flatPdf],
|
||||
"application/pdf"
|
||||
);
|
||||
try {
|
||||
const res = await getFileAsArrayBuffer(files[0]);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...flatPdf],
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} catch (error) {
|
||||
removeFile(e);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
await Parse.Cloud.run("encryptedpdf", {
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
}
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const size = files?.[0].size;
|
||||
const name = generatePdfName(16);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("password", "");
|
||||
const config = {
|
||||
headers: { "content-type": "multipart/form-data" },
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, formData, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(
|
||||
name,
|
||||
[...flatPdf],
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
@@ -150,16 +234,16 @@ const Forms = (props) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
const fileRes = await getSecureUrl(parseFile.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
removeFile();
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
@@ -168,95 +252,22 @@ const Forms = (props) => {
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} catch (error) {
|
||||
removeFile(e);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
await Parse.Cloud.run("encryptedpdf", {
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
}
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const size = files?.[0].size;
|
||||
const name = generatePdfName(16);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("password", "");
|
||||
const config = {
|
||||
headers: { "content-type": "multipart/form-data" },
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, formData, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
} catch (err) {
|
||||
removeFile();
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
} else {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(
|
||||
name,
|
||||
[...flatPdf],
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
const fileRes = await getSecureUrl(parseFile.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
removeFile();
|
||||
const title = generateTitleFromFilename(
|
||||
files?.[0]?.name
|
||||
);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} catch (err) {
|
||||
removeFile();
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
} else {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setIsDecrypting(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
e.target.value = "";
|
||||
}
|
||||
} else {
|
||||
console.log("err ", err);
|
||||
setFileUpload("");
|
||||
removeFile(e);
|
||||
}
|
||||
} else {
|
||||
console.log("err ", err);
|
||||
setFileUpload("");
|
||||
removeFile(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const isImage = files?.[0]?.type.includes("image/");
|
||||
if (isImage) {
|
||||
@@ -281,50 +292,50 @@ const Forms = (props) => {
|
||||
});
|
||||
const size = files?.[0]?.size;
|
||||
const name = generatePdfName(16);
|
||||
const getFile = await pdfDoc.save({
|
||||
useObjectStreams: false
|
||||
});
|
||||
setfileload(true);
|
||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...getFile],
|
||||
"application/pdf"
|
||||
);
|
||||
const getFile = await pdfDoc.save({
|
||||
useObjectStreams: false
|
||||
});
|
||||
setfileload(true);
|
||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...getFile],
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile(e);
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
if (response.url()) {
|
||||
const fileRes = await getSecureUrl(response.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile(e);
|
||||
}
|
||||
} catch (error) {
|
||||
} else {
|
||||
removeFile(e);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
removeFile(e);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,19 +385,24 @@ const Forms = (props) => {
|
||||
const isChecked = formData.SendinOrder === "false" ? false : true;
|
||||
const isTourEnabled =
|
||||
formData?.IsTourEnabled === "false" ? false : true;
|
||||
const remindOnceInEvery = parseInt(formData.remindOnceInEvery);
|
||||
const TimeToCompleteDays = parseInt(formData?.TimeToCompleteDays);
|
||||
const AutomaticReminders = formData.autoreminder;
|
||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||
if (AutomaticReminders && reminderCount > 15) {
|
||||
alert(t("only-15-reminder-allowed"));
|
||||
return;
|
||||
}
|
||||
object.set("SendinOrder", isChecked);
|
||||
object.set("AutomaticReminders", formData.autoreminder);
|
||||
object.set("RemindOnceInEvery", parseInt(formData.remindOnceInEvery));
|
||||
object.set("AutomaticReminders", AutomaticReminders);
|
||||
object.set("RemindOnceInEvery", remindOnceInEvery);
|
||||
object.set("IsTourEnabled", isTourEnabled);
|
||||
object.set(
|
||||
"TimeToCompleteDays",
|
||||
parseInt(formData?.TimeToCompleteDays)
|
||||
);
|
||||
object.set("AllowModifications", false);
|
||||
object.set("IsEnableOTP", false);
|
||||
if (formData.NotifyOnSignatures !== undefined) {
|
||||
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
||||
}
|
||||
object.set("TimeToCompleteDays", TimeToCompleteDays);
|
||||
object.set("AllowModifications", false);
|
||||
object.set("IsEnableOTP", false);
|
||||
if (formData.NotifyOnSignatures !== undefined) {
|
||||
object.set("NotifyOnSignatures", formData.NotifyOnSignatures);
|
||||
}
|
||||
if (formData?.RedirectUrl) {
|
||||
object.set("RedirectUrl", formData.RedirectUrl);
|
||||
}
|
||||
@@ -425,10 +441,9 @@ const Forms = (props) => {
|
||||
setSigners([]);
|
||||
setBcc([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
const notifySign = extUserData?.NotifyOnSignatures
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
setFormData({
|
||||
Name: "",
|
||||
Description: "",
|
||||
@@ -437,14 +452,14 @@ const Forms = (props) => {
|
||||
? "Note to myself"
|
||||
: "Please review and sign this document",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "true",
|
||||
SendinOrder: sendinorder,
|
||||
password: "",
|
||||
file: "",
|
||||
NotifyOnSignatures: notifySign,
|
||||
remindOnceInEvery: 5,
|
||||
autoreminder: false,
|
||||
IsEnableOTP: "false",
|
||||
IsTourEnabled: "true",
|
||||
IsTourEnabled: istourenabled,
|
||||
RedirectUrl: "",
|
||||
AllowModifications: false
|
||||
});
|
||||
@@ -454,7 +469,17 @@ const Forms = (props) => {
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
setIsAlert({ type: "danger", message: t("something-went-wrong-mssg") });
|
||||
if (err.message === "only 15 reminder allowed") {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: t("only-15-reminder-allowed")
|
||||
});
|
||||
} else {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setTimeout(() => setIsAlert({ type: "success", message: "" }), 1000);
|
||||
setIsSubmit(false);
|
||||
@@ -494,10 +519,9 @@ const Forms = (props) => {
|
||||
setSigners([]);
|
||||
setBcc([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
const notifySign =
|
||||
extUserData?.NotifyOnSignatures
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
const notifySign = extUserData?.NotifyOnSignatures
|
||||
? extUserData?.NotifyOnSignatures
|
||||
: true;
|
||||
let obj = {
|
||||
Name: "",
|
||||
Description: "",
|
||||
@@ -506,13 +530,13 @@ const Forms = (props) => {
|
||||
? "Note to myself"
|
||||
: "Please review and sign this document",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "true",
|
||||
SendinOrder: sendinorder,
|
||||
password: "",
|
||||
file: "",
|
||||
remindOnceInEvery: 5,
|
||||
autoreminder: false,
|
||||
IsEnableOTP: "false",
|
||||
IsTourEnabled: "true",
|
||||
IsTourEnabled: istourenabled,
|
||||
NotifyOnSignatures: notifySign,
|
||||
RedirectUrl: "",
|
||||
AllowModifications: false
|
||||
@@ -551,36 +575,28 @@ const Forms = (props) => {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
const fileRes = await getSecureUrl(parseFile.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
removeFile();
|
||||
const title = generateTitleFromFilename(formData?.file?.name);
|
||||
setFormData((obj) => ({ ...obj, password: "", Name: title }));
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile();
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setIsDecrypting(false);
|
||||
if (inputFileRef.current) {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
const parseFile = new Parse.File(name, [...flatPdf], "application/pdf");
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
const fileRes = await getSecureUrl(parseFile.url());
|
||||
if (fileRes.url) {
|
||||
setFileUpload(fileRes.url);
|
||||
removeFile();
|
||||
const title = generateTitleFromFilename(formData?.file?.name);
|
||||
setFormData((obj) => ({ ...obj, password: "", Name: title }));
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, fileRes.url, tenantId);
|
||||
return fileRes.url;
|
||||
} else {
|
||||
removeFile();
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
@@ -589,6 +605,14 @@ const Forms = (props) => {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removeFile();
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setIsDecrypting(false);
|
||||
if (inputFileRef.current) {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
removeFile();
|
||||
if (err?.response?.status === 401) {
|
||||
@@ -710,9 +734,7 @@ const Forms = (props) => {
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
{`${`${t("report-heading.File")} (${t("file-type")}`}${
|
||||
")"
|
||||
}`}
|
||||
{`${`${t("report-heading.File")} (${t("file-type")}`}${")"}`}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
@@ -736,9 +758,7 @@ const Forms = (props) => {
|
||||
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
ref={inputFileRef}
|
||||
accept={
|
||||
"application/pdf,image/png,image/jpeg"
|
||||
}
|
||||
accept={"application/pdf,image/png,image/jpeg"}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
@@ -1028,10 +1048,7 @@ const Forms = (props) => {
|
||||
{isAdvanceOpt && (
|
||||
<div
|
||||
style={{
|
||||
height:
|
||||
props.title === "New Template"
|
||||
? "100px"
|
||||
: "280px"
|
||||
height: props.title === "New Template" ? "100px" : "280px"
|
||||
}}
|
||||
className="w-[1px] bg-gray-300 m-auto hidden md:inline-block"
|
||||
></div>
|
||||
@@ -1140,11 +1157,7 @@ const Forms = (props) => {
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
@@ -1153,11 +1166,7 @@ const Forms = (props) => {
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
`flex items-center gap-2 ml-2 mb-1`
|
||||
}
|
||||
>
|
||||
<div className={`flex items-center gap-2 ml-2 mb-1`}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
|
||||
@@ -68,8 +68,8 @@ import AddContact from "../primitives/AddContact";
|
||||
|
||||
function PlaceHolderSign() {
|
||||
const { t } = useTranslation();
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const copyUrlRef = useRef(null);
|
||||
const appName = "OpenSign™";
|
||||
const editorRef = useRef();
|
||||
const { state } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -863,11 +863,7 @@ function PlaceHolderSign() {
|
||||
scale
|
||||
);
|
||||
const pdfName = generatePdfName(16);
|
||||
const pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64,
|
||||
"",
|
||||
);
|
||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, "");
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const buffer = atob(pdfBase64);
|
||||
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||
@@ -879,11 +875,7 @@ function PlaceHolderSign() {
|
||||
} else if (pdfBase64Url) {
|
||||
try {
|
||||
const pdfName = generatePdfName(16);
|
||||
const pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64Url,
|
||||
"",
|
||||
);
|
||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
||||
return pdfUrl;
|
||||
} catch (err) {
|
||||
console.log("error to convertBase64ToFile in placeholder flow", err);
|
||||
@@ -989,11 +981,7 @@ function PlaceHolderSign() {
|
||||
let pdfUrl;
|
||||
if (isUploadPdf) {
|
||||
const pdfName = generatePdfName(16);
|
||||
pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64Url,
|
||||
"",
|
||||
);
|
||||
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
||||
}
|
||||
try {
|
||||
const docCls = new Parse.Object("contracts_Document");
|
||||
@@ -1091,6 +1079,9 @@ function PlaceHolderSign() {
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
if (copyUrlRef.current) {
|
||||
copyUrlRef.current.textContent = text; // Update text safely
|
||||
}
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
@@ -1149,8 +1140,7 @@ function PlaceHolderSign() {
|
||||
year: "numeric"
|
||||
});
|
||||
|
||||
let senderEmail =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
let signerMail = signersdata.slice();
|
||||
|
||||
@@ -1176,16 +1166,11 @@ function PlaceHolderSign() {
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const senderName =
|
||||
pdfDetails?.[0].ExtUserPtr.Name;
|
||||
const senderName = pdfDetails?.[0].ExtUserPtr.Name;
|
||||
const documentName = `${pdfDetails?.[0].Name}`;
|
||||
let replaceVar;
|
||||
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
isCustomize
|
||||
) {
|
||||
if (requestBody && requestSubject && isCustomize) {
|
||||
const replacedRequestBody = requestBody.replace(/"/g, "'");
|
||||
htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
@@ -1209,10 +1194,7 @@ function PlaceHolderSign() {
|
||||
htmlReqBody,
|
||||
variables
|
||||
);
|
||||
} else if (
|
||||
tenantMailTemplate?.body &&
|
||||
tenantMailTemplate?.subject
|
||||
) {
|
||||
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
|
||||
const mailBody = tenantMailTemplate?.body;
|
||||
const mailSubject = tenantMailTemplate?.subject;
|
||||
const replacedRequestBody = mailBody.replace(/"/g, "'");
|
||||
@@ -1249,8 +1231,7 @@ function PlaceHolderSign() {
|
||||
? replaceVar?.subject
|
||||
: mailTemplate(mailparam).subject,
|
||||
replyto: senderEmail,
|
||||
from:
|
||||
senderEmail,
|
||||
from: senderEmail,
|
||||
html: replaceVar?.body
|
||||
? replaceVar?.body
|
||||
: mailTemplate(mailparam).body
|
||||
@@ -1265,20 +1246,13 @@ function PlaceHolderSign() {
|
||||
setMailStatus("success");
|
||||
try {
|
||||
let data;
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
isCustomize
|
||||
) {
|
||||
if (requestBody && requestSubject && isCustomize) {
|
||||
data = {
|
||||
RequestBody: htmlReqBody,
|
||||
RequestSubject: requestSubject,
|
||||
SendMail: true
|
||||
};
|
||||
} else if (
|
||||
tenantMailTemplate?.body &&
|
||||
tenantMailTemplate?.subject
|
||||
) {
|
||||
} else if (tenantMailTemplate?.body && tenantMailTemplate?.subject) {
|
||||
data = {
|
||||
RequestBody: tenantMailTemplate?.body,
|
||||
RequestSubject: tenantMailTemplate?.subject,
|
||||
@@ -1311,8 +1285,7 @@ function PlaceHolderSign() {
|
||||
setIsMailSend(true);
|
||||
setIsLoading({ isLoad: false });
|
||||
setIsUiLoading(false);
|
||||
}
|
||||
else if (sendMail?.data?.result?.status === "quota-reached") {
|
||||
} else if (sendMail?.data?.result?.status === "quota-reached") {
|
||||
setMailStatus("quotareached");
|
||||
setIsSend(true);
|
||||
setIsMailSend(true);
|
||||
@@ -1558,8 +1531,7 @@ function PlaceHolderSign() {
|
||||
status: defaultdata?.status || "required",
|
||||
hint: defaultdata?.hint || "",
|
||||
defaultValue: defaultdata?.defaultValue || "",
|
||||
validation:
|
||||
{},
|
||||
validation: {},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
@@ -1569,6 +1541,15 @@ function PlaceHolderSign() {
|
||||
isReadOnly: defaultdata?.isReadOnly || false
|
||||
}
|
||||
};
|
||||
} else if (["signature"].includes(position.type)) {
|
||||
return {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
name: defaultdata.name,
|
||||
hint: defaultdata?.hint || ""
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...position,
|
||||
@@ -1577,6 +1558,7 @@ function PlaceHolderSign() {
|
||||
name: defaultdata.name,
|
||||
status: defaultdata.status,
|
||||
defaultValue: defaultdata.defaultValue,
|
||||
hint: defaultdata?.hint || "",
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
@@ -2024,30 +2006,26 @@ function PlaceHolderSign() {
|
||||
{!isCustomize && (
|
||||
<span>{t("placeholder-alert-3")}</span>
|
||||
)}
|
||||
{
|
||||
isCustomize && (
|
||||
<>
|
||||
<EmailBody
|
||||
editorRef={editorRef}
|
||||
requestBody={requestBody}
|
||||
requestSubject={requestSubject}
|
||||
handleOnchangeRequest={
|
||||
handleOnchangeRequest
|
||||
}
|
||||
setRequestSubject={setRequestSubject}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||
onClick={() => {
|
||||
setRequestBody(defaultBody);
|
||||
setRequestSubject(defaultSubject);
|
||||
}}
|
||||
>
|
||||
<span>{t("reset-to-default")}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{isCustomize && (
|
||||
<>
|
||||
<EmailBody
|
||||
editorRef={editorRef}
|
||||
requestBody={requestBody}
|
||||
requestSubject={requestSubject}
|
||||
handleOnchangeRequest={handleOnchangeRequest}
|
||||
setRequestSubject={setRequestSubject}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||
onClick={() => {
|
||||
setRequestBody(defaultBody);
|
||||
setRequestSubject(defaultSubject);
|
||||
}}
|
||||
>
|
||||
<span>{t("reset-to-default")}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex flex-row items-center gap-2 md:gap-6 mt-2">
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
@@ -2065,16 +2043,14 @@ function PlaceHolderSign() {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
!isCustomize && (
|
||||
<span
|
||||
className="op-link op-link-accent text-sm"
|
||||
onClick={() => setIsCustomize(!isCustomize)}
|
||||
>
|
||||
{t("cutomize-email")}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{!isCustomize && (
|
||||
<span
|
||||
className="op-link op-link-accent text-sm"
|
||||
onClick={() => setIsCustomize(!isCustomize)}
|
||||
>
|
||||
{t("cutomize-email")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -2086,6 +2062,11 @@ function PlaceHolderSign() {
|
||||
<span className="h-[1px] w-[20%] bg-[#ccc]"></span>
|
||||
</div>
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
<p
|
||||
id="copyUrl"
|
||||
ref={copyUrlRef}
|
||||
className="hidden"
|
||||
></p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -2127,7 +2108,11 @@ function PlaceHolderSign() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-[10px]">
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
{mailStatus === "dailyquotareached" ? (
|
||||
<p>{t("daily-quota-reached")}</p>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
)}
|
||||
{isCurrUser && (
|
||||
<p className="mt-1">{t("placeholder-alert-5")}</p>
|
||||
)}
|
||||
|
||||
@@ -22,8 +22,7 @@ import EditorToolbar, {
|
||||
} from "../components/pdf/EditorToolbar";
|
||||
|
||||
const Preferences = () => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const editorRef = useRef();
|
||||
const editorRefCom = useRef();
|
||||
@@ -46,6 +45,8 @@ const Preferences = () => {
|
||||
const [activeTab, setactiveTab] = useState(0);
|
||||
const generaltab = { title: t("general"), icon: "fa-light fa-gears" };
|
||||
const [tab, setTab] = useState([generaltab]);
|
||||
const [sendinOrder, setSendinOrder] = useState(true);
|
||||
const [isTourEnabled, setIsTourEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSignType();
|
||||
@@ -90,6 +91,14 @@ const Preferences = () => {
|
||||
const SignatureType = _getUser?.SignatureType || signatureTypes;
|
||||
setSignatureType(SignatureType);
|
||||
}
|
||||
const sendinorder =
|
||||
_getUser?.SendinOrder !== undefined ? _getUser?.SendinOrder : true;
|
||||
setSendinOrder(sendinorder);
|
||||
const istourenabled =
|
||||
_getUser?.IsTourEnabled !== undefined
|
||||
? _getUser?.IsTourEnabled
|
||||
: true;
|
||||
setIsTourEnabled(istourenabled);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting user details", err);
|
||||
@@ -142,6 +151,11 @@ const Preferences = () => {
|
||||
params = { ...params, NotifyOnSignatures: isNotifyOnSignatures };
|
||||
}
|
||||
try {
|
||||
params = {
|
||||
...params,
|
||||
SendinOrder: sendinOrder,
|
||||
IsTourEnabled: isTourEnabled
|
||||
};
|
||||
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
||||
if (updateRes) {
|
||||
setIsAlert({ type: "success", msg: "Saved successfully." });
|
||||
@@ -150,6 +164,8 @@ const Preferences = () => {
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.NotifyOnSignatures = isNotifyOnSignatures;
|
||||
extUser.SendinOrder = sendinOrder;
|
||||
extUser.IsTourEnabled = isTourEnabled;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
@@ -285,6 +301,9 @@ const Preferences = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTourInput = () => setIsTourEnabled(!isTourEnabled);
|
||||
const handleSendinOrderInput = () => setSendinOrder(!sendinOrder);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={t("Preferences")} />
|
||||
@@ -302,7 +321,7 @@ const Preferences = () => {
|
||||
) : (
|
||||
<div className="relative bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
|
||||
{isLoader && (
|
||||
<div className="flex justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<div className="flex z-[100] justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
@@ -322,7 +341,9 @@ const Preferences = () => {
|
||||
onClick={() => setactiveTab(ind)}
|
||||
key={ind}
|
||||
role="tab"
|
||||
className={`${activeTab === ind ? "op-tab-active" : ""} op-tab text-xs md:text-base pb-2 md:pb-0 }`}
|
||||
className={`${
|
||||
activeTab === ind ? "op-tab-active" : ""
|
||||
} op-tab text-xs md:text-base pb-2 md:pb-0 }`}
|
||||
>
|
||||
<i className={tabData.icon}></i>
|
||||
<span className="ml-1 md:ml-2">{tabData.title}</span>
|
||||
@@ -332,154 +353,272 @@ const Preferences = () => {
|
||||
</div>
|
||||
<div className="flex justify-center md:justify-start mt-3 md:mt-4 break-all">
|
||||
{activeTab === 0 ? (
|
||||
<>
|
||||
<div className="ml-4 mt-1 mb-2 flex flex-col">
|
||||
<div className="mb-[0.75rem]">
|
||||
<label
|
||||
htmlFor="signaturetype"
|
||||
className="text-[14px] mb-[0.7rem] font-medium"
|
||||
>
|
||||
<div className="ml-4 mt-1 mb-2 flex flex-col">
|
||||
<div className="mb-[0.75rem]">
|
||||
<label
|
||||
className="mb-[0.7rem] text-[12px]"
|
||||
htmlFor="signaturetype"
|
||||
>
|
||||
<span className="font-medium text-[14px]">
|
||||
{t("allowed-signature-types")}
|
||||
<a
|
||||
data-tooltip-id="signtypes-tooltip"
|
||||
className="ml-1"
|
||||
>
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id="signtypes-tooltip"
|
||||
className="z-[999]"
|
||||
>
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">
|
||||
{t("allowed-signature-types")}
|
||||
</p>
|
||||
<p>{t("allowed-signature-types-help.p1")}</p>
|
||||
<p className="p-[5px] ml-2">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">Draw: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l1")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Type: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l2")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Upload: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l3")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Default: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l4")}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type?.name === "typed" ? "type" : type?.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||
{t("notify-on-signatures")}
|
||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="nos-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">
|
||||
{t("notify-on-signatures")}
|
||||
</p>
|
||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||
<p>{t("notify-on-signatures-help.note")}</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
</span>
|
||||
<a data-tooltip-id="signtypes-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id="signtypes-tooltip"
|
||||
className="z-[999]"
|
||||
>
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">
|
||||
{t("allowed-signature-types")}
|
||||
</p>
|
||||
<p>{t("allowed-signature-types-help.p1")}</p>
|
||||
<p className="p-[5px] ml-2">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">Draw: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l1")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Type: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l2")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Upload: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l3")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Default: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l4")}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className="ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => (
|
||||
<div
|
||||
className={
|
||||
"flex items-center gap-2 ml-2 mb-1"
|
||||
}
|
||||
key={i}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(true)}
|
||||
checked={isNotifyOnSignatures === true}
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div className="text-[13px] font-medium cursor-default capitalize">
|
||||
{t("yes")}
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type?.name === "typed" ? "type" : type?.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={
|
||||
"flex items-center gap-2 ml-2 mb-1"
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(false)}
|
||||
checked={isNotifyOnSignatures === false}
|
||||
/>
|
||||
<div className="text-[13px] font-medium cursor-default capitalize">
|
||||
{t("no")}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<label className="mb-[0.7rem] text-[12px]">
|
||||
<span className="text-[14px] font-medium">
|
||||
{t("notify-on-signatures")}
|
||||
</span>
|
||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="nos-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">
|
||||
{t("notify-on-signatures")}
|
||||
</p>
|
||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||
<p>{t("notify-on-signatures-help.note")}</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className={"flex items-center gap-2 ml-2 mb-1"}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(true)}
|
||||
checked={isNotifyOnSignatures === true}
|
||||
/>
|
||||
<div className="text-[13px] cursor-default capitalize">
|
||||
{t("yes")}
|
||||
</div>
|
||||
</div>
|
||||
<div className={"flex items-center gap-2 ml-2 mb-1"}>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(false)}
|
||||
checked={isNotifyOnSignatures === false}
|
||||
/>
|
||||
<div className="text-[13px] cursor-default capitalize">
|
||||
{t("no")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<TimezoneSelector
|
||||
timezone={timezone}
|
||||
setTimezone={setTimezone}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={handleSave}
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<TimezoneSelector
|
||||
timezone={timezone}
|
||||
setTimezone={setTimezone}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-[0.75rem] text-[12px]">
|
||||
<label className="block mb-[0.7rem]">
|
||||
<span className="text-[14px] font-medium">
|
||||
{t("send-in-order")}
|
||||
</span>
|
||||
<a
|
||||
data-tooltip-id="sendInOrder-tooltip"
|
||||
className="ml-1"
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id="sendInOrder-tooltip"
|
||||
className="z-[999]"
|
||||
>
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("send-in-order")}</p>
|
||||
<p>{t("send-in-order-help.p1")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">
|
||||
{t("yes")}:{" "}
|
||||
</span>
|
||||
<span>{t("send-in-order-help.p2")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("send-in-order-help.p3")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("send-in-order-help.p4")}</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={true}
|
||||
className="op-radio op-radio-xs"
|
||||
name="SendinOrder"
|
||||
checked={sendinOrder}
|
||||
onChange={handleSendinOrderInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={false}
|
||||
name="SendinOrder"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={!sendinOrder}
|
||||
onChange={handleSendinOrderInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="mb-[0.75rem] text-[12px]">
|
||||
<label className="block mb-[0.7rem]">
|
||||
<span className="text-[14px] font-medium">
|
||||
{t("enable-tour")}
|
||||
</span>
|
||||
<a
|
||||
data-tooltip-id="istourenabled-tooltip"
|
||||
className="ml-1"
|
||||
>
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id="istourenabled-tooltip"
|
||||
className="z-[999]"
|
||||
>
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("enable-tour")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">
|
||||
{t("yes")}:{" "}
|
||||
</span>
|
||||
<span>{t("istourenabled-help.p1")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("istourenabled-help.p2")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>
|
||||
{t("istourenabled-help.p3", {
|
||||
appName: appName
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={true}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsTourEnabled"
|
||||
checked={isTourEnabled}
|
||||
onChange={handleTourInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={false}
|
||||
name="IsTourEnabled"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={!isTourEnabled}
|
||||
onChange={handleTourInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<button
|
||||
className="op-btn op-btn-primary w-[110px]"
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col mx-4 mb-4">
|
||||
<div className="flex flex-col">
|
||||
@@ -496,7 +635,9 @@ const Preferences = () => {
|
||||
{t("subject")}
|
||||
<Tooltip
|
||||
id={"request-sub-tooltip"}
|
||||
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||
message={`${t(
|
||||
"variables-use"
|
||||
)}: {{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
@@ -505,7 +646,9 @@ const Preferences = () => {
|
||||
onChange={(e) =>
|
||||
setRequestSubject(e.target.value)
|
||||
}
|
||||
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
|
||||
placeholder={`{{sender_name}} ${t(
|
||||
"send-to-sign"
|
||||
)} {{document_title}}`}
|
||||
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||
/>
|
||||
</div>
|
||||
@@ -514,7 +657,9 @@ const Preferences = () => {
|
||||
{t("body")}
|
||||
<Tooltip
|
||||
id={"request-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{sender_name}} {{document_title}}`}
|
||||
message={`${t(
|
||||
"variables-use"
|
||||
)}: {{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<EditorToolbar containerId="toolbar1" />
|
||||
@@ -560,7 +705,9 @@ const Preferences = () => {
|
||||
{t("subject")}
|
||||
<Tooltip
|
||||
id={"complete-sub-tooltip"}
|
||||
message={`${t("variables-use")}:{{sender_name}} {{document_title}}`}
|
||||
message={`${t(
|
||||
"variables-use"
|
||||
)}:{{sender_name}} {{document_title}}`}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
@@ -569,7 +716,9 @@ const Preferences = () => {
|
||||
onChange={(e) =>
|
||||
setCompletionSubject(e.target.value)
|
||||
}
|
||||
placeholder={`{{sender_name}} ${t("send-to-sign")} {{document_title}}`}
|
||||
placeholder={`{{sender_name}} ${t(
|
||||
"send-to-sign"
|
||||
)} {{document_title}}`}
|
||||
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||
/>
|
||||
</div>
|
||||
@@ -578,7 +727,9 @@ const Preferences = () => {
|
||||
{t("body")}
|
||||
<Tooltip
|
||||
id={"complete-body-tooltip"}
|
||||
message={`${t("variables-use")}:{{sender_name}} {{document_title}} {{signing_url}}`}
|
||||
message={`${t(
|
||||
"variables-use"
|
||||
)}:{{sender_name}} {{document_title}} {{signing_url}}`}
|
||||
/>
|
||||
</label>
|
||||
<EditorToolbar containerId="toolbar2" />
|
||||
|
||||
@@ -66,8 +66,7 @@ import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
function SignYourSelf() {
|
||||
const { t } = useTranslation();
|
||||
const { docId } = useParams();
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const appName = "OpenSign™";
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const imageRef = useRef(null);
|
||||
@@ -124,7 +123,6 @@ function SignYourSelf() {
|
||||
const [pdfLoad, setPdfLoad] = useState(false);
|
||||
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [extUserId, setExtUserId] = useState("");
|
||||
const [isCompleted, setIsCompleted] = useState(false);
|
||||
const [isCelebration, setIsCelebration] = useState(false);
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
@@ -207,7 +205,6 @@ function SignYourSelf() {
|
||||
|
||||
if (documentData && documentData.length > 0) {
|
||||
setPdfDetails(documentData);
|
||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||
const placeholders =
|
||||
documentData[0]?.Placeholders?.length > 0
|
||||
? documentData[0]?.Placeholders
|
||||
@@ -564,11 +561,7 @@ function SignYourSelf() {
|
||||
let pdfUrl;
|
||||
if (isUploadPdf) {
|
||||
const pdfName = generatePdfName(16);
|
||||
pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64Url,
|
||||
"",
|
||||
);
|
||||
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
||||
}
|
||||
const widgetsType = ["signature", "stamp", "image", "initials"];
|
||||
let updatedXYPosition;
|
||||
@@ -754,10 +747,7 @@ function SignYourSelf() {
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject
|
||||
) {
|
||||
if (tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
@@ -793,7 +783,7 @@ function SignYourSelf() {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
isCustomCompletionMail: isCustomCompletionMail,
|
||||
signature: suffixbase64,
|
||||
signature: suffixbase64
|
||||
};
|
||||
const resSignPdf = await Parse.Cloud.run("signPdf", params);
|
||||
if (resSignPdf) {
|
||||
@@ -1371,13 +1361,10 @@ function SignYourSelf() {
|
||||
{/*render email component to send email after finish signature on document */}
|
||||
<EmailComponent
|
||||
isEmail={isEmail}
|
||||
pdfUrl={pdfUrl}
|
||||
setIsEmail={setIsEmail}
|
||||
pdfDetails={pdfDetails}
|
||||
setSuccessEmail={setSuccessEmail}
|
||||
sender={jsonSender}
|
||||
pdfDetails={pdfDetails}
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
|
||||
@@ -165,7 +165,6 @@ const TemplatePlaceholder = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current, isHeader]);
|
||||
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const user = JSON.parse(
|
||||
@@ -778,11 +777,7 @@ const TemplatePlaceholder = () => {
|
||||
let pdfUrl;
|
||||
if (isUploadPdf) {
|
||||
const pdfName = generatePdfName(16);
|
||||
pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64Url,
|
||||
"",
|
||||
);
|
||||
pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
||||
}
|
||||
if (signersdata?.length > 0) {
|
||||
signersdata.forEach((x) => {
|
||||
@@ -849,11 +844,7 @@ const TemplatePlaceholder = () => {
|
||||
scale
|
||||
);
|
||||
const pdfName = generatePdfName(16);
|
||||
const pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64,
|
||||
"",
|
||||
);
|
||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64, "");
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const buffer = atob(pdfBase64);
|
||||
SaveFileSize(buffer.length, pdfUrl, tenantId);
|
||||
@@ -865,11 +856,7 @@ const TemplatePlaceholder = () => {
|
||||
} else if (pdfBase64Url) {
|
||||
try {
|
||||
const pdfName = generatePdfName(16);
|
||||
const pdfUrl = await convertBase64ToFile(
|
||||
pdfName,
|
||||
pdfBase64Url,
|
||||
"",
|
||||
);
|
||||
const pdfUrl = await convertBase64ToFile(pdfName, pdfBase64Url, "");
|
||||
return pdfUrl;
|
||||
} catch (err) {
|
||||
console.log("error to convertBase64ToFile in placeholder flow", err);
|
||||
@@ -881,6 +868,14 @@ const TemplatePlaceholder = () => {
|
||||
};
|
||||
const handleSaveTemplate = async () => {
|
||||
if (signersdata?.length) {
|
||||
const remindOnceInEvery = parseInt(pdfDetails[0]?.RemindOnceInEvery);
|
||||
const TimeToCompleteDays = parseInt(pdfDetails[0]?.TimeToCompleteDays);
|
||||
const AutomaticReminders = pdfDetails[0]?.AutomaticReminders;
|
||||
const reminderCount = TimeToCompleteDays / remindOnceInEvery;
|
||||
if (AutomaticReminders && reminderCount > 15) {
|
||||
alert(t("only-15-reminder-allowed"));
|
||||
return;
|
||||
}
|
||||
setIsLoading({ isLoad: true, message: t("loading-mssg") });
|
||||
setIsSendAlert(false);
|
||||
let signers = [],
|
||||
@@ -1086,7 +1081,7 @@ const TemplatePlaceholder = () => {
|
||||
});
|
||||
setIsCreateDoc(false);
|
||||
} else {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setHandleError(t(res.id));
|
||||
setIsCreateDoc(false);
|
||||
}
|
||||
};
|
||||
@@ -1461,8 +1456,7 @@ const TemplatePlaceholder = () => {
|
||||
status: defaultdata?.status || "required",
|
||||
hint: defaultdata?.hint || "",
|
||||
defaultValue: defaultdata?.defaultValue || "",
|
||||
validation:
|
||||
{},
|
||||
validation: {},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
@@ -1471,6 +1465,15 @@ const TemplatePlaceholder = () => {
|
||||
"black"
|
||||
}
|
||||
};
|
||||
} else if (["signature"].includes(position.type)) {
|
||||
return {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
name: defaultdata.name,
|
||||
hint: defaultdata?.hint || ""
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...position,
|
||||
@@ -1479,6 +1482,7 @@ const TemplatePlaceholder = () => {
|
||||
name: defaultdata.name,
|
||||
status: defaultdata.status,
|
||||
defaultValue: defaultdata.defaultValue,
|
||||
hint: defaultdata?.hint || "",
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
|
||||
+188
-197
@@ -10,7 +10,6 @@ import AddUser from "../components/AddUser";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
||||
// const actions = [];
|
||||
const UserList = () => {
|
||||
const { t } = useTranslation();
|
||||
const [userList, setUserList] = useState([]);
|
||||
@@ -114,9 +113,8 @@ const UserList = () => {
|
||||
setUserList(_userRes);
|
||||
} catch (err) {
|
||||
console.log("Err in fetch userlist", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
@@ -173,17 +171,15 @@ const UserList = () => {
|
||||
extUser.id = user.objectId;
|
||||
extUser.set("IsDisabled", !IsDisabled);
|
||||
await extUser.save();
|
||||
setIsAlert({
|
||||
type: !IsDisabled === true ? "danger" : "success",
|
||||
msg:
|
||||
!IsDisabled === true ? t("user-deactivated") : t("user-activated")
|
||||
});
|
||||
showAlert(
|
||||
!IsDisabled === true ? "danger" : "success",
|
||||
!IsDisabled === true ? t("user-deactivated") : t("user-activated")
|
||||
);
|
||||
} catch (err) {
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
showAlert("danger", t("something-went-wrong-mssg"));
|
||||
console.log("err in disable team", err);
|
||||
} finally {
|
||||
setIsActLoader({});
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -191,6 +187,11 @@ const UserList = () => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
|
||||
// `showAlert` handle show/hide alert
|
||||
const showAlert = (type, msg) => {
|
||||
setIsAlert({ type, msg });
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={isAdmin ? "Users" : "Page not found"} />
|
||||
@@ -205,197 +206,187 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
!isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>{isAlert.msg}</Alert>
|
||||
)}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
{t("report-name.Users")}{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={t("users-from-teams")} />
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModal("form")}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
{heading?.map((item, index) => (
|
||||
<th key={index} className="px-4 py-2">
|
||||
{t(`report-heading.${item}`)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{userList?.length > 0 && (
|
||||
<>
|
||||
{currentList.map((item, index) => (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
{heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
{startIndex + index + 1}
|
||||
</th>
|
||||
)}
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.Name}{" "}
|
||||
</td>
|
||||
<td className="px-4 py-2 ">
|
||||
{item?.Email || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{item?.Phone || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{item?.UserRole?.split("_").pop() || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item.TeamIds)}
|
||||
</td>
|
||||
{item.UserRole !== "contracts_Admin" && (
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
<label className="cursor-pointer relative block items-center mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all op-toggle-secondary"
|
||||
checked={item?.IsDisabled !== true}
|
||||
onChange={() => handleToggleBtn(item)}
|
||||
/>
|
||||
</label>
|
||||
{isActiveModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("user-status")}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("are-you-sure")}{" "}
|
||||
{item?.IsDisabled
|
||||
? t("activate")
|
||||
: t("deactivate")}{" "}
|
||||
{t("this-user")}?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() =>
|
||||
handleToggleSubmit(item)
|
||||
}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between items-center text-xs font-medium">
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isModal.form}
|
||||
title={formHeader}
|
||||
handleClose={() => handleModal("form")}
|
||||
>
|
||||
<AddUser
|
||||
setIsAlert={setIsAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={() => handleModal("form")}
|
||||
setFormHeader={setFormHeader}
|
||||
/>
|
||||
</ModalUi>
|
||||
{!isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
{t("report-name.Users")}{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={t("users-from-teams")} />
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold">
|
||||
404
|
||||
</h1>
|
||||
<p className="text-[30px] lg:text-[50px]">
|
||||
{t("page-not-found")}
|
||||
</p>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModal("form")}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full overflow-x-auto">
|
||||
<table className="op-table border-collapse w-full mb-[50px]">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
{heading?.map((item, index) => (
|
||||
<th key={index} className="px-4 py-2">
|
||||
{t(`report-heading.${item}`)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
{userList?.length > 0 && (
|
||||
<tbody className="text-[12px]">
|
||||
{currentList.map((item, index) => (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
{heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
{startIndex + index + 1}
|
||||
</th>
|
||||
)}
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.Name}{" "}
|
||||
</td>
|
||||
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
|
||||
<td className="px-4 py-2">{item?.Phone || "-"}</td>
|
||||
<td className="px-4 py-2">
|
||||
{item?.UserRole?.split("_").pop() || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item.TeamIds)}
|
||||
</td>
|
||||
{item.UserRole !== "contracts_Admin" ? (
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
<label className="cursor-pointer relative block items-center mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all op-toggle-secondary"
|
||||
checked={item?.IsDisabled !== true}
|
||||
onChange={() => handleToggleBtn(item)}
|
||||
/>
|
||||
</label>
|
||||
{isActiveModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("user-status")}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("are-you-sure")}{" "}
|
||||
{item?.IsDisabled
|
||||
? t("activate")
|
||||
: t("deactivate")}{" "}
|
||||
{t("this-user")}?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() => handleToggleSubmit(item)}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</td>
|
||||
) : (
|
||||
<td className="px-4 py-2 font-semibold"></td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between items-center text-xs font-medium">
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
<ModalUi
|
||||
isOpen={isModal.form}
|
||||
title={formHeader}
|
||||
handleClose={() => handleModal("form")}
|
||||
>
|
||||
<AddUser
|
||||
showAlert={showAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={() => handleModal("form")}
|
||||
setFormHeader={setFormHeader}
|
||||
/>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold">
|
||||
404
|
||||
</h1>
|
||||
<p className="text-[30px] lg:text-[50px]">
|
||||
{t("page-not-found")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import pad from "../assets/images/pad.svg";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import axios from "axios";
|
||||
import ModalUi from "./ModalUi";
|
||||
import AddSigner from "../components/AddSigner";
|
||||
import {
|
||||
emailRegex,
|
||||
} from "../constant/const";
|
||||
import { emailRegex } from "../constant/const";
|
||||
import Alert from "./Alert";
|
||||
import Tooltip from "./Tooltip";
|
||||
import { RWebShare } from "react-web-share";
|
||||
@@ -38,8 +36,8 @@ import * as XLSX from "xlsx";
|
||||
import EditContactForm from "../components/EditContactForm";
|
||||
|
||||
const ReportTable = (props) => {
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const copyUrlRef = useRef(null);
|
||||
const appName = "OpenSign™";
|
||||
const drivename = appName === "OpenSign™" ? "OpenSign™" : "";
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -156,14 +154,14 @@ const ReportTable = (props) => {
|
||||
const teamtRes = await Parse.Cloud.run("getteams", { active: true });
|
||||
if (teamtRes.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teamtRes));
|
||||
const selected = _teamRes.map(
|
||||
(x) =>
|
||||
x.Name === "All Users" && {
|
||||
label: x.Name,
|
||||
value: x.objectId
|
||||
}
|
||||
);
|
||||
setSelectedTeam(selected);
|
||||
const selected = _teamRes.map(
|
||||
(x) =>
|
||||
x.Name === "All Users" && {
|
||||
label: x.Name,
|
||||
value: x.objectId
|
||||
}
|
||||
);
|
||||
setSelectedTeam(selected);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -224,7 +222,7 @@ const ReportTable = (props) => {
|
||||
const handleURL = async (item, act) => {
|
||||
if (props.ReportName === "Templates") {
|
||||
if (act.hoverLabel === "Edit") {
|
||||
navigate(`/${act.redirectUrl}/${item.objectId}`);
|
||||
navigate(`/${act.redirectUrl}/${item.objectId}`);
|
||||
} else {
|
||||
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
|
||||
try {
|
||||
@@ -410,8 +408,7 @@ const ReportTable = (props) => {
|
||||
setSelectedTeam(formatedList);
|
||||
}
|
||||
setIsShareWith({ [item.objectId]: true });
|
||||
}
|
||||
else if (act.action === "duplicate") {
|
||||
} else if (act.action === "duplicate") {
|
||||
setIsModal({ [`duplicate_${item.objectId}`]: true });
|
||||
} else if (act.action === "rename") {
|
||||
setIsModal({ [`rename_${item.objectId}`]: true });
|
||||
@@ -495,8 +492,7 @@ const ReportTable = (props) => {
|
||||
setActLoader({});
|
||||
}
|
||||
};
|
||||
const handleClose = (
|
||||
) => {
|
||||
const handleClose = () => {
|
||||
setIsRevoke({});
|
||||
setIsDeleteModal({});
|
||||
setReason("");
|
||||
@@ -527,6 +523,9 @@ const ReportTable = (props) => {
|
||||
|
||||
const copytoclipboard = (share) => {
|
||||
copytoData(share.url);
|
||||
if (copyUrlRef.current) {
|
||||
copyUrlRef.current.textContent = share.url; // Update text safely
|
||||
}
|
||||
setCopied({ ...copied, [share.email]: true });
|
||||
};
|
||||
//function to handle revoke/decline docment
|
||||
@@ -652,11 +651,7 @@ const ReportTable = (props) => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal({ [item.objectId]: true });
|
||||
} else {
|
||||
const signedUrl = await getSignedUrl(
|
||||
url,
|
||||
docId,
|
||||
templateId
|
||||
);
|
||||
const signedUrl = await getSignedUrl(url, docId, templateId);
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
}
|
||||
setActLoader({});
|
||||
@@ -683,10 +678,8 @@ const ReportTable = (props) => {
|
||||
const signPdf = `${window.location.origin}/login/${encodeBase64}`;
|
||||
const variables = {
|
||||
document_title: doc.Name,
|
||||
sender_name:
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_name: doc.ExtUserPtr.Name,
|
||||
sender_mail: doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email,
|
||||
@@ -714,10 +707,8 @@ const ReportTable = (props) => {
|
||||
const signPdf = `${window.location.origin}/login/${encodeBase64}`;
|
||||
const variables = {
|
||||
document_title: doc.Name,
|
||||
sender_name:
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_name: doc.ExtUserPtr.Name,
|
||||
sender_mail: doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email || "",
|
||||
@@ -757,10 +748,8 @@ const ReportTable = (props) => {
|
||||
const signPdf = `${window.location.origin}/login/${encodeBase64}`;
|
||||
const variables = {
|
||||
document_title: doc.Name,
|
||||
sender_name:
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_name: doc.ExtUserPtr.Name,
|
||||
sender_mail: doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: user?.signerPtr?.Name || "",
|
||||
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
|
||||
@@ -790,14 +779,11 @@ const ReportTable = (props) => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
let params = {
|
||||
replyto:
|
||||
doc?.ExtUserPtr?.Email ||
|
||||
"",
|
||||
replyto: doc?.ExtUserPtr?.Email || "",
|
||||
extUserId: doc?.ExtUserPtr?.objectId,
|
||||
recipient: userDetails?.Email,
|
||||
subject: mail.subject,
|
||||
from:
|
||||
doc?.ExtUserPtr?.Email,
|
||||
from: doc?.ExtUserPtr?.Email,
|
||||
html: mail.body
|
||||
};
|
||||
try {
|
||||
@@ -806,8 +792,7 @@ const ReportTable = (props) => {
|
||||
setIsAlert(true);
|
||||
setAlertMsg({ type: "success", message: t("mail-sent-alert") });
|
||||
setIsResendMail({});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setIsAlert(true);
|
||||
setAlertMsg({
|
||||
type: "danger",
|
||||
@@ -1741,11 +1726,7 @@ const ReportTable = (props) => {
|
||||
<i className={act.btnIcon}></i>
|
||||
{act.btnLabel && (
|
||||
<span className="uppercase font-medium">
|
||||
{
|
||||
`${t(
|
||||
`btnLabel.${act.btnLabel}`
|
||||
)}`
|
||||
}
|
||||
{`${t(`btnLabel.${act.btnLabel}`)}`}
|
||||
</span>
|
||||
)}
|
||||
{/* template report */}
|
||||
@@ -1825,7 +1806,9 @@ const ReportTable = (props) => {
|
||||
title={t(`btnLabel.${act.hoverLabel}`)}
|
||||
className={
|
||||
act.action !== "option"
|
||||
? `${act?.btnColor || ""} op-btn op-btn-sm mr-1`
|
||||
? `${
|
||||
act?.btnColor || ""
|
||||
} op-btn op-btn-sm mr-1`
|
||||
: "text-base-content focus:outline-none text-lg mr-2 relative"
|
||||
}
|
||||
>
|
||||
@@ -1912,28 +1895,26 @@ const ReportTable = (props) => {
|
||||
{isShareWith[item.objectId] && (
|
||||
<div className="op-modal op-modal-open">
|
||||
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
{t("share-with")}
|
||||
</h3>
|
||||
<div
|
||||
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
|
||||
onClick={() => setIsShareWith({})}
|
||||
>
|
||||
✕
|
||||
</div>
|
||||
<div className="px-2 mt-3 w-full h-full">
|
||||
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
|
||||
{selectedTeam?.[0]?.label}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleShareWith(e, item)
|
||||
}
|
||||
className="op-btn op-btn-primary ml-[10px] my-3"
|
||||
>
|
||||
{t("submit")}
|
||||
</button>
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
{t("share-with")}
|
||||
</h3>
|
||||
<div
|
||||
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2 z-40"
|
||||
onClick={() => setIsShareWith({})}
|
||||
>
|
||||
✕
|
||||
</div>
|
||||
<div className="px-2 mt-3 w-full h-full">
|
||||
<div className="op-input op-input-bordered op-input-sm w-full h-full text-[13px] break-all">
|
||||
{selectedTeam?.[0]?.label}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => handleShareWith(e, item)}
|
||||
className="op-btn op-btn-primary ml-[10px] my-3"
|
||||
>
|
||||
{t("submit")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -2026,9 +2007,7 @@ const ReportTable = (props) => {
|
||||
{isBulkSend[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={
|
||||
t("quick-send")
|
||||
}
|
||||
title={t("quick-send")}
|
||||
handleClose={() => setIsBulkSend({})}
|
||||
>
|
||||
{isLoader[item.objectId] ? (
|
||||
@@ -2088,6 +2067,11 @@ const ReportTable = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<p
|
||||
id="copyUrl"
|
||||
ref={copyUrlRef}
|
||||
className="hidden"
|
||||
></p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:20
|
||||
FROM node:22.14.0
|
||||
|
||||
# install java
|
||||
RUN wget https://download.oracle.com/java/23/latest/jdk-23_linux-x64_bin.deb \
|
||||
&& dpkg -i jdk-23_linux-x64_bin.deb
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
Binary file not shown.
@@ -5,8 +5,7 @@ import { PDFDocument } from 'pdf-lib';
|
||||
dotenv.config();
|
||||
|
||||
export const cloudServerUrl = 'http://localhost:8080/app';
|
||||
export const appName =
|
||||
'OpenSign™';
|
||||
export const appName = 'OpenSign™';
|
||||
export function customAPIurl() {
|
||||
const url = new URL(cloudServerUrl);
|
||||
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
|
||||
@@ -139,131 +138,6 @@ export const updateMailCount = async (extUserId, plan, monthchange) => {
|
||||
}
|
||||
};
|
||||
|
||||
export function formatWidgetOptions(type, options) {
|
||||
const colorsArr = ['red', 'black', 'blue', 'yellow'];
|
||||
const fontSizes = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||
const status = options?.required === true ? 'required' : 'optional' || 'required';
|
||||
const defaultValue = options?.default || '';
|
||||
const values = options?.values || [];
|
||||
const color = options?.color ? options.color : 'black';
|
||||
const fontColor = colorsArr.includes(color) ? color : 'black';
|
||||
const size = options?.fontsize ? parseInt(options.fontsize) : 12;
|
||||
const fontSize = fontSizes.includes(size) ? size : 12;
|
||||
switch (type) {
|
||||
case 'signature':
|
||||
return { name: 'signature', status: 'required' };
|
||||
case 'stamp':
|
||||
return { status: status, name: 'stamp' };
|
||||
case 'initials':
|
||||
return { status: status, name: options.name || 'initials' };
|
||||
case 'image':
|
||||
return { status: status, name: options.name || 'image' };
|
||||
case 'email':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'email',
|
||||
validation: { type: 'email' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'name':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'name',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'job title':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'job title',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'company':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'company',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'date': {
|
||||
let today = new Date();
|
||||
let dd = String(today.getDate()).padStart(2, '0');
|
||||
let mm = String(today.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||
let yyyy = today.getFullYear();
|
||||
today = dd + '-' + mm + '-' + yyyy;
|
||||
let dateFormat = options?.format;
|
||||
dateFormat = dateFormat.replace(/m/g, 'M');
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'date',
|
||||
response: defaultValue || today,
|
||||
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'textbox':
|
||||
return {
|
||||
status: status,
|
||||
name: 'textbox',
|
||||
defaultValue: defaultValue,
|
||||
hint: options.hint,
|
||||
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
isReadOnly: options?.readonly || false,
|
||||
};
|
||||
case 'checkbox': {
|
||||
const arr = options?.values;
|
||||
let selectedvalues = [];
|
||||
for (const obj of options.selectedvalues) {
|
||||
const index = arr.indexOf(obj);
|
||||
selectedvalues.push(index);
|
||||
}
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'checkbox',
|
||||
values: values,
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
validation: {
|
||||
minRequiredCount: options?.validation?.minselections || 0,
|
||||
maxRequiredCount: options?.validation?.maxselections || 0,
|
||||
},
|
||||
defaultValue: selectedvalues || [],
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'radio button': {
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'radio',
|
||||
values: values,
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'dropdown':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'dropdown',
|
||||
values: values,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
isReadOnly: options?.readonly || false,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function sanitizeFileName(fileName) {
|
||||
// Remove spaces and invalid characters
|
||||
const file = fileName.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||
@@ -276,7 +150,6 @@ export const smtpsecure = process.env.SMTP_PORT && process.env.SMTP_PORT !== '46
|
||||
export const smtpenable =
|
||||
process.env.SMTP_ENABLE && process.env.SMTP_ENABLE.toLowerCase() === 'true' ? true : false;
|
||||
|
||||
|
||||
// `generateId` is used to unique Id for fileAdapter
|
||||
export function generateId(length) {
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
@@ -356,11 +229,9 @@ export const flattenPdf = async pdfFile => {
|
||||
export const mailTemplate = param => {
|
||||
const themeColor = '#47a3ad';
|
||||
const subject = `${param.senderName} has requested you to sign "${param.title}"`;
|
||||
const logo =
|
||||
`<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`;
|
||||
const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`;
|
||||
|
||||
const opurl =
|
||||
` <a href='www.opensignlabs.com' target=_blank>here</a>`;
|
||||
const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
|
||||
|
||||
const body =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background:white;padding-bottom:20px'><div style='padding:10px'>" +
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import PDF from './parsefunction/pdf/PDF.js';
|
||||
import sendmailv3 from './parsefunction/sendMailv3.js';
|
||||
import usersignup from './parsefunction/usersignup.js';
|
||||
@@ -51,7 +50,7 @@ import generateCertificatebydocId from './parsefunction/generateCertificatebydoc
|
||||
import fileUpload from './parsefunction/fileUpload.js';
|
||||
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
|
||||
import editContact from './parsefunction/editContact.js';
|
||||
|
||||
import forwardDoc from './parsefunction/ForwardDoc.js';
|
||||
|
||||
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||
@@ -112,3 +111,4 @@ Parse.Cloud.define('generatecertificate', generateCertificatebydocId);
|
||||
Parse.Cloud.define('fileupload', fileUpload);
|
||||
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
|
||||
Parse.Cloud.define('editcontact', editContact);
|
||||
Parse.Cloud.define('forwarddoc', forwardDoc);
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
async function DocumentBeforesave(request) {
|
||||
if (!request.original) {
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
|
||||
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||
const reminderCount = TimeToCompleteDays / RemindOnceInEvery;
|
||||
if (AutoReminder && reminderCount > 15) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'only 15 reminder allowed');
|
||||
}
|
||||
}
|
||||
try {
|
||||
// below code is used to update document when user sent document or self signed
|
||||
const document = request.object;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import axios from 'axios';
|
||||
import { appName, cloudServerUrl } from '../../Utils.js';
|
||||
|
||||
export default async function forwardDoc(request) {
|
||||
try {
|
||||
if (!request.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized.');
|
||||
}
|
||||
const { docId, recipients } = request.params;
|
||||
const isReceipents = recipients?.length > 0 && recipients?.length <= 10;
|
||||
if (docId && isReceipents) {
|
||||
const userPtr = { __type: 'Pointer', className: '_User', objectId: request.user.id };
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery
|
||||
.equalTo('objectId', docId)
|
||||
.equalTo('CreatedBy', userPtr)
|
||||
.notEqualTo('IsArchive', true)
|
||||
.notEqualTo('IsDeclined', true)
|
||||
.include('Signers')
|
||||
.include('ExtUserPtr')
|
||||
.include('Placeholders.signerPtr')
|
||||
.include('ExtUserPtr.TenantId');
|
||||
const docRes = await docQuery.first({ useMasterKey: true });
|
||||
if (!docRes) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||
}
|
||||
const _docRes = docRes?.toJSON();
|
||||
const docName = _docRes.Name;
|
||||
const fileAdapterId = _docRes?.FileAdapterId || '';
|
||||
const extUserId = _docRes?.ExtUserPtr?.objectId;
|
||||
const from = _docRes?.ExtUserPtr?.Email;
|
||||
const replyTo = _docRes?.ExtUserPtr?.Email;
|
||||
const senderName = _docRes?.ExtUserPtr?.Name;
|
||||
|
||||
try {
|
||||
let mailRes;
|
||||
for (let i = 0; i < recipients.length; i++) {
|
||||
const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>`;
|
||||
const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`;
|
||||
const themeColor = '#47a3ad';
|
||||
|
||||
let params = {
|
||||
extUserId: extUserId,
|
||||
pdfName: docName,
|
||||
url: _docRes?.SignedUrl || '',
|
||||
recipient: recipients[i],
|
||||
subject: `${senderName} has signed the doc - ${docName}`,
|
||||
replyto: replyTo || '',
|
||||
from: from,
|
||||
html:
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'><div>` +
|
||||
`${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` +
|
||||
`<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${docName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` +
|
||||
`</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` +
|
||||
`If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`,
|
||||
};
|
||||
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||
},
|
||||
});
|
||||
}
|
||||
return mailRes.data?.result;
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.error ||
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
'Something went wrong.';
|
||||
throw new Parse.Error(400, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'please provide parameters.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in forwardDoc', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export default async function Newsletter(request) {
|
||||
const email = request.params.email;
|
||||
const domain = request.params.domain;
|
||||
try {
|
||||
const envAppId = process.env.REACT_APP_APPID || 'legadranaxn';
|
||||
const envAppId = process.env.REACT_APP_APPID || 'opensign';
|
||||
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId };
|
||||
const envProdServer = process.env.REACT_APP_SERVERURL || 'https://app.opensignlabs.com/api/app';
|
||||
const newsletter = await axios.post(
|
||||
|
||||
@@ -11,7 +11,7 @@ async function getDocument(docId) {
|
||||
query.include('Placeholders');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
const _res = res.toJSON();
|
||||
const _res = res?.toJSON();
|
||||
return _res?.ExtUserPtr?.objectId;
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
async function TemplateBeforeSave(request) {
|
||||
if (!request.original) {
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
|
||||
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||
const reminderCount = TimeToCompleteDays / RemindOnceInEvery;
|
||||
if (AutoReminder && reminderCount > 15) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'only 15 reminder allowed');
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!request.original) {
|
||||
// below code is used to update template when user sent template or self signed
|
||||
|
||||
@@ -12,21 +12,18 @@ const eSignName = 'OpenSign';
|
||||
const eSigncontact = 'hello@opensignlabs.com';
|
||||
|
||||
// `uploadFile` is used to create url in from pdfFile
|
||||
async function uploadFile(
|
||||
pdfName,
|
||||
filepath,
|
||||
) {
|
||||
async function uploadFile(pdfName, filepath) {
|
||||
try {
|
||||
const filedata = fs.readFileSync(filepath);
|
||||
let fileUrl;
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
const fileRes = getSecureUrl(file.url());
|
||||
fileUrl = fileRes.url;
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
const fileRes = getSecureUrl(file.url());
|
||||
fileUrl = fileRes.url;
|
||||
return { imageUrl: fileUrl };
|
||||
} catch (err) {
|
||||
console.log('Err ', err);
|
||||
// `unlinkCertificate` is used to remove exported signed pdf file from exports folder
|
||||
// `unlinkCertificate` is used to remove exported certificate file from exports folder
|
||||
unlinkCertificate(filepath);
|
||||
}
|
||||
}
|
||||
@@ -86,10 +83,7 @@ export default async function generateCertificatebydocId(req) {
|
||||
|
||||
//below is used to save signed certificate in exports folder
|
||||
fs.writeFileSync(certificatePath, signedCertificate);
|
||||
const file = await uploadFile(
|
||||
'certificate.pdf',
|
||||
certificatePath,
|
||||
);
|
||||
const file = await uploadFile('certificate.pdf', certificatePath);
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = doc.objectId;
|
||||
updateDoc.set('CertificateUrl', file.imageUrl);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { exec } from 'child_process';
|
||||
|
||||
export default function DigitalSign(pdf, pfx, details) {
|
||||
const signcmd = `java -jar PDFDigitalSigner.jar "${pdf}" "${pfx.name}" "${pfx.passphrase}" "${details.name}" "${details.location}" "${details.reason}"`;
|
||||
// const signcmd = `java -jar PDFDigitalSigner.jar "${pdf}" keystore.pfx opensign "${details.name}" "${details.location}" "${details.reason}"`;
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(signcmd, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(`Error: ${error.message}`);
|
||||
}
|
||||
if (stderr) {
|
||||
reject(`stderr: ${stderr}`);
|
||||
}
|
||||
// Resolve the promise with the output
|
||||
resolve(stdout.trim());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function retryAsync(fn, args = [], retries = 3, delay = 2000) {
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const response = await fn(...args); // Try executing the async function
|
||||
if (response) return { response, attempt }; // Stop retrying if response is received
|
||||
} catch (error) {
|
||||
if (attempt < retries) {
|
||||
console.log(`Attempt ${attempt} failed. Retrying in ${delay / 1000} seconds...\n`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay)); // Wait before retrying
|
||||
} else {
|
||||
return { error, attempt };
|
||||
// throw new Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
import axios from 'axios';
|
||||
import { SignPdf } from '@signpdf/signpdf';
|
||||
import { P12Signer } from '@signpdf/signer-p12';
|
||||
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import {
|
||||
cloudServerUrl,
|
||||
@@ -12,24 +9,29 @@ import {
|
||||
appName,
|
||||
} from '../../../Utils.js';
|
||||
import GenerateCertificate from './GenerateCertificate.js';
|
||||
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
|
||||
import { Placeholder } from './Placeholder.js';
|
||||
import { SignPdf } from '@signpdf/signpdf';
|
||||
import { P12Signer } from '@signpdf/signer-p12';
|
||||
|
||||
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
const eSignName = 'OpenSign';
|
||||
const eSigncontact = 'hello@opensignlabs.com';
|
||||
const logo =
|
||||
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
|
||||
const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`;
|
||||
|
||||
// `updateDoc` is used to create url in from pdfFile
|
||||
async function uploadFile(
|
||||
pdfName,
|
||||
filepath,
|
||||
) {
|
||||
async function uploadFile(pdfName, filepath) {
|
||||
try {
|
||||
const filedata = fs.readFileSync(filepath);
|
||||
let fileUrl;
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
const fileRes = getSecureUrl(file.url());
|
||||
fileUrl = fileRes.url;
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
const fileRes = getSecureUrl(file.url());
|
||||
fileUrl = fileRes.url;
|
||||
|
||||
return { imageUrl: fileUrl };
|
||||
} catch (err) {
|
||||
@@ -104,14 +106,10 @@ async function sendNotifyMail(doc, signUser, mailProvider) {
|
||||
const signerName = signUser.Name;
|
||||
const signerEmail = signUser.Email;
|
||||
const viewDocUrl = `${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`;
|
||||
const logo =
|
||||
`<div><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px' /></div>`;
|
||||
const opurl =
|
||||
` <a href=www.opensignlabs.com target=_blank>here</a>`;
|
||||
const subject = `Document "${pdfName}" has been signed by ${signerName}`;
|
||||
const body =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'>" +
|
||||
`${logo}<div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed by ${signerName}</p>` +
|
||||
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed by ${signerName}</p>` +
|
||||
`</div><div style='padding:20px;font-family:system-ui;font-size:14px'><p>Dear ${creatorName},</p><p>${pdfName} has been signed by ${signerName} "${signerEmail}" successfully</p>` +
|
||||
`<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, ` +
|
||||
`please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`;
|
||||
@@ -154,17 +152,13 @@ async function sendCompletedMail(obj) {
|
||||
signersMail = sender.Email;
|
||||
}
|
||||
const recipient = signersMail;
|
||||
const logo =
|
||||
`<div><img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/></div>`;
|
||||
const opurl =
|
||||
` <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>`;
|
||||
let subject = `Document "${pdfName}" has been signed by all parties`;
|
||||
let body =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'>" +
|
||||
`${logo}<div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed successfully</p></div><div>` +
|
||||
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed successfully</p></div><div>` +
|
||||
`<p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document <b>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` +
|
||||
`</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` +
|
||||
`If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}`;
|
||||
`If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`;
|
||||
|
||||
if (obj?.isCustomMail) {
|
||||
const tenant = sender?.TenantId;
|
||||
@@ -202,8 +196,7 @@ async function sendCompletedMail(obj) {
|
||||
|
||||
const variables = {
|
||||
document_title: pdfName,
|
||||
sender_name:
|
||||
sender.Name,
|
||||
sender_name: sender.Name,
|
||||
sender_mail: doc?.SenderMail || sender.Email,
|
||||
sender_phone: sender?.Phone || '',
|
||||
receiver_name: sender.Name,
|
||||
@@ -220,45 +213,40 @@ async function sendCompletedMail(obj) {
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
url: url,
|
||||
from:
|
||||
appName,
|
||||
replyto:
|
||||
doc?.ExtUserPtr?.Email ||
|
||||
'',
|
||||
from: appName,
|
||||
replyto: doc?.ExtUserPtr?.Email || '',
|
||||
recipient: recipient,
|
||||
subject: subject,
|
||||
pdfName: pdfName,
|
||||
html: body,
|
||||
mailProvider: obj.mailProvider,
|
||||
bcc: Bcc,
|
||||
certificatePath: `./exports/certificate_${doc.objectId}.pdf`,
|
||||
certificatePath: `./exports/signed_certificate_${doc.objectId}.pdf`,
|
||||
filename: obj?.filename,
|
||||
};
|
||||
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
// console.log('res', res.data.result);
|
||||
if (res.data?.result?.status !== 'success') {
|
||||
fs.unlinkSync(`./exports/signed_certificate_${doc.objectId}.pdf`);
|
||||
}
|
||||
} catch (err) {
|
||||
fs.unlinkSync(`./exports/signed_certificate_${doc.objectId}.pdf`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// `sendMailsaveCertifcate` is used send completion mail and update complete status of document
|
||||
async function sendMailsaveCertifcate(
|
||||
doc,
|
||||
P12Buffer,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
filename,
|
||||
) {
|
||||
async function sendMailsaveCertifcate(doc, pfx, isCustomMail, mailProvider, filename) {
|
||||
const certificate = await GenerateCertificate(doc);
|
||||
const certificatePdf = await PDFDocument.load(certificate);
|
||||
let passphrase = process.env.PASS_PHRASE;
|
||||
if (doc?.ExtUserPtr?.TenantId?.PfxFile?.password) {
|
||||
passphrase = doc?.ExtUserPtr?.TenantId?.PfxFile?.password;
|
||||
}
|
||||
const p12 = new P12Signer(P12Buffer, { passphrase: passphrase || null });
|
||||
const P12Buffer = fs.readFileSync(pfx.name);
|
||||
const p12 = new P12Signer(P12Buffer, { passphrase: pfx.passphrase || null });
|
||||
// `pdflibAddPlaceholder` is used to add code of only digitial sign in certificate
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: certificatePdf,
|
||||
@@ -274,13 +262,11 @@ async function sendMailsaveCertifcate(
|
||||
const certificateOBJ = new SignPdf();
|
||||
// `signedCertificate` is used to sign certificate digitally
|
||||
const signedCertificate = await certificateOBJ.sign(CertificateBuffer, p12);
|
||||
const certificatePath = `./exports/certificate_${doc.objectId}.pdf`;
|
||||
const certificatePath = `./exports/signed_certificate_${doc.objectId}.pdf`;
|
||||
|
||||
//below is used to save signed certificate in exports folder
|
||||
fs.writeFileSync(certificatePath, signedCertificate);
|
||||
const file = await uploadFile(
|
||||
'certificate.pdf',
|
||||
certificatePath,
|
||||
);
|
||||
const file = await uploadFile('certificate.pdf', certificatePath);
|
||||
const body = { CertificateUrl: file.imageUrl };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
|
||||
headers: {
|
||||
@@ -296,6 +282,7 @@ async function sendMailsaveCertifcate(
|
||||
sendCompletedMail({ isCustomMail, doc, mailProvider, filename });
|
||||
}
|
||||
saveFileUsage(CertificateBuffer.length, file.imageUrl, doc?.CreatedBy?.objectId);
|
||||
fs.unlinkSync(pfx.name);
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -304,9 +291,11 @@ async function sendMailsaveCertifcate(
|
||||
* @returns if success {status, data} else {status, message}
|
||||
*/
|
||||
async function PDF(req) {
|
||||
const docId = req.params.docId;
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const pfxname = `keystore_${randomNumber}.pfx`;
|
||||
try {
|
||||
const userIP = req.headers['x-real-ip']; // client IPaddress
|
||||
const docId = req.params.docId;
|
||||
const reqUserId = req.params.userId;
|
||||
const isCustomMail = req.params.isCustomCompletionMail || false;
|
||||
const mailProvider = req.params.mailProvider || '';
|
||||
@@ -354,9 +343,9 @@ async function PDF(req) {
|
||||
pfxFile = _resDoc?.ExtUserPtr?.TenantId?.PfxFile?.base64;
|
||||
passphrase = _resDoc?.ExtUserPtr?.TenantId?.PfxFile?.password;
|
||||
}
|
||||
// const P12Buffer = fs.readFileSync();
|
||||
const pfx = { name: pfxname, passphrase: passphrase };
|
||||
const P12Buffer = Buffer.from(pfxFile, 'base64');
|
||||
const p12Cert = new P12Signer(P12Buffer, { passphrase: passphrase || null });
|
||||
fs.writeFileSync(pfxname, P12Buffer);
|
||||
const UserPtr = { __type: 'Pointer', className: className, objectId: signUser.objectId };
|
||||
const obj = { UserPtr: UserPtr, SignedUrl: '', Activity: 'Signed', ipAddress: userIP };
|
||||
let updateAuditTrail;
|
||||
@@ -375,12 +364,12 @@ async function PDF(req) {
|
||||
} else {
|
||||
isCompleted = true;
|
||||
}
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
// below regex is used to replace all word with "_" except A to Z, a to z, numbers
|
||||
const docName = _resDoc?.Name?.replace(/[^a-zA-Z0-9._-]/g, '_')?.toLowerCase();
|
||||
const filename = docName?.length > 100 ? docName?.slice(0, 100) : docName;
|
||||
const name = `signed_${filename}_${randomNumber}.pdf`;
|
||||
const filePath = `./exports/${name}`;
|
||||
const name = `${filename}_${randomNumber}.pdf`;
|
||||
let filePath = `./exports/${name}`;
|
||||
let signedFilePath = `./exports/signed_${name}`;
|
||||
let pdfSize = PdfBuffer.length;
|
||||
if (isCompleted) {
|
||||
const signersName = _resDoc.Signers?.map(x => x.Name + ' <' + x.Email + '>');
|
||||
@@ -394,6 +383,8 @@ async function PDF(req) {
|
||||
form.updateFieldAppearances();
|
||||
// Flattens the form, converting all form fields into non-editable, static content
|
||||
form.flatten();
|
||||
const p12Cert = new P12Signer(P12Buffer, { passphrase: passphrase || null });
|
||||
signedFilePath = `./exports/signed_${name}`;
|
||||
Placeholder({
|
||||
pdfDoc: pdfDoc,
|
||||
reason: `Digitally signed by ${eSignName} for ${reason}`,
|
||||
@@ -410,19 +401,18 @@ async function PDF(req) {
|
||||
const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
|
||||
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
fs.writeFileSync(filePath, signedDocs);
|
||||
fs.writeFileSync(signedFilePath, signedDocs);
|
||||
pdfSize = signedDocs.length;
|
||||
console.log(`✅ PDF digitally signed created: ${signedFilePath} \n`);
|
||||
} else {
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
fs.writeFileSync(filePath, PdfBuffer);
|
||||
fs.writeFileSync(signedFilePath, PdfBuffer);
|
||||
pdfSize = PdfBuffer.length;
|
||||
console.log(`New Signed PDF created called: ${signedFilePath}`);
|
||||
}
|
||||
|
||||
// `uploadFile` is used to upload pdf to aws s3 and get it's url
|
||||
const data = await uploadFile(
|
||||
name,
|
||||
filePath,
|
||||
);
|
||||
const data = await uploadFile(`signed_${name}`, signedFilePath);
|
||||
|
||||
if (data && data.imageUrl) {
|
||||
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
|
||||
@@ -439,17 +429,13 @@ async function PDF(req) {
|
||||
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
|
||||
if (updatedDoc && updatedDoc.isCompleted) {
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl };
|
||||
sendMailsaveCertifcate(
|
||||
doc,
|
||||
P12Buffer,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
name,
|
||||
);
|
||||
sendMailsaveCertifcate(doc, pfx, isCustomMail, mailProvider, `signed_${name}`);
|
||||
} else {
|
||||
fs.unlinkSync(pfxname);
|
||||
}
|
||||
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(`New Signed PDF created called: ${filePath}`);
|
||||
fs.unlinkSync(signedFilePath);
|
||||
// console.log(`New Signed PDF created called: ${filePath}`);
|
||||
if (updatedDoc.message === 'success') {
|
||||
return { status: 'success', data: data.imageUrl };
|
||||
} else {
|
||||
@@ -465,6 +451,15 @@ async function PDF(req) {
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in signpdf', err);
|
||||
const body = { DebugginLog: err?.message };
|
||||
try {
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + docId, body, {
|
||||
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('err in saving debugginglog', err);
|
||||
}
|
||||
fs.unlinkSync(pfxname);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ export default async function updatePreferences(request) {
|
||||
newOrg.set('SignatureType', SignatureType);
|
||||
}
|
||||
}
|
||||
if (request.params.SendinOrder !== undefined) {
|
||||
newOrg.set('SendinOrder', request.params.SendinOrder);
|
||||
}
|
||||
if (request.params.IsTourEnabled !== undefined) {
|
||||
newOrg.set('IsTourEnabled', request.params.IsTourEnabled);
|
||||
}
|
||||
const updateUserRes = await newOrg.save(null, { useMasterKey: true });
|
||||
if (updateUserRes) {
|
||||
const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes));
|
||||
|
||||
Binary file not shown.
Generated
+4
-4
@@ -47,7 +47,7 @@
|
||||
"mongodb-runner": "^5.7.1",
|
||||
"nodemon": "^3.1.9",
|
||||
"nyc": "^17.1.0",
|
||||
"prettier": "^3.5.3"
|
||||
"prettier": "^3.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
@@ -16933,9 +16933,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz",
|
||||
"integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"mongodb-runner": "^5.7.1",
|
||||
"nodemon": "^3.1.9",
|
||||
"nyc": "^17.1.0",
|
||||
"prettier": "^3.5.3"
|
||||
"prettier": "^3.5.0"
|
||||
},
|
||||
"overrides": {
|
||||
"ws": "$ws",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user