diff --git a/apps/OpenSign/src/components/BulkSendUi.js b/apps/OpenSign/src/components/BulkSendUi.js
new file mode 100644
index 000000000..039f43c39
--- /dev/null
+++ b/apps/OpenSign/src/components/BulkSendUi.js
@@ -0,0 +1,215 @@
+import React, { useState, useEffect, useRef } from "react";
+import axios from "axios";
+import SuggestionInput from "./shared/fields/SuggestionInput";
+
+const BulkSendUi = (props) => {
+ const [forms, setForms] = useState([]);
+ const [formId, setFormId] = useState(2);
+ const formRef = useRef(null);
+ const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
+ const [isSubmit, setIsSubmit] = useState(false);
+ useEffect(() => {
+ if (scrollOnNextUpdate && formRef.current) {
+ formRef.current.scrollIntoView({
+ behavior: "smooth",
+ block: "end",
+ inline: "nearest"
+ });
+ setScrollOnNextUpdate(false);
+ }
+ }, [forms, scrollOnNextUpdate]);
+
+ useEffect(() => {
+ (() => {
+ if (props?.Placeholders?.length > 0) {
+ let users = [];
+ props?.Placeholders?.forEach((element) => {
+ if (!element.signerObjId) {
+ users = [
+ ...users,
+ {
+ fieldId: element.Id,
+ email: "",
+ label: element.Role,
+ signer: {}
+ }
+ ];
+ }
+ });
+ setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
+ }
+ })();
+ // eslint-disable-next-line
+ }, []);
+ const handleInputChange = (index, signer, fieldIndex) => {
+ console.log("index", index);
+ console.log("signer", signer);
+ console.log("fieldIndex", fieldIndex);
+
+ const newForms = [...forms];
+ newForms[index].fields[fieldIndex].email = signer?.Email
+ ? signer?.Email
+ : signer || "";
+ newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
+ console.log("newForms[index] ", newForms[index]);
+ setForms(newForms);
+ };
+
+ const handleAddForm = (e) => {
+ e.preventDefault();
+ if (props?.Placeholders.length > 0) {
+ let newForm = [];
+ props?.Placeholders?.forEach((element) => {
+ if (!element.signerObjId) {
+ newForm = [
+ ...newForm,
+ {
+ fieldId: element.Id,
+ email: "",
+ label: element.Role,
+ signer: {}
+ }
+ ];
+ }
+ });
+ setForms([...forms, { Id: formId, fields: newForm }]);
+ }
+ setFormId(formId + 1);
+ setScrollOnNextUpdate(true);
+ };
+
+ const handleRemoveForm = (index) => {
+ const updatedForms = forms.filter((_, i) => i !== index);
+ setForms(updatedForms);
+ };
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsSubmit(true);
+
+ // Create a copy of Placeholders array from props.item
+ let Placeholders = [...props.item.Placeholders];
+ // Initialize an empty array to store updated documents
+ let Documents = [];
+
+ // Loop through each form
+ forms.forEach((form) => {
+ // Map through the copied Placeholders array to update email values
+ const updatedPlaceholders = Placeholders.map((placeholder) => {
+ // Find the field in the current form that matches the placeholder Id
+ const field = form.fields.find(
+ (element) => parseInt(element.fieldId) === placeholder.Id
+ );
+ // If a matching field is found, update the email value in the placeholder
+ const signer = field?.signer?.objectId ? field.signer : {};
+ console.log("signer ", signer);
+ if (field) {
+ return {
+ ...placeholder,
+ email: field.email,
+ signerObjId: field?.signer?.objectId || "",
+ signerPtr: signer
+ };
+ }
+ // If no matching field is found, keep the placeholder as is
+ return placeholder;
+ });
+
+ // Push a new document object with updated Placeholders into the Documents array
+ Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
+ });
+ console.log("Documents ", Documents);
+ // await batchQuery(Documents);
+ };
+
+ const batchQuery = async (Documents) => {
+ const serverUrl = localStorage.getItem("baseUrl");
+ const functionsUrl = `${serverUrl}functions/batchdocuments`;
+ const headers = {
+ "Content-Type": "application/json",
+ "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
+ sessionToken: localStorage.getItem("accesstoken")
+ };
+ const params = {
+ Documents: JSON.stringify(Documents)
+ };
+ try {
+ const res = await axios.post(functionsUrl, params, { headers: headers });
+ // console.log("res ", res);
+ if (res.data && res.data.result) {
+ props.handleClose("success", Documents?.length);
+ }
+ } catch (err) {
+ console.log("Err ", err);
+ props.handleClose("error", 0);
+ } finally {
+ setIsSubmit(false);
+ }
+ };
+
+ return (
+ <>
+ {isSubmit && (
+
+ )}
+
+ >
+ );
+};
+
+export default BulkSendUi;
diff --git a/apps/OpenSign/src/components/shared/fields/SuggestionInput.js b/apps/OpenSign/src/components/shared/fields/SuggestionInput.js
new file mode 100644
index 000000000..37f2c0b12
--- /dev/null
+++ b/apps/OpenSign/src/components/shared/fields/SuggestionInput.js
@@ -0,0 +1,92 @@
+import React, { useState, useEffect, useRef } from "react";
+import { findContact } from "../../../constant/Utils";
+const SuggestionInput = (props) => {
+ const [inputValue, setInputValue] = useState(props?.value || "");
+ const [suggestions, setSuggestions] = useState([]);
+ const [showSuggestions, setShowSuggestions] = useState(false);
+ const ref = useRef(null);
+
+ useEffect(() => {
+ document.addEventListener("mousedown", Clickout);
+ return () => {
+ document.removeEventListener("mousedown", Clickout);
+ };
+ }, []);
+
+ const Clickout = (event) => {
+ if (ref.current && !ref.current.contains(event.target)) {
+ setShowSuggestions(false);
+ }
+ };
+ // create debounce to avoid unnecessay api calls
+ useEffect(() => {
+ let timer;
+ if (inputValue) {
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(() => {
+ (async () => {
+ const res = await findContact(inputValue);
+ if (res?.length > 0) {
+ setSuggestions(res);
+ setShowSuggestions(true);
+ } else {
+ setSuggestions(res);
+ setShowSuggestions(false);
+ }
+ })();
+ }, 1000);
+ }
+ return () => clearTimeout(timer);
+ }, [inputValue]);
+
+ const handleInputChange = async (e) => {
+ const value = e.target.value;
+ setInputValue(value);
+ if (props.onChange) {
+ props.onChange(value);
+ }
+
+ if (value.trim() === "") {
+ setSuggestions([]);
+ setShowSuggestions(false);
+ return;
+ }
+ };
+ const handleSuggestionClick = (suggestion) => {
+ setInputValue(suggestion.Email);
+ setSuggestions([]);
+ setShowSuggestions(false);
+ if (props.onChange) {
+ props.onChange(suggestion);
+ }
+ };
+ return (
+
+
+ {showSuggestions && (
+
+ {suggestions.map((suggestion, index) => (
+ handleSuggestionClick(suggestion)}
+ >
+ {suggestion.Email}
+
+ ))}
+
+ )}
+
+ );
+};
+export default SuggestionInput;
diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js
index 93cefe04c..47833399c 100644
--- a/apps/OpenSign/src/constant/Utils.js
+++ b/apps/OpenSign/src/constant/Utils.js
@@ -2030,3 +2030,24 @@ export const handleSendOTP = async (email) => {
alert(error.message);
}
};
+
+export async function findContact(value) {
+ try {
+ const currentUser = Parse.User.current();
+ const contactbook = new Parse.Query("contracts_Contactbook");
+ contactbook.equalTo(
+ "CreatedBy",
+ Parse.User.createWithoutData(currentUser.id)
+ );
+ contactbook.notEqualTo("IsDeleted", true);
+ contactbook.matches("Email", new RegExp(value, "i"));
+
+ const contactRes = await contactbook.find();
+ if (contactRes) {
+ const res = JSON.parse(JSON.stringify(contactRes));
+ return res;
+ }
+ } catch (error) {
+ console.error("Error fetching suggestions:", error);
+ }
+}
diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js
index 8bc528add..f16efca1c 100644
--- a/apps/OpenSign/src/json/ReportJson.js
+++ b/apps/OpenSign/src/json/ReportJson.js
@@ -360,6 +360,14 @@ export default function reportJson(id) {
redirectUrl: "template",
action: "redirect"
},
+ {
+ btnId: "1631",
+ btnLabel: "Quick send",
+ hoverLabel: "Quick send",
+ btnIcon: "fa-solid fa-envelope",
+ redirectUrl: "",
+ action: "bulksend"
+ },
{
btnId: "1834",
btnLabel: "Delete",
diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js
index 1e82297e5..21b8baf9f 100644
--- a/apps/OpenSign/src/primitives/GetReportDisplay.js
+++ b/apps/OpenSign/src/primitives/GetReportDisplay.js
@@ -18,6 +18,7 @@ import EditorToolbar, {
} from "../components/pdf/EditorToolbar";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
+import BulkSendUi from "../components/BulkSendUi";
const ReportTable = (props) => {
const navigate = useNavigate();
@@ -39,6 +40,11 @@ const ReportTable = (props) => {
const [mail, setMail] = useState({ subject: "", body: "" });
const [userDetails, setUserDetails] = useState({});
const [isNextStep, setIsNextStep] = useState({});
+ const [isBulkSend, setIsBulkSend] = useState({});
+ const [templateDeatils, setTemplateDetails] = useState({});
+ const [placeholders, setPlaceholders] = useState([]);
+ const [isErr, setIsErr] = useState(false);
+
const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props;
// For loop is used to calculate page numbers visible below table
@@ -211,6 +217,7 @@ const ReportTable = (props) => {
};
const handleActionBtn = (act, item) => {
+ console.log("item", item);
if (act.action === "redirect") {
handleURL(item, act);
} else if (act.action === "delete") {
@@ -223,6 +230,8 @@ const ReportTable = (props) => {
setIsOption({ [item.objectId]: !isOption[item.objectId] });
} else if (act.action === "resend") {
setIsResendMail({ [item.objectId]: true });
+ } else if (act.action === "bulksend") {
+ handleBulkSend(item);
}
};
// Get current list
@@ -605,6 +614,30 @@ const ReportTable = (props) => {
);
};
+ const handleQuickSendClose = (status, count) => {
+ setIsBulkSend({});
+ setIsAlert(true);
+ if (status === "success") {
+ if (count > 1) {
+ setAlertMsg(count + " Documents sent successfully!");
+ } else {
+ setAlertMsg(count + " Document sent successfully!");
+ }
+ } else {
+ setIsAlert(true);
+ setIsErr(true);
+ }
+ };
+
+ const handleBulkSend = (template) => {
+ if (template?.Placeholders?.length > 0) {
+ setPlaceholders(template?.Placeholders);
+ setTemplateDetails(template);
+ setIsBulkSend({ [template.objectId]: true });
+ } else {
+ setIsDocErr(true);
+ }
+ };
return (
{Object.keys(actLoader)?.length > 0 && (
@@ -845,6 +878,19 @@ const ReportTable = (props) => {
)}
+ {isBulkSend[`${item.objectId}`] && (
+ setIsBulkSend({})}
+ >
+
+
+ )}
{isShare[item.objectId] && (
Digital Signature Request
" +
+ document.ExtUserPtr.Name +
+ ' has requested you to review and sign ' +
+ document.Name +
+ " .
Sender " +
+ sender +
+ " Organization " +
+ orgName +
+ " Expires on " +
+ localExpireDate +
+ "
This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
+ sender +
+ ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here .