// email-modal.js — ES Module // Modal to review and send invoice emails via AWS SES // With Stripe Payment Link integration import { showSpinner, hideSpinner } from '../utils/helpers.js'; let currentInvoice = null; let quillInstance = null; // ============================================================ // DOM & Render // ============================================================ function ensureModalElement() { let modal = document.getElementById('email-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'email-modal'; modal.className = 'modal fixed inset-0 bg-black bg-opacity-50 z-50 justify-center items-start pt-10 overflow-y-auto hidden'; document.body.appendChild(modal); } } function renderModalContent() { const modal = document.getElementById('email-modal'); if (!modal) return; const defaultEmail = currentInvoice.email || ''; const existingStripeUrl = currentInvoice.stripe_payment_link_url || ''; const stripeStatus = currentInvoice.stripe_payment_status || ''; // Status indicator for existing link let stripeBadgeHtml = ''; if (existingStripeUrl && stripeStatus === 'paid') { stripeBadgeHtml = 'Paid'; } else if (existingStripeUrl && stripeStatus === 'processing') { stripeBadgeHtml = 'Processing'; } else if (existingStripeUrl) { stripeBadgeHtml = 'Active'; } modal.innerHTML = `

📤 Send Invoice #${currentInvoice.invoice_number || currentInvoice.id}

You can override this for testing.

📎
Invoice_${currentInvoice.invoice_number || currentInvoice.id}.pdf will be generated and attached automatically.
`; // Initialize Quill const editorDiv = document.getElementById('email-message-editor'); quillInstance = new Quill(editorDiv, { theme: 'snow', modules: { toolbar: [ ['bold', 'italic', 'underline'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], ['clean'] ] } }); // Variablen für den Text aufbereiten const invoiceNum = currentInvoice.invoice_number || currentInvoice.id; const totalDue = parseFloat(currentInvoice.balance ?? currentInvoice.total).toFixed(2); // Datum formatieren let dueDateStr = 'Upon Receipt'; if (currentInvoice.due_date) { const d = new Date(currentInvoice.due_date); dueDateStr = d.toLocaleDateString('en-US', { timeZone: 'UTC' }); } // Dynamischer Text für die Fälligkeit let paymentText = ''; if (currentInvoice.terms && currentInvoice.terms.toLowerCase().includes('receipt')) { paymentText = 'Our terms are Net 30.'; } else if (dueDateStr !== 'Upon Receipt') { paymentText = `payable by ${dueDateStr}.`; } else { paymentText = 'Our terms are Net 30.'; } const customerName = currentInvoice.customer_name || 'Valued Customer'; const defaultHtml = `

Dear ${customerName},

Attached is invoice #${invoiceNum} for service performed at your location. The total amount due is $${totalDue}, ${paymentText}

Please pay at your earliest convenience. We appreciate your continued business.

If you have any questions about the invoice, feel free to reply to this email.

Best regards,

Claudia Knuth

Bay Area Affiliates, Inc.

accounting@bayarea-cc.com

`; quillInstance.root.innerHTML = defaultHtml; // Bind Submit Handler document.getElementById('email-send-form').addEventListener('submit', submitEmail); } // ============================================================ // Stripe Payment Link Generation // ============================================================ async function generateStripeLink() { const btn = document.getElementById('stripe-generate-btn'); const input = document.getElementById('email-stripe-link'); const info = document.getElementById('stripe-link-info'); const originalBtnText = btn.innerHTML; btn.innerHTML = '⏳ Creating...'; btn.disabled = true; try { const response = await fetch(`/api/invoices/${currentInvoice.id}/create-payment-link`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (response.ok) { input.value = result.paymentLinkUrl; currentInvoice.stripe_payment_link_url = result.paymentLinkUrl; currentInvoice.stripe_payment_link_id = result.paymentLinkId; currentInvoice.stripe_payment_status = 'pending'; btn.innerHTML = '♻️ Regenerate'; info.innerHTML = `✅ Payment link created for $${result.amount.toFixed(2)}. Will be included in the email.`; info.classList.remove('text-gray-400'); info.classList.add('text-green-600'); } else { info.textContent = `❌ ${result.error}`; info.classList.remove('text-gray-400'); info.classList.add('text-red-500'); } } catch (e) { console.error('Stripe link generation error:', e); info.textContent = '❌ Network error creating payment link.'; info.classList.remove('text-gray-400'); info.classList.add('text-red-500'); } finally { btn.disabled = false; if (btn.innerHTML === '⏳ Creating...') { btn.innerHTML = originalBtnText; } } } // ============================================================ // Logic & API // ============================================================ export async function openEmailModal(invoiceId) { ensureModalElement(); if (typeof showSpinner === 'function') showSpinner('Loading invoice data...'); try { const res = await fetch(`/api/invoices/${invoiceId}`); const data = await res.json(); if (!data.invoice) throw new Error('Invoice not found'); currentInvoice = data.invoice; renderModalContent(); document.getElementById('email-modal').classList.remove('hidden'); document.getElementById('email-modal').classList.add('flex'); } catch (e) { console.error('Error loading invoice for email:', e); alert('Could not load invoice details.'); } finally { if (typeof hideSpinner === 'function') hideSpinner(); } } export function closeEmailModal() { const modal = document.getElementById('email-modal'); if (modal) { modal.classList.add('hidden'); modal.classList.remove('flex'); } currentInvoice = null; quillInstance = null; } async function submitEmail(e) { e.preventDefault(); const recipientEmail = document.getElementById('email-recipient').value.trim(); const customText = quillInstance.root.innerHTML; if (!recipientEmail) { alert('Please enter a recipient email.'); return; } const submitBtn = document.getElementById('email-submit-btn'); submitBtn.innerHTML = '⏳ Sending...'; submitBtn.disabled = true; if (typeof showSpinner === 'function') showSpinner('Generating PDF and sending email...'); try { const response = await fetch(`/api/invoices/${currentInvoice.id}/send-email`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipientEmail, customText }) }); const result = await response.json(); if (response.ok) { alert('✅ Invoice sent successfully!'); closeEmailModal(); if (window.invoiceView) window.invoiceView.loadInvoices(); } else { alert(`❌ Error: ${result.error}`); } } catch (e) { console.error('Send email error:', e); alert('Network error while sending email.'); } finally { submitBtn.innerHTML = 'Send via AWS SES'; submitBtn.disabled = false; if (typeof hideSpinner === 'function') hideSpinner(); } } // ============================================================ // Expose // ============================================================ window.emailModal = { open: openEmailModal, close: closeEmailModal, generateStripeLink };