secondary email
This commit is contained in:
@@ -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, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
@@ -248,15 +248,29 @@ export function openModal(customerId = null) {
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Primary Email</label>
|
||||
<input type="email" id="cf-email" value="${customer?.email || ''}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Secondary Email</label>
|
||||
<input type="email" id="cf-secondary-email" value="${customer?.secondary_email || ''}"
|
||||
placeholder="Optional — used for invoices when 'Both' is selected"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Phone</label>
|
||||
<input type="tel" id="cf-phone" value="${customer?.phone || ''}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Phone (alt)</label>
|
||||
<input type="tel" id="cf-phone2" value="${customer?.phone2 || ''}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -303,8 +317,9 @@ async function handleSubmit(e) {
|
||||
zip_code: document.getElementById('cf-zip').value || null,
|
||||
account_number: document.getElementById('cf-account').value || null,
|
||||
email: document.getElementById('cf-email').value || null,
|
||||
secondary_email: document.getElementById('cf-secondary-email').value || null,
|
||||
phone: document.getElementById('cf-phone').value || null,
|
||||
phone2: null,
|
||||
phone2: document.getElementById('cf-phone2').value || null,
|
||||
taxable: document.getElementById('cf-taxable').checked,
|
||||
remarks: document.getElementById('cf-remarks').value || null
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user