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(` Receipts `); 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 (

Processing PDFs

{progress}% Complete

); }; 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( ); 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(` Receipts `); 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); } } };