| Line 763... |
Line 763... |
| 763 |
}
|
763 |
}
|
| 764 |
}
|
764 |
}
|
| 765 |
}*/
|
765 |
}*/
|
| 766 |
|
766 |
|
| 767 |
$(document).on('click', "#confirmSign", function () {
|
767 |
$(document).on('click', "#confirmSign", function () {
|
| - |
|
768 |
let btn = $(this);
|
| 768 |
let loiId = $(this).val();
|
769 |
let loiId = btn.val();
|
| - |
|
770 |
if (btn.prop('disabled')) return false;
|
| 769 |
let otp = prompt("Enter received OTP here");
|
771 |
let otp = prompt("Enter received OTP here");
|
| - |
|
772 |
if (otp === undefined || otp === null) {
|
| - |
|
773 |
return false;
|
| - |
|
774 |
}
|
| - |
|
775 |
otp = otp.trim();
|
| - |
|
776 |
if (!otp) {
|
| - |
|
777 |
alert("Please enter the OTP received by the partner.");
|
| - |
|
778 |
return false;
|
| - |
|
779 |
}
|
| 770 |
let validateOtpUrl = `${context}/validateLoiOtp?loiId=${loiId}&provideOtp=${otp}`;
|
780 |
let validateOtpUrl = `${context}/validateLoiOtp?loiId=${loiId}&provideOtp=${encodeURIComponent(otp)}`;
|
| - |
|
781 |
|
| - |
|
782 |
// Once the OTP is accepted the LOI is only half signed: the server has the OTP but not the
|
| - |
|
783 |
// signed PDF, and the partner mail + onboarding only happen in /saveLoiDoc. Every failure from
|
| - |
|
784 |
// here on must say that plainly - a silent failure here left LOIs stuck with no mail.
|
| - |
|
785 |
function signNotSaved(reason) {
|
| - |
|
786 |
btn.prop('disabled', false);
|
| - |
|
787 |
alert("OTP was verified, but the signed LOI was NOT saved.\nReason: " + reason
|
| - |
|
788 |
+ "\n\nNothing has been emailed to the partner. Click Send OTP to get a new OTP, then Confirm Sign again."
|
| - |
|
789 |
+ " Keep this page open until you see the \"LOI has been signed successfully\" message.");
|
| - |
|
790 |
}
|
| 771 |
|
791 |
|
| 772 |
if (otp !== undefined && otp !== null) {
|
792 |
btn.prop('disabled', true);
|
| - |
|
793 |
$.ajax({
|
| - |
|
794 |
url: validateOtpUrl,
|
| - |
|
795 |
type: 'PUT',
|
| - |
|
796 |
cache: false,
|
| - |
|
797 |
suppressGlobalError: true,
|
| 773 |
doPutAjaxRequestHandler(validateOtpUrl, function (response) {
|
798 |
success: function (response) {
|
| 774 |
console.log('OTP validation response:', response);
|
799 |
console.log('OTP validation response:', response);
|
| 775 |
|
800 |
|
| 776 |
if (response && response.response && response.response.success) {
|
801 |
if (response && response.response && response.response.success) {
|
| - |
|
802 |
// html2pdf comes from cdnjs; if that script was blocked, the old code threw a
|
| - |
|
803 |
// ReferenceError right here and the operator saw nothing at all.
|
| - |
|
804 |
if (typeof html2pdf !== 'function') {
|
| - |
|
805 |
signNotSaved("the PDF library did not load in this browser (check internet / try Chrome, then reload the page)");
|
| - |
|
806 |
return;
|
| - |
|
807 |
}
|
| - |
|
808 |
let tempContainer = null;
|
| - |
|
809 |
try {
|
| 777 |
const docHash = (response.response.documentHash || "");
|
810 |
const docHash = (response.response.documentHash || "");
|
| 778 |
const ip = response.response.ipAddress || "";
|
811 |
const ip = response.response.ipAddress || "";
|
| 779 |
const createdAt = response.response.createdAt || new Date().toLocaleString();
|
812 |
const createdAt = response.response.createdAt || new Date().toLocaleString();
|
| 780 |
const watermarkText =
|
813 |
const watermarkText =
|
| 781 |
`OTP VERIFIED\n` +
|
814 |
`OTP VERIFIED\n` +
|
| 782 |
`DOCUMENT HASH:\n ${docHash}\n` +
|
815 |
`DOCUMENT HASH:\n ${docHash}\n` +
|
| 783 |
`IP ADDRESS: ${ip}\n` +
|
816 |
`IP ADDRESS: ${ip}\n` +
|
| 784 |
`CREATED ON: ${createdAt}`;
|
817 |
`CREATED ON: ${createdAt}`;
|
| 785 |
const originalElement = document.getElementById('loiPDF_content');
|
818 |
const originalElement = document.getElementById('loiPDF_content');
|
| 786 |
const clonedElement = originalElement.cloneNode(true);
|
819 |
const clonedElement = originalElement.cloneNode(true);
|
| 787 |
const existingStyles = clonedElement.querySelectorAll('style');
|
820 |
const existingStyles = clonedElement.querySelectorAll('style');
|
| 788 |
existingStyles.forEach(style => style.remove());
|
821 |
existingStyles.forEach(style => style.remove());
|
| 789 |
$(clonedElement).find('.pending-badge, .pending-indicator').remove();
|
822 |
$(clonedElement).find('.pending-badge, .pending-indicator').remove();
|
| 790 |
$(clonedElement).find('.signature-section').each(function () {
|
823 |
$(clonedElement).find('.signature-section').each(function () {
|
| 791 |
this.style.setProperty('position', 'relative', 'important');
|
824 |
this.style.setProperty('position', 'relative', 'important');
|
| 792 |
this.style.setProperty('padding', '20px', 'important');
|
825 |
this.style.setProperty('padding', '20px', 'important');
|
| 793 |
});
|
- |
|
| 794 |
|
- |
|
| 795 |
const signatureSections = clonedElement.querySelectorAll('.signature-section');
|
- |
|
| 796 |
signatureSections.forEach(section => {
|
- |
|
| 797 |
const oldWatermarks = section.querySelectorAll('.watermark-overlay');
|
- |
|
| 798 |
oldWatermarks.forEach(w => w.remove());
|
- |
|
| 799 |
|
- |
|
| 800 |
const watermarkDiv = document.createElement('div');
|
- |
|
| 801 |
watermarkDiv.className = 'watermark-overlay';
|
- |
|
| 802 |
watermarkDiv.textContent = watermarkText;
|
- |
|
| 803 |
watermarkDiv.style.position = 'absolute';
|
- |
|
| 804 |
watermarkDiv.style.fontSize = '7px';
|
- |
|
| 805 |
watermarkDiv.style.top = '30px';
|
- |
|
| 806 |
watermarkDiv.style.left = '1%';
|
- |
|
| 807 |
watermarkDiv.style.color = 'rgb(4,97,225)';
|
- |
|
| 808 |
watermarkDiv.style.fontWeight = 'bold';
|
- |
|
| 809 |
watermarkDiv.style.fontFamily = 'Arial,sans-serif';
|
- |
|
| 810 |
watermarkDiv.style.whiteSpace = 'pre-line';
|
- |
|
| 811 |
watermarkDiv.style.textAlign = 'center';
|
- |
|
| 812 |
watermarkDiv.style.pointerEvents = 'none';
|
- |
|
| 813 |
watermarkDiv.style.width = '90%';
|
- |
|
| 814 |
watermarkDiv.style.lineHeight = '1.7';
|
- |
|
| 815 |
watermarkDiv.style.zIndex = '1';
|
- |
|
| 816 |
watermarkDiv.style.userSelect = 'none';
|
- |
|
| 817 |
|
- |
|
| 818 |
section.style.position = 'relative';
|
- |
|
| 819 |
section.appendChild(watermarkDiv);
|
- |
|
| 820 |
});
|
- |
|
| 821 |
|
- |
|
| 822 |
const tempContainer = document.createElement('div');
|
- |
|
| 823 |
tempContainer.style.position = 'absolute';
|
- |
|
| 824 |
tempContainer.style.left = '-9999px';
|
- |
|
| 825 |
tempContainer.style.width = '800px';
|
- |
|
| 826 |
tempContainer.appendChild(clonedElement);
|
- |
|
| 827 |
document.body.appendChild(tempContainer);
|
- |
|
| 828 |
|
- |
|
| 829 |
const opt = {
|
- |
|
| 830 |
margin: [0.5, 0.5, 0.5, 0.5],
|
- |
|
| 831 |
filename: `LOI_${loiId}_${new Date().getTime()}.pdf`,
|
- |
|
| 832 |
image: {
|
- |
|
| 833 |
type: 'jpeg',
|
- |
|
| 834 |
quality: 0.98
|
- |
|
| 835 |
},
|
- |
|
| 836 |
html2canvas: {
|
- |
|
| 837 |
scale: 1.2,
|
- |
|
| 838 |
logging: false,
|
- |
|
| 839 |
useCORS: true,
|
- |
|
| 840 |
allowTaint: false,
|
- |
|
| 841 |
backgroundColor: '#ffffff'
|
- |
|
| 842 |
},
|
826 |
});
|
| 843 |
jsPDF: {
|
- |
|
| 844 |
unit: 'in',
|
- |
|
| 845 |
format: 'letter',
|
- |
|
| 846 |
orientation: 'portrait'
|
- |
|
| 847 |
}
|
- |
|
| 848 |
};
|
- |
|
| 849 |
|
827 |
|
| - |
|
828 |
const signatureSections = clonedElement.querySelectorAll('.signature-section');
|
| - |
|
829 |
signatureSections.forEach(section => {
|
| - |
|
830 |
const oldWatermarks = section.querySelectorAll('.watermark-overlay');
|
| - |
|
831 |
oldWatermarks.forEach(w => w.remove());
|
| - |
|
832 |
|
| - |
|
833 |
const watermarkDiv = document.createElement('div');
|
| - |
|
834 |
watermarkDiv.className = 'watermark-overlay';
|
| - |
|
835 |
watermarkDiv.textContent = watermarkText;
|
| - |
|
836 |
watermarkDiv.style.position = 'absolute';
|
| - |
|
837 |
watermarkDiv.style.fontSize = '7px';
|
| - |
|
838 |
watermarkDiv.style.top = '30px';
|
| - |
|
839 |
watermarkDiv.style.left = '1%';
|
| - |
|
840 |
watermarkDiv.style.color = 'rgb(4,97,225)';
|
| - |
|
841 |
watermarkDiv.style.fontWeight = 'bold';
|
| - |
|
842 |
watermarkDiv.style.fontFamily = 'Arial,sans-serif';
|
| - |
|
843 |
watermarkDiv.style.whiteSpace = 'pre-line';
|
| - |
|
844 |
watermarkDiv.style.textAlign = 'center';
|
| - |
|
845 |
watermarkDiv.style.pointerEvents = 'none';
|
| - |
|
846 |
watermarkDiv.style.width = '90%';
|
| - |
|
847 |
watermarkDiv.style.lineHeight = '1.7';
|
| - |
|
848 |
watermarkDiv.style.zIndex = '1';
|
| - |
|
849 |
watermarkDiv.style.userSelect = 'none';
|
| - |
|
850 |
|
| - |
|
851 |
section.style.position = 'relative';
|
| - |
|
852 |
section.appendChild(watermarkDiv);
|
| - |
|
853 |
});
|
| - |
|
854 |
|
| - |
|
855 |
tempContainer = document.createElement('div');
|
| - |
|
856 |
tempContainer.style.position = 'absolute';
|
| - |
|
857 |
tempContainer.style.left = '-9999px';
|
| - |
|
858 |
tempContainer.style.width = '800px';
|
| - |
|
859 |
tempContainer.appendChild(clonedElement);
|
| - |
|
860 |
document.body.appendChild(tempContainer);
|
| - |
|
861 |
|
| - |
|
862 |
const opt = {
|
| - |
|
863 |
margin: [0.5, 0.5, 0.5, 0.5],
|
| - |
|
864 |
filename: `LOI_${loiId}_${new Date().getTime()}.pdf`,
|
| - |
|
865 |
image: {
|
| - |
|
866 |
type: 'jpeg',
|
| - |
|
867 |
quality: 0.98
|
| - |
|
868 |
},
|
| - |
|
869 |
html2canvas: {
|
| - |
|
870 |
scale: 1.2,
|
| - |
|
871 |
logging: false,
|
| - |
|
872 |
useCORS: true,
|
| - |
|
873 |
allowTaint: false,
|
| - |
|
874 |
backgroundColor: '#ffffff'
|
| - |
|
875 |
},
|
| - |
|
876 |
jsPDF: {
|
| - |
|
877 |
unit: 'in',
|
| - |
|
878 |
format: 'letter',
|
| - |
|
879 |
orientation: 'portrait'
|
| - |
|
880 |
}
|
| - |
|
881 |
};
|
| - |
|
882 |
|
| 850 |
html2pdf().from(clonedElement).set(opt).outputPdf("arraybuffer")
|
883 |
html2pdf().from(clonedElement).set(opt).outputPdf("arraybuffer")
|
| 851 |
.then(function (pdf) {
|
884 |
.then(function (pdf) {
|
| 852 |
document.body.removeChild(tempContainer);
|
885 |
document.body.removeChild(tempContainer);
|
| 853 |
|
886 |
|
| 854 |
const file = new Blob([pdf], {type: 'application/pdf'});
|
887 |
const file = new File([pdf], `LOI_${loiId}_signed.pdf`, {type: 'application/pdf'});
|
| 855 |
|
888 |
|
| 856 |
uploadDocument(file, function (documentId) {
|
889 |
uploadDocument(file, function (documentId) {
|
| 857 |
IdempotencyKey = uuidv4();
|
890 |
IdempotencyKey = uuidv4();
|
| 858 |
if (documentId > 0) {
|
891 |
saveLoiDoc(loiId, documentId, signNotSaved);
|
| 859 |
saveLoiDoc(loiId, documentId);
|
892 |
}, function (message) {
|
| 860 |
alert("Document signed and saved successfully!");
|
893 |
signNotSaved("upload failed - " + message);
|
| 861 |
} else {
|
894 |
});
|
| - |
|
895 |
})
|
| - |
|
896 |
.catch(function (error) {
|
| - |
|
897 |
console.error('PDF generation failed:', error);
|
| - |
|
898 |
if (tempContainer && tempContainer.parentNode) {
|
| 862 |
alert("Something went wrong, please try again to confirm OTP");
|
899 |
tempContainer.parentNode.removeChild(tempContainer);
|
| 863 |
}
|
900 |
}
|
| - |
|
901 |
signNotSaved("the PDF could not be generated in this browser (" + (error && error.message ? error.message : error) + ")");
|
| 864 |
});
|
902 |
});
|
| 865 |
})
|
903 |
} catch (error) {
|
| 866 |
.catch(function (error) {
|
904 |
console.error('Signed LOI build failed:', error);
|
| 867 |
console.error('PDF generation failed:', error);
|
905 |
if (tempContainer && tempContainer.parentNode) {
|
| 868 |
document.body.removeChild(tempContainer);
|
906 |
tempContainer.parentNode.removeChild(tempContainer);
|
| - |
|
907 |
}
|
| 869 |
alert("PDF generation failed. Please try again.");
|
908 |
signNotSaved("the signed PDF could not be prepared (" + (error && error.message ? error.message : error) + ")");
|
| 870 |
});
|
909 |
}
|
| 871 |
} else {
|
910 |
} else {
|
| 872 |
console.error('OTP validation failed:', response);
|
911 |
console.error('OTP validation failed:', response);
|
| - |
|
912 |
btn.prop('disabled', false);
|
| 873 |
alert("OTP validation failed: " + (response.response?.message || "Please try again."));
|
913 |
alert("OTP validation failed: " + (response && response.response && response.response.message ? response.response.message : "Please try again."));
|
| 874 |
}
|
914 |
}
|
| - |
|
915 |
},
|
| 875 |
}, function (error) {
|
916 |
error: function (xhr) {
|
| 876 |
console.error('AJAX request failed:', error);
|
917 |
btn.prop('disabled', false);
|
| 877 |
alert("Request failed. Please check your connection and try again.");
|
918 |
alert("OTP not accepted: " + uploadErrorMessage(xhr));
|
| 878 |
});
|
919 |
}
|
| 879 |
} else {
|
- |
|
| 880 |
return false;
|
- |
|
| 881 |
}
|
920 |
});
|
| 882 |
});
|
921 |
});
|
| 883 |
|
922 |
|
| 884 |
|
923 |
|
| 885 |
function saveLoiDoc(loiId, documentId) {
|
924 |
function saveLoiDoc(loiId, documentId, onFailure) {
|
| 886 |
let saveLoiDocUrl = `${context}/saveLoiDoc?loiId=` + loiId + `&loiDocId=` + documentId
|
925 |
let saveLoiDocUrl = `${context}/saveLoiDoc?loiId=` + loiId + `&loiDocId=` + documentId;
|
| - |
|
926 |
$.ajax({
|
| 887 |
doPostAjaxRequestHandler(saveLoiDocUrl, function (response) {
|
927 |
url: saveLoiDocUrl,
|
| - |
|
928 |
type: 'POST',
|
| - |
|
929 |
cache: false,
|
| - |
|
930 |
suppressGlobalError: true,
|
| 888 |
if (response) {
|
931 |
success: function (response) {
|
| - |
|
932 |
// The "response" view renders the bare boolean, so a failure arrives as the string "false".
|
| - |
|
933 |
if (response === true || String(response).trim() === 'true') {
|
| 889 |
alert("LOI has been signed successfully and sent to the registered email. Please check your email.");
|
934 |
alert("LOI has been signed successfully and sent to the registered email. Please check your email.");
|
| 890 |
pendingLoiForm("main-content");
|
935 |
pendingLoiForm("main-content");
|
| 891 |
} else {
|
936 |
} else {
|
| 892 |
alert("Something went wrong , Please try again to generate Loi and Validate otp");
|
937 |
onFailure("the server did not accept the signed document");
|
| - |
|
938 |
}
|
| - |
|
939 |
},
|
| - |
|
940 |
error: function (xhr) {
|
| - |
|
941 |
onFailure("saving failed - " + uploadErrorMessage(xhr));
|
| 893 |
}
|
942 |
}
|
| 894 |
});
|
943 |
});
|
| 895 |
}
|
944 |
}
|
| 896 |
|
945 |
|
| 897 |
|
946 |
|