702 lines
21 KiB
React
702 lines
21 KiB
React
|
|
import { useState, useRef } from 'react';
|
|||
|
|
import { Emailsend } from '../../../../Features/PurchaseOrder/PurchaseOrder';
|
|||
|
|
import {
|
|||
|
|
blobToBase64,
|
|||
|
|
encryptObject,
|
|||
|
|
pdfDiv,
|
|||
|
|
} from '../../../../Services/Others';
|
|||
|
|
import { PrintStyleFunction } from '../../../paymentpdfPage/PrintStyleFunction';
|
|||
|
|
import { PDFDocument } from 'pdf-lib';
|
|||
|
|
import { PublicQrCodepost } from '../../../../Features/ConfigMasterPage/ConfigMasterPage';
|
|||
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|||
|
|
|
|||
|
|
const MainHomeUrl = import.meta.env.ENV_MAIN_BASE_URL;
|
|||
|
|
|
|||
|
|
// Create a secure blob URL creator that works in all environments
|
|||
|
|
const createSecureBlobUrl = (blob) => {
|
|||
|
|
try {
|
|||
|
|
return URL.createObjectURL(blob);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn(
|
|||
|
|
'Standard blob URL creation failed, using alternative approach'
|
|||
|
|
);
|
|||
|
|
return window.URL.createObjectURL(blob);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Revoke blob URL safely
|
|||
|
|
const revokeSecureBlobUrl = (url) => {
|
|||
|
|
if (url && url.startsWith('blob:')) {
|
|||
|
|
try {
|
|||
|
|
URL.revokeObjectURL(url);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn('Failed to revoke blob URL:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Merge multiple pdf blobs into one
|
|||
|
|
const mergePdfBlobs = async (blobs) => {
|
|||
|
|
const mergedPdf = await PDFDocument.create();
|
|||
|
|
|
|||
|
|
for (const blob of blobs) {
|
|||
|
|
const pdfBytes = await blob.arrayBuffer();
|
|||
|
|
const pdf = await PDFDocument.load(pdfBytes);
|
|||
|
|
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
|||
|
|
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const mergedBytes = await mergedPdf.save();
|
|||
|
|
return new Blob([mergedBytes], { type: 'application/pdf' });
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const MobileMultiPdfPrint = async ({
|
|||
|
|
printerTemplateStyle = 'Style 13',
|
|||
|
|
printDatas = {},
|
|||
|
|
PrintOrderDetails = [],
|
|||
|
|
setPrintOrderDetails = () => {},
|
|||
|
|
PrintComing = '',
|
|||
|
|
emailId = '',
|
|||
|
|
dispatch,
|
|||
|
|
UserId = '',
|
|||
|
|
onProgress = () => {},
|
|||
|
|
}) => {
|
|||
|
|
try {
|
|||
|
|
const style = await PrintStyleFunction(printerTemplateStyle, 'Sales');
|
|||
|
|
|
|||
|
|
const stylesMap = {
|
|||
|
|
'Style 1': 'PrintStyle1',
|
|||
|
|
'Style 2': 'PrintStyle2',
|
|||
|
|
'Style 3': 'PrintStyle3',
|
|||
|
|
'Style 4': 'PrintStyle4',
|
|||
|
|
'Style 5': 'PrintStyle5',
|
|||
|
|
'Style 6': 'PrintStyle6',
|
|||
|
|
'Style 7': 'PrintStyle7',
|
|||
|
|
'Style 8': 'PrintStyle8',
|
|||
|
|
'Style 9': 'PrintStyle9',
|
|||
|
|
'Style 10': 'PrintStyle10',
|
|||
|
|
'Style 11': 'PrintStyle11',
|
|||
|
|
'Style 12': 'PrintStyle12',
|
|||
|
|
'Style 13': 'RePrint',
|
|||
|
|
A4: 'PrintStyleA4',
|
|||
|
|
A5: 'PrintStyleA5',
|
|||
|
|
A4Standard: 'A4Standard',
|
|||
|
|
TaxInvoice: 'TaxInvoice',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const selectedStyle = stylesMap[printerTemplateStyle] || 'RePrint';
|
|||
|
|
const Printsize = printDatas?.PageSize || 'A4';
|
|||
|
|
|
|||
|
|
// Create or clear the container
|
|||
|
|
let container = document.getElementById('print-merged');
|
|||
|
|
if (!container) {
|
|||
|
|
container = document.createElement('div');
|
|||
|
|
container.id = 'print-merged';
|
|||
|
|
container.style.display = 'none';
|
|||
|
|
document.body.appendChild(container);
|
|||
|
|
} else {
|
|||
|
|
container.innerHTML = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Process in batches for large files
|
|||
|
|
const BATCH_SIZE = 5;
|
|||
|
|
const totalOrders = PrintOrderDetails.length;
|
|||
|
|
let processedOrders = 0;
|
|||
|
|
let allBlobs = [];
|
|||
|
|
|
|||
|
|
for (let i = 0; i < totalOrders; i += BATCH_SIZE) {
|
|||
|
|
const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
|
|||
|
|
|
|||
|
|
// Clear container for each batch
|
|||
|
|
container.innerHTML = '';
|
|||
|
|
|
|||
|
|
// Process each order in the current batch
|
|||
|
|
batch.forEach((_, index) => {
|
|||
|
|
const globalIndex = i + index;
|
|||
|
|
const element = document.getElementById(
|
|||
|
|
`${selectedStyle}-${globalIndex}`
|
|||
|
|
);
|
|||
|
|
if (element) {
|
|||
|
|
const cloned = element.cloneNode(true);
|
|||
|
|
|
|||
|
|
// Add page break between orders (except the last one)
|
|||
|
|
if (globalIndex < totalOrders - 1) {
|
|||
|
|
const pageBreak = document.createElement('div');
|
|||
|
|
pageBreak.style.pageBreakAfter = 'always';
|
|||
|
|
container.appendChild(cloned);
|
|||
|
|
container.appendChild(pageBreak);
|
|||
|
|
} else {
|
|||
|
|
container.appendChild(cloned);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.warn(
|
|||
|
|
'⚠️ Element not found:',
|
|||
|
|
`${selectedStyle}-${globalIndex}`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Generate PDF for this batch
|
|||
|
|
try {
|
|||
|
|
const pdfBlob = await pdfDiv('print-merged', style);
|
|||
|
|
allBlobs.push(pdfBlob);
|
|||
|
|
|
|||
|
|
processedOrders += batch.length;
|
|||
|
|
const progress = Math.round((processedOrders / totalOrders) * 100);
|
|||
|
|
onProgress(progress);
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error generating PDF batch:', error);
|
|||
|
|
throw new Error(
|
|||
|
|
`Failed to generate PDF for batch starting at index ${i}`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ✅ Merge all PDF blobs into one
|
|||
|
|
const combinedPdfBlob = await mergePdfBlobs(allBlobs);
|
|||
|
|
const base64Pdf = await blobToBase64(combinedPdfBlob);
|
|||
|
|
|
|||
|
|
// Handle different print actions
|
|||
|
|
if (PrintComing === 'download') {
|
|||
|
|
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
|||
|
|
const link = document.createElement('a');
|
|||
|
|
link.href = blobUrl;
|
|||
|
|
link.download = 'Receipts.pdf';
|
|||
|
|
document.body.appendChild(link);
|
|||
|
|
link.click();
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.body.removeChild(link);
|
|||
|
|
revokeSecureBlobUrl(blobUrl);
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// if (PrintComing === "whatsapp") {
|
|||
|
|
// const orderIds = PrintOrderDetails.map((row) => row?.OrderId).filter(Boolean);
|
|||
|
|
// const documentUrl = `${MainHomeUrl}viewbill?code=${orderIds}`;
|
|||
|
|
// const waUrl =
|
|||
|
|
// "https://web.whatsapp.com/send?text=" +
|
|||
|
|
// encodeURIComponent("Here are your receipts 👉 " + documentUrl);
|
|||
|
|
// window.open(waUrl, "_blank");
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
if (PrintComing === 'whatsapp') {
|
|||
|
|
const orderIdsArray = PrintOrderDetails.map((row) => row?.OrderId).filter(
|
|||
|
|
Boolean
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (orderIdsArray.length === 0) {
|
|||
|
|
console.warn('⚠️ No order IDs found for WhatsApp sharing.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2️⃣ Encrypt order IDs
|
|||
|
|
const encrypted = encryptObject(orderIdsArray);
|
|||
|
|
|
|||
|
|
// 3️⃣ Prepare payload for backend
|
|||
|
|
const postData = {
|
|||
|
|
Url: encrypted,
|
|||
|
|
CreatedBy: UserId, // Make sure UserId is passed correctly to your function
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 4️⃣ Call backend to get a ReferenceNo
|
|||
|
|
const res = await dispatch(PublicQrCodepost(postData)).unwrap();
|
|||
|
|
console.log('Backend response:', res);
|
|||
|
|
|
|||
|
|
// 5️⃣ Extract ReferenceNo (adjust based on your backend response structure)
|
|||
|
|
// Example: it might be res.data.ReferenceNo or res.ReferenceNo
|
|||
|
|
const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
|
|||
|
|
|
|||
|
|
if (!urlData) {
|
|||
|
|
console.error('❌ No ReferenceNo returned from backend');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6️⃣ Build the shareable URL
|
|||
|
|
const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
|
|||
|
|
|
|||
|
|
// 7️⃣ Open WhatsApp Web with URL
|
|||
|
|
const waUrl =
|
|||
|
|
'https://web.whatsapp.com/send?text=' +
|
|||
|
|
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
|||
|
|
|
|||
|
|
window.open(waUrl, '_blank');
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('❌ WhatsApp sharing failed:', err);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (PrintComing === 'email') {
|
|||
|
|
try {
|
|||
|
|
const payload = {
|
|||
|
|
fileBlob: combinedPdfBlob,
|
|||
|
|
toEmail: emailId,
|
|||
|
|
type: 'Sales',
|
|||
|
|
messageTemplatesList: [],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = await dispatch(Emailsend(payload))?.unwrap();
|
|||
|
|
console.log('✅ Email sent response:', res?.data || res);
|
|||
|
|
return res;
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('❌ Email error:', err);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!['download', 'whatsapp', 'email'].includes(PrintComing)) {
|
|||
|
|
const blobUrl = createSecureBlobUrl(combinedPdfBlob);
|
|||
|
|
|
|||
|
|
const newWindow = window.open('', '_blank');
|
|||
|
|
if (newWindow) {
|
|||
|
|
newWindow.document.write(`
|
|||
|
|
<html>
|
|||
|
|
<head>
|
|||
|
|
<title>Receipts</title>
|
|||
|
|
<style>
|
|||
|
|
body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
|
|||
|
|
iframe { width: 100%; height: 100%; border: none; }
|
|||
|
|
.print-button { position: fixed; top: 20px; right: 20px; z-index: 1000; padding: 10px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<button class="print-button" onclick="window.print()">Print</button>
|
|||
|
|
<iframe src="${blobUrl}" type="application/pdf"></iframe>
|
|||
|
|
</body>
|
|||
|
|
</html>
|
|||
|
|
`);
|
|||
|
|
newWindow.document.close();
|
|||
|
|
} else {
|
|||
|
|
window.location.href = blobUrl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// For mobile intent URL
|
|||
|
|
const intentUrl =
|
|||
|
|
'intent://open?' +
|
|||
|
|
'filename=' +
|
|||
|
|
encodeURIComponent('Receipts.pdf') +
|
|||
|
|
'&size=' +
|
|||
|
|
combinedPdfBlob.size +
|
|||
|
|
'&type=application/pdf' +
|
|||
|
|
'&data=' +
|
|||
|
|
encodeURIComponent(base64Pdf) +
|
|||
|
|
'&PrintTypeS=' +
|
|||
|
|
encodeURIComponent(Printsize) +
|
|||
|
|
'&PrintTypeE' +
|
|||
|
|
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
|||
|
|
|
|||
|
|
setTimeout(() => {
|
|||
|
|
try {
|
|||
|
|
window.location.href = intentUrl;
|
|||
|
|
} catch (e) {
|
|||
|
|
console.warn('Could not open intent URL:', e);
|
|||
|
|
}
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Clean up
|
|||
|
|
setPrintOrderDetails([]);
|
|||
|
|
if (container) {
|
|||
|
|
document.body.removeChild(container);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { success: true, message: 'PDF processed successfully' };
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('MobileMultiPdfPrint error:', error);
|
|||
|
|
return { success: false, message: error.message };
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Progress component to show processing status
|
|||
|
|
export const PdfProcessingProgress = ({ progress, isProcessing }) => {
|
|||
|
|
if (!isProcessing) return null;
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
position: 'fixed',
|
|||
|
|
top: 0,
|
|||
|
|
left: 0,
|
|||
|
|
right: 0,
|
|||
|
|
bottom: 0,
|
|||
|
|
backgroundColor: 'rgba(0,0,0,0.7)',
|
|||
|
|
display: 'flex',
|
|||
|
|
flexDirection: 'column',
|
|||
|
|
justifyContent: 'center',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
zIndex: 10999,
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
backgroundColor: 'white',
|
|||
|
|
padding: '20px',
|
|||
|
|
borderRadius: '8px',
|
|||
|
|
textAlign: 'center',
|
|||
|
|
minWidth: '300px',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<h3>Processing PDFs</h3>
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
width: '100%',
|
|||
|
|
backgroundColor: '#f0f0f0',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
margin: '15px 0',
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
width: `${progress}%`,
|
|||
|
|
height: '20px',
|
|||
|
|
backgroundColor: '#4caf50',
|
|||
|
|
borderRadius: '4px',
|
|||
|
|
transition: 'width 0.3s',
|
|||
|
|
}}
|
|||
|
|
></div>
|
|||
|
|
</div>
|
|||
|
|
<p>{progress}% Complete</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export const MobileMultiCreditPdfPrint = async ({
|
|||
|
|
PrintOrderDetails = [],
|
|||
|
|
BSCreditCustomerPrint,
|
|||
|
|
style,
|
|||
|
|
PrintComing = '',
|
|||
|
|
emailId = '',
|
|||
|
|
dispatch,
|
|||
|
|
UserId = '',
|
|||
|
|
onProgress = () => {},
|
|||
|
|
BATCH_SIZE = 5,
|
|||
|
|
}) => {
|
|||
|
|
if (!BSCreditCustomerPrint) {
|
|||
|
|
throw new Error('BSCreditCustomerPrint component is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Helper: wait for images & fonts in a container
|
|||
|
|
const waitForImagesAndFonts = (container, timeout = 7000) =>
|
|||
|
|
new Promise((resolve) => {
|
|||
|
|
// images
|
|||
|
|
const imgs = Array.from(container.querySelectorAll('img'));
|
|||
|
|
let remaining = imgs.length;
|
|||
|
|
if (remaining === 0) {
|
|||
|
|
// still ensure fonts ready
|
|||
|
|
if (document.fonts && document.fonts.ready) {
|
|||
|
|
document.fonts.ready.then(() => resolve()).catch(() => resolve());
|
|||
|
|
} else resolve();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const timer = setTimeout(() => {
|
|||
|
|
clearTimeout(timer);
|
|||
|
|
resolve();
|
|||
|
|
}, timeout);
|
|||
|
|
imgs.forEach((img) => {
|
|||
|
|
if (img.complete) {
|
|||
|
|
remaining--;
|
|||
|
|
if (remaining === 0) {
|
|||
|
|
clearTimeout(timer);
|
|||
|
|
// fonts:
|
|||
|
|
if (document.fonts && document.fonts.ready) {
|
|||
|
|
document.fonts.ready.then(() => resolve()).catch(() => resolve());
|
|||
|
|
} else resolve();
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
img.addEventListener(
|
|||
|
|
'load',
|
|||
|
|
() => {
|
|||
|
|
remaining--;
|
|||
|
|
if (remaining === 0) {
|
|||
|
|
clearTimeout(timer);
|
|||
|
|
if (document.fonts && document.fonts.ready) {
|
|||
|
|
document.fonts.ready
|
|||
|
|
.then(() => resolve())
|
|||
|
|
.catch(() => resolve());
|
|||
|
|
} else resolve();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ once: true }
|
|||
|
|
);
|
|||
|
|
img.addEventListener(
|
|||
|
|
'error',
|
|||
|
|
() => {
|
|||
|
|
remaining--;
|
|||
|
|
if (remaining === 0) {
|
|||
|
|
clearTimeout(timer);
|
|||
|
|
if (document.fonts && document.fonts.ready) {
|
|||
|
|
document.fonts.ready
|
|||
|
|
.then(() => resolve())
|
|||
|
|
.catch(() => resolve());
|
|||
|
|
} else resolve();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ once: true }
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Fallback mergePdfBlobs using pdf-lib (client-side)
|
|||
|
|
const mergePdfBlobsFallback = async (blobs) => {
|
|||
|
|
if (!blobs || blobs.length === 0) {
|
|||
|
|
return new Blob([], { type: 'application/pdf' });
|
|||
|
|
}
|
|||
|
|
if (blobs.length === 1) return blobs[0];
|
|||
|
|
|
|||
|
|
const mergedPdf = await PDFDocument.create();
|
|||
|
|
for (const blob of blobs) {
|
|||
|
|
const arrayBuffer = await blob.arrayBuffer();
|
|||
|
|
const donorPdf = await PDFDocument.load(arrayBuffer);
|
|||
|
|
const copiedPages = await mergedPdf.copyPages(
|
|||
|
|
donorPdf,
|
|||
|
|
donorPdf.getPageIndices()
|
|||
|
|
);
|
|||
|
|
copiedPages.forEach((p) => mergedPdf.addPage(p));
|
|||
|
|
}
|
|||
|
|
const mergedBytes = await mergedPdf.save();
|
|||
|
|
return new Blob([mergedBytes], { type: 'application/pdf' });
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const blobToBase64 = (blob) =>
|
|||
|
|
new Promise((resolve, reject) => {
|
|||
|
|
const reader = new FileReader();
|
|||
|
|
reader.onloadend = () => resolve(reader.result.split(',')[1]); // base64 without prefix
|
|||
|
|
reader.onerror = reject;
|
|||
|
|
reader.readAsDataURL(blob);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// create container or ensure empty
|
|||
|
|
let printRootContainer = document.getElementById('print-merged-react');
|
|||
|
|
if (printRootContainer) {
|
|||
|
|
printRootContainer.innerHTML = '';
|
|||
|
|
} else {
|
|||
|
|
printRootContainer = document.createElement('div');
|
|||
|
|
printRootContainer.id = 'print-merged-react';
|
|||
|
|
printRootContainer.style.display = 'none';
|
|||
|
|
document.body.appendChild(printRootContainer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const total = PrintOrderDetails.length;
|
|||
|
|
let allBlobs = [];
|
|||
|
|
let processedVisible = 0;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
for (let i = 0; i < total; i += BATCH_SIZE) {
|
|||
|
|
const batch = PrintOrderDetails.slice(i, i + BATCH_SIZE);
|
|||
|
|
|
|||
|
|
// clear container for each batch
|
|||
|
|
printRootContainer.innerHTML = '';
|
|||
|
|
|
|||
|
|
// Render each BSCreditCustomerPrint into its own div
|
|||
|
|
const roots = [];
|
|||
|
|
for (let idx = 0; idx < batch.length; idx++) {
|
|||
|
|
const globalIndex = i + idx;
|
|||
|
|
|
|||
|
|
const wrapper = document.createElement('div');
|
|||
|
|
wrapper.id = `Credit-customer-${globalIndex}`;
|
|||
|
|
|
|||
|
|
const html = renderToStaticMarkup(
|
|||
|
|
<BSCreditCustomerPrint PrintDatas={batch[idx]} />
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
wrapper.innerHTML = html;
|
|||
|
|
|
|||
|
|
printRootContainer.appendChild(wrapper);
|
|||
|
|
|
|||
|
|
// add page break if needed
|
|||
|
|
if (globalIndex < total - 1) {
|
|||
|
|
const pb = document.createElement('div');
|
|||
|
|
pb.style.pageBreakAfter = 'always';
|
|||
|
|
printRootContainer.appendChild(pb);
|
|||
|
|
}
|
|||
|
|
roots.push(printRootContainer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await new Promise((res) => setTimeout(res, 120)); // small tick
|
|||
|
|
|
|||
|
|
await waitForImagesAndFonts(printRootContainer, 8000);
|
|||
|
|
|
|||
|
|
const batchContainerId = `print-merged-batch-${i}`;
|
|||
|
|
let batchContainer = document.getElementById(batchContainerId);
|
|||
|
|
if (!batchContainer) {
|
|||
|
|
batchContainer = document.createElement('div');
|
|||
|
|
batchContainer.id = batchContainerId;
|
|||
|
|
batchContainer.style.display = 'none';
|
|||
|
|
document.body.appendChild(batchContainer);
|
|||
|
|
}
|
|||
|
|
batchContainer.innerHTML = `${style}${printRootContainer.innerHTML}`;
|
|||
|
|
|
|||
|
|
const pdfBlob = await pdfDiv(batchContainerId, style);
|
|||
|
|
allBlobs.push(pdfBlob);
|
|||
|
|
|
|||
|
|
roots.forEach((r) => {
|
|||
|
|
try {
|
|||
|
|
r.unmount();
|
|||
|
|
} catch (e) {
|
|||
|
|
// ignore
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// cleanup batch container
|
|||
|
|
try {
|
|||
|
|
if (batchContainer && batchContainer.parentElement) {
|
|||
|
|
batchContainer.parentElement.removeChild(batchContainer);
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
/* ignore */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// progress
|
|||
|
|
processedVisible += batch.length;
|
|||
|
|
onProgress(Math.round((processedVisible / total) * 100));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// merge blobs - use existing merge function if available, otherwise fallback
|
|||
|
|
const combinedPdfBlob =
|
|||
|
|
typeof mergePdfBlobs === 'function'
|
|||
|
|
? await mergePdfBlobs(allBlobs)
|
|||
|
|
: await mergePdfBlobsFallback(allBlobs);
|
|||
|
|
|
|||
|
|
const base64Pdf = await blobToBase64(combinedPdfBlob);
|
|||
|
|
|
|||
|
|
// Action handlers
|
|||
|
|
if (PrintComing === 'download') {
|
|||
|
|
const blobUrl =
|
|||
|
|
typeof createSecureBlobUrl === 'function'
|
|||
|
|
? createSecureBlobUrl(combinedPdfBlob)
|
|||
|
|
: URL.createObjectURL(combinedPdfBlob);
|
|||
|
|
const link = document.createElement('a');
|
|||
|
|
link.href = blobUrl;
|
|||
|
|
link.download = 'Receipts.pdf';
|
|||
|
|
document.body.appendChild(link);
|
|||
|
|
link.click();
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.body.removeChild(link);
|
|||
|
|
if (typeof revokeSecureBlobUrl === 'function')
|
|||
|
|
revokeSecureBlobUrl(blobUrl);
|
|||
|
|
else URL.revokeObjectURL(blobUrl);
|
|||
|
|
}, 300);
|
|||
|
|
return { success: true };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (PrintComing === 'whatsapp') {
|
|||
|
|
const orderIdsArray = PrintOrderDetails.map((r) => r?.receiptNo).filter(
|
|||
|
|
Boolean
|
|||
|
|
);
|
|||
|
|
if (orderIdsArray.length === 0) {
|
|||
|
|
console.warn('No order ids for whatsapp');
|
|||
|
|
return { success: false, message: 'No orders' };
|
|||
|
|
}
|
|||
|
|
const encrypted = encryptObject(orderIdsArray);
|
|||
|
|
const postData = { Url: encrypted, CreatedBy: UserId };
|
|||
|
|
try {
|
|||
|
|
const res = await dispatch(PublicQrCodepost(postData)).unwrap();
|
|||
|
|
const urlData = res?.data?.ReferenceNo || res?.ReferenceNo;
|
|||
|
|
if (!urlData) throw new Error('No ReferenceNo from backend');
|
|||
|
|
const documentUrl = `${MainHomeUrl}viewbill?code=${urlData}`;
|
|||
|
|
const waUrl =
|
|||
|
|
'https://web.whatsapp.com/send?text=' +
|
|||
|
|
encodeURIComponent('Here are your receipts 👉 ' + documentUrl);
|
|||
|
|
window.open(waUrl, '_blank');
|
|||
|
|
return { success: true };
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('WhatsApp sharing failed', err);
|
|||
|
|
return { success: false, message: err?.message || String(err) };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (PrintComing === 'email') {
|
|||
|
|
try {
|
|||
|
|
const payload = {
|
|||
|
|
fileBlob: combinedPdfBlob,
|
|||
|
|
toEmail: emailId,
|
|||
|
|
type: 'CreditReceipt',
|
|||
|
|
messageTemplatesList: [],
|
|||
|
|
};
|
|||
|
|
const res = await dispatch(Emailsend(payload)).unwrap();
|
|||
|
|
return { success: true, data: res };
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('Email send failed', err);
|
|||
|
|
return { success: false, message: err?.message || String(err) };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// default: open viewer tab and attempt mobile intent
|
|||
|
|
const blobUrl =
|
|||
|
|
typeof createSecureBlobUrl === 'function'
|
|||
|
|
? createSecureBlobUrl(combinedPdfBlob)
|
|||
|
|
: URL.createObjectURL(combinedPdfBlob);
|
|||
|
|
|
|||
|
|
const newWindow = window.open('', '_blank');
|
|||
|
|
if (newWindow) {
|
|||
|
|
newWindow.document.write(`
|
|||
|
|
<html>
|
|||
|
|
<head><title>Receipts</title>
|
|||
|
|
<style>
|
|||
|
|
body{margin:0;background:#f0f0f0;height:100vh;display:flex;align-items:center;justify-content:center}
|
|||
|
|
iframe{width:100%;height:100%;border:none}
|
|||
|
|
.print-button{position:fixed;top:16px;right:16px;padding:10px 14px;background:#4CAF50;color:white;border:none;border-radius:6px;cursor:pointer;z-index:9999}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<button class="print-button" onclick="window.print()">Print</button>
|
|||
|
|
<iframe src="${blobUrl}" type="application/pdf"></iframe>
|
|||
|
|
</body>
|
|||
|
|
</html>
|
|||
|
|
`);
|
|||
|
|
newWindow.document.close();
|
|||
|
|
} else {
|
|||
|
|
window.location.href = blobUrl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// mobile intent (optional)
|
|||
|
|
const intentUrl =
|
|||
|
|
'intent://open?' +
|
|||
|
|
'filename=' +
|
|||
|
|
encodeURIComponent('Receipts.pdf') +
|
|||
|
|
'&size=' +
|
|||
|
|
combinedPdfBlob.size +
|
|||
|
|
'&type=application/pdf' +
|
|||
|
|
'&data=' +
|
|||
|
|
encodeURIComponent(base64Pdf) +
|
|||
|
|
'#Intent;scheme=pozoprinter;package=com.example.pozoprinter;end';
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
try {
|
|||
|
|
window.location.href = intentUrl;
|
|||
|
|
} catch (e) {
|
|||
|
|
/* ignore */
|
|||
|
|
}
|
|||
|
|
}, 500);
|
|||
|
|
} catch (e) {
|
|||
|
|
/* ignore */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { success: true };
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error('MobileMultiCreditPdfPrint error:', err);
|
|||
|
|
return { success: false, message: err?.message || String(err) };
|
|||
|
|
} finally {
|
|||
|
|
// final cleanup
|
|||
|
|
try {
|
|||
|
|
// remove printRootContainer and any leftover child batch containers
|
|||
|
|
const root = document.getElementById('print-merged-react');
|
|||
|
|
if (root && root.parentElement) root.parentElement.removeChild(root);
|
|||
|
|
|
|||
|
|
// remove any batch containers (ids starting with print-merged-batch-)
|
|||
|
|
Array.from(
|
|||
|
|
document.querySelectorAll("[id^='print-merged-batch-']")
|
|||
|
|
).forEach((el) => {
|
|||
|
|
try {
|
|||
|
|
el.parentElement.removeChild(el);
|
|||
|
|
} catch (e) {}
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
console.warn('Cleanup error', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|