// email-modal.js — ES Module // Modal to review and send invoice emails via AWS SES // With Stripe Payment Link integration import { showSpinner, hideSpinner, formatDate } 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 = `
Dear ${customerName},
We hope this message finds you well. Our records indicate that invoice #${invoiceNum} in the amount of $${totalDue}, dated ${formatDate(currentInvoice.invoice_date)}, remains unpaid.
This invoice is now ${daysSinceInvoice} days past the invoice date. We kindly request prompt payment at your earliest convenience.
For your convenience, you can pay securely online using the payment link included below. We accept both Credit Card and ACH bank transfer.
If payment has already been sent, please disregard this notice. Should you have any questions or need to discuss payment arrangements, please do not hesitate to reply to this email.
Thank you for your attention to this matter. We value your business and look forward to continuing our partnership.
Best regards,
Claudia Knuth
Bay Area Affiliates, Inc.
accounting@bayarea-cc.com
`; } else { // Standard template 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 };