Spaces:
Sleeping
Sleeping
| import html2canvas from 'html2canvas'; | |
| import jsPDF from 'jspdf'; | |
| /** | |
| * Export invoice element as high-quality image | |
| */ | |
| export async function exportInvoiceAsImage( | |
| element: HTMLElement, | |
| options?: { scale?: number; format?: 'png' | 'jpeg'; quality?: number } | |
| ): Promise<string> { | |
| const { scale = 3, format = 'png', quality = 0.95 } = options || {}; | |
| const canvas = await html2canvas(element, { | |
| scale, // Higher quality for better readability | |
| useCORS: true, | |
| backgroundColor: '#ffffff', | |
| logging: false, | |
| windowWidth: element.scrollWidth, | |
| windowHeight: element.scrollHeight, | |
| }); | |
| const imageFormat = format === 'jpeg' ? 'image/jpeg' : 'image/png'; | |
| return canvas.toDataURL(imageFormat, quality); | |
| } | |
| /** | |
| * Downloads the invoice as an image file | |
| * @param base64Image - Base64 encoded image string | |
| * @param filename - Name of the file to download | |
| */ | |
| export function downloadInvoiceImage(base64Image: string, filename: string = 'invoice.png') { | |
| const link = document.createElement('a'); | |
| link.href = base64Image; | |
| link.download = filename; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| } | |
| /** | |
| * Shares invoice via WhatsApp Web | |
| * @param phoneNumber - Customer's phone number (with country code, e.g., 919876543210) | |
| * @param billNumber - Invoice/Bill number | |
| * @param amount - Total amount | |
| * @param base64Image - Base64 encoded image (optional, for future file sharing) | |
| */ | |
| export function shareInvoiceViaWhatsApp( | |
| phoneNumber: string, | |
| billNumber: string, | |
| amount: number, | |
| language: 'en' | 'mr' = 'mr' | |
| ) { | |
| // Clean phone number (remove spaces, hyphens, etc.) | |
| const cleanedNumber = phoneNumber.replace(/[^\d+]/g, ''); | |
| // Ensure country code is present | |
| const formattedNumber = cleanedNumber.startsWith('+') || cleanedNumber.startsWith('91') | |
| ? cleanedNumber | |
| : `91${cleanedNumber}`; | |
| // Create message text based on language | |
| const message = language === 'mr' | |
| ? `नमस्कार,\n\nतुमचे बिल नंबर *${billNumber}* तयार झाले आहे.\n\nएकूण रक्कम: ₹${amount.toLocaleString('en-IN')}\n\nकृपया रक्कम वेळेवर भरावी.\n\nधन्यवाद!` | |
| : `Hello,\n\nYour bill number *${billNumber}* is ready.\n\nTotal Amount: ₹${amount.toLocaleString('en-IN')}\n\nPlease pay on time.\n\nThank you!`; | |
| // WhatsApp Web/API URL | |
| const whatsappUrl = `https://wa.me/${formattedNumber}?text=${encodeURIComponent(message)}`; | |
| // Open WhatsApp | |
| window.open(whatsappUrl, '_blank'); | |
| } | |
| /** | |
| * Shares invoice image via WhatsApp using the device's native share (mobile) | |
| * @param base64Image - Base64 encoded image | |
| * @param billNumber - Invoice number | |
| * @param partyName - Customer name | |
| */ | |
| export async function shareInvoiceImageNative( | |
| base64Image: string, | |
| billNumber: string, | |
| partyName: string | |
| ) { | |
| try { | |
| // Convert base64 to blob | |
| const response = await fetch(base64Image); | |
| const blob = await response.blob(); | |
| // Create a file from blob | |
| const file = new File([blob], `Invoice_${billNumber}.png`, { type: 'image/png' }); | |
| // Check if Web Share API is supported | |
| if (navigator.share && navigator.canShare({ files: [file] })) { | |
| await navigator.share({ | |
| title: `Invoice ${billNumber}`, | |
| text: `Invoice for ${partyName}`, | |
| files: [file], | |
| }); | |
| return true; | |
| } else { | |
| // Fallback: Download the image | |
| downloadInvoiceImage(base64Image, `Invoice_${billNumber}_${partyName}.png`); | |
| return false; | |
| } | |
| } catch (error) { | |
| console.error('Error sharing invoice:', error); | |
| // Fallback: Download the image | |
| downloadInvoiceImage(base64Image, `Invoice_${billNumber}_${partyName}.png`); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Formats phone number for WhatsApp | |
| * @param phone - Phone number in any format | |
| * @returns Formatted phone number with country code | |
| */ | |
| export function formatPhoneForWhatsApp(phone: string): string { | |
| const cleaned = phone.replace(/[^\d+]/g, ''); | |
| if (cleaned.startsWith('+91')) { | |
| return cleaned; | |
| } else if (cleaned.startsWith('91') && cleaned.length === 12) { | |
| return `+${cleaned}`; | |
| } else if (cleaned.length === 10) { | |
| return `+91${cleaned}`; | |
| } | |
| return cleaned; | |
| } | |
| /** | |
| * Validates Indian phone number | |
| * @param phone - Phone number to validate | |
| * @returns true if valid | |
| */ | |
| export function isValidIndianPhoneNumber(phone: string): boolean { | |
| const cleaned = phone.replace(/[^\d]/g, ''); | |
| // Check if it's a 10-digit number starting with 6-9 | |
| if (cleaned.length === 10 && /^[6-9]\d{9}$/.test(cleaned)) { | |
| return true; | |
| } | |
| // Check if it's a 12-digit number starting with 91 | |
| if (cleaned.length === 12 && cleaned.startsWith('91') && /^91[6-9]\d{9}$/.test(cleaned)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Export invoice as PDF | |
| */ | |
| export async function exportInvoiceAsPDF( | |
| element: HTMLElement, | |
| filename: string = 'invoice.pdf', | |
| options?: { orientation?: 'portrait' | 'landscape'; format?: 'a4' | 'letter' } | |
| ): Promise<void> { | |
| const { orientation = 'portrait', format = 'a4' } = options || {}; | |
| // Generate high-quality image first | |
| const imageData = await exportInvoiceAsImage(element, { scale: 3, format: 'jpeg', quality: 0.95 }); | |
| // Create PDF | |
| const pdf = new jsPDF({ | |
| orientation, | |
| unit: 'mm', | |
| format, | |
| }); | |
| // Calculate dimensions to fit page | |
| const imgProps = pdf.getImageProperties(imageData); | |
| const pdfWidth = pdf.internal.pageSize.getWidth(); | |
| const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; | |
| // Add image to PDF | |
| pdf.addImage(imageData, 'JPEG', 0, 0, pdfWidth, pdfHeight); | |
| // Save PDF | |
| pdf.save(filename); | |
| } | |
| /** | |
| * Generate invoice text for WhatsApp or SMS | |
| */ | |
| export function generateInvoiceText( | |
| billNumber: string, | |
| partyName: string, | |
| items: Array<{ name: string; quantity: number; rate: number; amount: number }>, | |
| charges: { bardhana?: number; hamali?: number; adhath?: number; cess?: number; gaadiBharni?: number }, | |
| total: number, | |
| language: 'en' | 'mr' = 'mr' | |
| ): string { | |
| if (language === 'mr') { | |
| let text = `🧾 *बिल नंबर:* ${billNumber}\n`; | |
| text += `👤 *पार्टी:* ${partyName}\n`; | |
| text += `📅 *तारीख:* ${new Date().toLocaleDateString('mr-IN')}\n\n`; | |
| text += `*📦 माल तपशील:*\n`; | |
| text += `${'─'.repeat(35)}\n`; | |
| items.forEach((item, i) => { | |
| text += `${i + 1}. ${item.name}\n`; | |
| text += ` ${item.quantity} पोत्या × ₹${item.rate} = ₹${item.amount.toLocaleString('en-IN')}\n`; | |
| }); | |
| text += `${'─'.repeat(35)}\n`; | |
| // Add charges if any | |
| const totalCharges = (charges.bardhana || 0) + (charges.hamali || 0) + | |
| (charges.adhath || 0) + (charges.cess || 0) + (charges.gaadiBharni || 0); | |
| if (totalCharges > 0) { | |
| text += `\n*💰 खर्च:*\n`; | |
| if (charges.bardhana) text += ` बर्धाना: ₹${charges.bardhana.toLocaleString('en-IN')}\n`; | |
| if (charges.hamali) text += ` हमाली: ₹${charges.hamali.toLocaleString('en-IN')}\n`; | |
| if (charges.adhath) text += ` अडत: ₹${charges.adhath.toLocaleString('en-IN')}\n`; | |
| if (charges.cess) text += ` सेस: ₹${charges.cess.toLocaleString('en-IN')}\n`; | |
| if (charges.gaadiBharni) text += ` गाडी भरणी: ₹${charges.gaadiBharni.toLocaleString('en-IN')}\n`; | |
| text += `${'─'.repeat(35)}\n`; | |
| } | |
| text += `\n*💵 एकूण रक्कम: ₹${total.toLocaleString('en-IN')}*\n\n`; | |
| text += `कृपया रक्कम वेळेवर भरावी.\n\n`; | |
| text += `धन्यवाद! 🙏`; | |
| return text; | |
| } else { | |
| let text = `🧾 *Bill No:* ${billNumber}\n`; | |
| text += `👤 *Party:* ${partyName}\n`; | |
| text += `📅 *Date:* ${new Date().toLocaleDateString('en-IN')}\n\n`; | |
| text += `*📦 Items:*\n`; | |
| text += `${'─'.repeat(35)}\n`; | |
| items.forEach((item, i) => { | |
| text += `${i + 1}. ${item.name}\n`; | |
| text += ` ${item.quantity} bags × ₹${item.rate} = ₹${item.amount.toLocaleString('en-IN')}\n`; | |
| }); | |
| text += `${'─'.repeat(35)}\n`; | |
| const totalCharges = (charges.bardhana || 0) + (charges.hamali || 0) + | |
| (charges.adhath || 0) + (charges.cess || 0) + (charges.gaadiBharni || 0); | |
| if (totalCharges > 0) { | |
| text += `\n*💰 Charges:*\n`; | |
| if (charges.bardhana) text += ` Bardhana: ₹${charges.bardhana.toLocaleString('en-IN')}\n`; | |
| if (charges.hamali) text += ` Hamali: ₹${charges.hamali.toLocaleString('en-IN')}\n`; | |
| if (charges.adhath) text += ` Commission: ₹${charges.adhath.toLocaleString('en-IN')}\n`; | |
| if (charges.cess) text += ` Cess: ₹${charges.cess.toLocaleString('en-IN')}\n`; | |
| if (charges.gaadiBharni) text += ` Loading: ₹${charges.gaadiBharni.toLocaleString('en-IN')}\n`; | |
| text += `${'─'.repeat(35)}\n`; | |
| } | |
| text += `\n*💵 Total Amount: ₹${total.toLocaleString('en-IN')}*\n\n`; | |
| text += `Please pay on time.\n\n`; | |
| text += `Thank you! 🙏`; | |
| return text; | |
| } | |
| } | |
| /** | |
| * Share detailed invoice text via WhatsApp | |
| */ | |
| export function shareDetailedInvoiceText( | |
| phoneNumber: string, | |
| invoiceText: string | |
| ): void { | |
| const formattedPhone = formatPhoneForWhatsApp(phoneNumber); | |
| const encodedText = encodeURIComponent(invoiceText); | |
| const whatsappUrl = `https://wa.me/${formattedPhone}?text=${encodedText}`; | |
| window.open(whatsappUrl, '_blank'); | |
| } | |
| /** | |
| * Prints the invoice | |
| * @param element - The invoice element to print | |
| */ | |
| export function printInvoice(element?: HTMLElement) { | |
| if (element) { | |
| // Create a new window with just the invoice content | |
| const printWindow = window.open('', '_blank'); | |
| if (printWindow) { | |
| printWindow.document.write(` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Print Invoice</title> | |
| <style> | |
| body { margin: 0; padding: 20px; font-family: Arial, sans-serif; } | |
| @media print { | |
| body { margin: 0; } | |
| @page { margin: 10mm; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| ${element.innerHTML} | |
| </body> | |
| </html> | |
| `); | |
| printWindow.document.close(); | |
| setTimeout(() => { | |
| printWindow.print(); | |
| printWindow.close(); | |
| }, 250); | |
| } | |
| } else { | |
| // Default browser print | |
| window.print(); | |
| } | |
| } | |