${invNo} — Complete Document Set

Generated by ShippingDocu Generation || ${new Date().toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })}

${invoiceContent}
${packingContent}
${cooContent}
${marksContent} `; const blob = new Blob([combinedHTML], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = invNo + '_Full-Document-Set.html'; a.click(); URL.revokeObjectURL(url); showToast('📥 Full document set downloaded as HTML! Open in browser → 🖨 Print → Save as PDF.', 'success'); } // ── 🖨 Print ── function printDocs() { const sections = document.getElementById('preview-section'); if (sections.classList.contains('hidden')) { showToast('Please generate the document first.', 'warn'); return; } // Activate all panels temporarily for print const allPanels = document.querySelectorAll('.doc-panel'); allPanels.forEach(p => p.style.display = 'block'); const activePanel = document.querySelector('.doc-panel.active'); const originalDisplay = activePanel?.style.display; window.print(); // Restore allPanels.forEach(p => { p.style.display = ''; if (!p.classList.contains('active')) p.style.display = 'none'; }); if (activePanel) { activePanel.style.display = 'block'; } } // ── 📧 Send to Forwarder (mock) ── function sendToForwarder() { const data = collectOrderData(); if (!data.customer) { showToast('Please generate the document first.', 'warn'); return; } const forwarders = [ 'Kuehne + Nagel', 'DHL Global Forwarding', 'Expeditors', 'DSV Panalpina', 'Kerry Logistics' ]; const randomFW = forwarders[Math.floor(Math.random() * forwarders.length)]; showToast(`📧 Document set has been sent to ${randomFW}! (Demo — email not actually sent)`, 'success'); // Simulate "sent" overlay const fwBtn = document.querySelector('.preview-btn.danger'); fwBtn.textContent = '✅ Sent to Forwarder'; fwBtn.disabled = true; fwBtn.style.opacity = '0.6'; fwBtn.style.cursor = 'default'; setTimeout(() => { fwBtn.textContent = '📧 Send to Forwarder'; fwBtn.disabled = false; fwBtn.style.opacity = ''; fwBtn.style.cursor = ''; }, 5000); } // ── Number to Words (simplified for display) ── function numberToWords(amount) { const ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']; const tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']; if (amount === 0) return 'zero'; const dollars = Math.floor(amount); const cents = Math.round((amount - dollars) * 100); function convertHundreds(n) { if (n < 20) return ones[n]; if (n < 100) return tens[Math.floor(n / 10)] + (n % 10 ? '-' + ones[n % 10] : ''); return ones[Math.floor(n / 100)] + ' hundred' + (n % 100 ? ' ' + convertHundreds(n % 100) : ''); } function convertLarge(n) { if (n === 0) return ''; if (n < 1000) return convertHundreds(n); const thousands = Math.floor(n / 1000); const remainder = n % 1000; return convertHundreds(thousands) + ' thousand' + (remainder ? ' ' + convertLarge(remainder) : ''); } let result = convertLarge(dollars); if (cents > 0) { result += ' and ' + cents + '/100'; } return result; } // ═══ AI PROXY FOR ORDER FILE PARSING ═══ var SUPPORTED_INLINE=['image/jpeg','image/png','image/webp','application/pdf']; function sleep(ms){return new Promise(function(r){setTimeout(r,ms)});} function canInline(m){return SUPPORTED_INLINE.indexOf(m)!==-1;} async function callAI(prompt, imageB64, temperature, maxTokens){ var resp = await fetch('/api/gemini', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ prompt: prompt, image: imageB64 | null, temperature: temperature | 0.2, maxTokens: maxTokens | 2048 }) }).catch(function(err){ throw new Error('AI service is temporarily unavailable, please try again later'); }); var data = await resp.json(); if(!resp.ok || data.error) throw new Error(data.error || 'API '+resp.status); return data.text || ''; } function getMime(f){var n=f.name.toLowerCase();if(n.endsWith('.pdf'))return'application/pdf';if(n.endsWith('.xlsx') || n.endsWith('.xls'))return'xlsx';if(n.endsWith('.docx') || n.endsWith('.doc'))return'docx';if(n.endsWith('.pptx') || n.endsWith('.ppt'))return'pptx';if(f.type&&f.type.match(/image\//))return f.type;if(n.endsWith('.jpg') || n.endsWith('.jpeg'))return'image/jpeg';if(n.endsWith('.png'))return'image/png';return'application/octet-stream';} function readFileB64(f){return new Promise(function(r,rej){var rd=new FileReader();rd.onload=function(e){r(e.target.result.split(',')[1])};rd.onerror=rej;rd.readAsDataURL(f);});} async function handleOrderFile(input){ if(!input.files || !input.files[0])return; var file=input.files[0]; var oz=document.getElementById('orderDropzone'); oz.classList.add('has-file'); var status=document.getElementById('orderStatus'); status.style.display='flex';status.style.background='rgba(99,102,241,.06)';status.style.color='var(--accent)'; document.getElementById('orderStatusText').textContent='🤖 AI Parsing'; try{ var mime=getMime(file),b64=await readFileB64(file); var orderPrompt='Extract order/shipping details. Return ONLY JSON:\n{"customer":"buyer company name","po":"PO number","incoterms":"FOB/CIF/EXW","portLoading":"port of loading","portDischarge":"port of discharge","container":"type","paymentTerms":"terms","vessel":"vessel name/voyage","buyerAddress":"address"}\nNo marks. Just JSON.'; var imageB64=canInline(mime)?b64:null; if(!canInline(mime))orderPrompt+='\n[File: '+file.name+']\nContent: '+b64.substring(0,4000)+'\n[Extract order details]'; var text=await callAI(orderPrompt, imageB64, 0, 2048); text=text.replace(/```json/g,'').replace(/```/g,'').trim();var d=JSON.parse(text); if(d.customer)document.getElementById('customer-name').value=d.customer; if(d.po)document.getElementById('po-number').value=d.po; if(d.incoterms)document.getElementById('incoterms').value=d.incoterms; if(d.portLoading)document.getElementById('port-loading').value=d.portLoading; if(d.portDischarge)document.getElementById('port-discharge').value=d.portDischarge; if(d.container)document.getElementById('container-type').value=d.container; if(d.paymentTerms)document.getElementById('payment-terms').value=d.paymentTerms; if(d.vessel)document.getElementById('vessel').value=d.vessel; if(d.buyerAddress)document.getElementById('buyer-address').value=d.buyerAddress; status.style.background='rgba(16,185,129,.05)';status.style.color='var(--green)'; document.getElementById('orderStatusText').textContent='✅ AI has automatically filled in the order details — check and click Generate Document'; }catch(e){ status.style.background='rgba(239,68,68,.05)';status.style.color='var(--red)'; document.getElementById('orderStatusText').textContent='❌ Parsing failed: '+e.message.substring(0,80); oz.classList.remove('has-file'); } } // Drag-drop for order dropzone var odz=document.getElementById('orderDropzone'); odz.addEventListener('dragover',function(e){e.preventDefault();odz.classList.add('drag-over');}); odz.addEventListener('dragleave',function(){odz.classList.remove('drag-over');}); odz.addEventListener('drop',function(e){e.preventDefault();odz.classList.remove('drag-over');if(e.dataTransfer.files[0]){var dt=new DataTransfer();dt.items.add(e.dataTransfer.files[0]);document.getElementById('orderFile').files=dt.files;handleOrderFile(document.getElementById('orderFile'));}});