secondary email

This commit is contained in:
2026-05-06 17:04:16 -05:00
parent 6662b3cb18
commit f9b4916e40
5 changed files with 238 additions and 46 deletions

View File

@@ -154,10 +154,8 @@ function renderModalContent() {
<form id="email-send-form" class="space-y-5">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Recipient Email *</label>
<input type="email" id="email-recipient" value="${defaultEmail}" required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
<p class="text-xs text-gray-400 mt-1">You can override this for testing.</p>
<label class="block text-sm font-medium text-gray-700 mb-1">Send to</label>
${renderRecipientSelector(currentInvoice)}
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
@@ -270,6 +268,15 @@ function renderModalContent() {
quillInstance.root.innerHTML = defaultHtml;
// Initiale Recipient-Summary
updateRecipientSummary();
// Listener für das Custom-Field
const customEl = document.getElementById('email-recipient-custom');
if (customEl) {
customEl.addEventListener('input', updateRecipientSummary);
}
// Bind Submit Handler
document.getElementById('email-send-form').addEventListener('submit', submitEmail);
}
@@ -365,14 +372,20 @@ export function closeEmailModal() {
async function submitEmail(e) {
e.preventDefault();
const recipientEmail = document.getElementById('email-recipient').value.trim();
const recipients = getSelectedRecipients();
const customText = quillInstance.root.innerHTML;
if (!recipientEmail) {
alert('Please enter a recipient email.');
if (recipients.length === 0) {
alert('Please select or enter at least one recipient email.');
return;
}
// Confirm bei Mehrfach-Empfängern, damit der User sieht, an wen es geht
if (recipients.length > 1) {
const ok = confirm(`Send invoice to ${recipients.length} recipients?\n\n${recipients.join('\n')}`);
if (!ok) return;
}
const submitBtn = document.getElementById('email-submit-btn');
submitBtn.innerHTML = '⏳ Sending...';
submitBtn.disabled = true;
@@ -384,7 +397,7 @@ async function submitEmail(e) {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
recipientEmail,
recipientEmails: recipients,
customText
})
});
@@ -392,7 +405,8 @@ async function submitEmail(e) {
const result = await response.json();
if (response.ok) {
alert('✅ Invoice sent successfully!');
const sentTo = (result.recipients || recipients).join(', ');
alert(`✅ Invoice sent to: ${sentTo}`);
closeEmailModal();
if (window.invoiceView) window.invoiceView.loadInvoices();
} else {
@@ -408,11 +422,134 @@ async function submitEmail(e) {
}
}
function renderRecipientSelector(invoice) {
const primary = (invoice.email || '').trim();
const secondary = (invoice.secondary_email || '').trim();
const hasPrimary = !!primary;
const hasSecondary = !!secondary;
// Default-Auswahl:
// - "Both" wenn beide vorhanden
// - "Primary" wenn nur primary
// - "Secondary" wenn nur secondary
// - "Custom" wenn keine
let defaultMode;
if (hasPrimary && hasSecondary) defaultMode = 'both';
else if (hasPrimary) defaultMode = 'primary';
else if (hasSecondary) defaultMode = 'secondary';
else defaultMode = 'custom';
// Falls weder primary noch secondary — direkt eine Warnung
let warningHtml = '';
if (!hasPrimary && !hasSecondary) {
warningHtml = `
<p class="text-xs text-amber-600 mt-1">
⚠️ This customer has no email address on file. Please enter one below.
</p>`;
}
return `
<div class="space-y-2">
<select id="email-recipient-mode"
onchange="window.emailModal.onRecipientModeChange()"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500 text-sm">
${hasPrimary ? `<option value="primary" ${defaultMode==='primary'?'selected':''}>Primary: ${escapeAttr(primary)}</option>` : ''}
${hasSecondary ? `<option value="secondary" ${defaultMode==='secondary'?'selected':''}>Secondary: ${escapeAttr(secondary)}</option>` : ''}
${hasPrimary && hasSecondary ? `<option value="both" ${defaultMode==='both'?'selected':''}>Both (Primary + Secondary)</option>` : ''}
<option value="custom" ${defaultMode==='custom'?'selected':''}>Custom / Test address…</option>
</select>
<input type="email" id="email-recipient-custom"
value="${escapeAttr(primary || secondary || '')}"
placeholder="Enter custom recipient email(s), comma-separated"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500 ${defaultMode==='custom' ? '' : 'hidden'}">
<p id="email-recipient-summary" class="text-xs text-gray-500"></p>
${warningHtml}
</div>
`;
}
function escapeAttr(s) {
return String(s || '')
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
/**
* Liefert die aktuell ausgewählten Empfänger als Array.
* Wird beim Submit aufgerufen.
*/
function getSelectedRecipients() {
const modeEl = document.getElementById('email-recipient-mode');
const customEl = document.getElementById('email-recipient-custom');
if (!modeEl) return [];
const mode = modeEl.value;
const primary = (currentInvoice.email || '').trim();
const secondary = (currentInvoice.secondary_email || '').trim();
if (mode === 'primary') return primary ? [primary] : [];
if (mode === 'secondary') return secondary ? [secondary] : [];
if (mode === 'both') {
return [primary, secondary].filter(Boolean);
}
if (mode === 'custom') {
return (customEl?.value || '')
.split(',')
.map(s => s.trim())
.filter(Boolean);
}
return [];
}
/**
* Wird vom <select onchange> aufgerufen.
* Zeigt/versteckt das Custom-Eingabefeld und aktualisiert die Zusammenfassung.
*/
function onRecipientModeChange() {
const modeEl = document.getElementById('email-recipient-mode');
const customEl = document.getElementById('email-recipient-custom');
if (!modeEl) return;
const mode = modeEl.value;
// Custom-Field nur bei mode==='custom' anzeigen
if (mode === 'custom') {
customEl.classList.remove('hidden');
customEl.focus();
} else {
customEl.classList.add('hidden');
}
updateRecipientSummary();
}
function updateRecipientSummary() {
const summary = document.getElementById('email-recipient-summary');
if (!summary) return;
const recipients = getSelectedRecipients();
if (recipients.length === 0) {
summary.textContent = '⚠️ No recipients selected.';
summary.className = 'text-xs text-amber-600';
} else if (recipients.length === 1) {
summary.textContent = `Will send to: ${recipients[0]}`;
summary.className = 'text-xs text-gray-600';
} else {
summary.textContent = `Will send to ${recipients.length} recipients: ${recipients.join(', ')}`;
summary.className = 'text-xs text-gray-600';
}
}
// ============================================================
// Expose
// ============================================================
window.emailModal = {
open: openEmailModal,
close: closeEmailModal,
generateStripeLink
generateStripeLink,
onRecipientModeChange
};