Fix email signature layout and enhance email thread fetching
- Move logo to top of email signature for better formatting - Enhance IMAP search to include CC/BCC fields and multiple folders - Fix EOI document generation to properly send and extract signing URLs - Update documentation with all email system fixes
This commit is contained in:
parent
77b6aa2752
commit
f593036d0f
|
|
@ -253,14 +253,14 @@ const getSignaturePreview = () => {
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div style="margin-top: 20px;">
|
<div style="margin-top: 20px;">
|
||||||
|
<img src="/Port_Nimara_Logo_2_Colour_New_Transparent.png" alt="Port Nimara" style="height: 60px; max-width: 200px; margin-bottom: 15px;">
|
||||||
|
<br>
|
||||||
<div style="font-weight: bold;">${sig.name || 'Your Name'}</div>
|
<div style="font-weight: bold;">${sig.name || 'Your Name'}</div>
|
||||||
<div style="color: #666; margin-bottom: 8px;">${sig.title || 'Your Title'}</div>
|
<div style="color: #666; margin-bottom: 8px;">${sig.title || 'Your Title'}</div>
|
||||||
<div style="font-weight: bold; margin-bottom: 12px;">${sig.company || 'Company Name'}</div>
|
<div style="font-weight: bold; margin-bottom: 12px;">${sig.company || 'Company Name'}</div>
|
||||||
${contactLines ? contactLines + '<br>' : ''}
|
${contactLines ? contactLines + '<br>' : ''}
|
||||||
<a href="mailto:${userEmail}" style="color: #0066cc;">${userEmail}</a>
|
<a href="mailto:${userEmail}" style="color: #0066cc;">${userEmail}</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="/Port_Nimara_Logo_2_Colour_New_Transparent.png" alt="Port Nimara" style="height: 60px; max-width: 200px;">
|
|
||||||
<br>
|
|
||||||
<div style="color: #666; font-size: 12px; margin-top: 10px;">
|
<div style="color: #666; font-size: 12px; margin-top: 10px;">
|
||||||
The information in this message is confidential and may be privileged.<br>
|
The information in this message is confidential and may be privileged.<br>
|
||||||
It is intended for the addressee alone.<br>
|
It is intended for the addressee alone.<br>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,27 @@
|
||||||
- Updated `update-interest.ts` API to accept both x-tag headers ("094ut234" and "pjnvü1230")
|
- Updated `update-interest.ts` API to accept both x-tag headers ("094ut234" and "pjnvü1230")
|
||||||
- Now both authenticated and unauthenticated users can save interest updates
|
- Now both authenticated and unauthenticated users can save interest updates
|
||||||
|
|
||||||
|
### 5. Email Signature Formatting
|
||||||
|
- **Problem**: Logo was appearing below the signature details
|
||||||
|
- **Solution**:
|
||||||
|
- Moved Port Nimara logo to the top of the signature
|
||||||
|
- Logo now appears above the name with proper spacing
|
||||||
|
|
||||||
|
### 6. Email Refresh Not Showing New Emails
|
||||||
|
- **Problem**: New emails from clients weren't appearing after refresh
|
||||||
|
- **Solution**:
|
||||||
|
- Enhanced IMAP search to include CC and BCC fields
|
||||||
|
- Now searches in multiple folders: INBOX, Sent, Sent Items, Sent Mail
|
||||||
|
- Better email detection for comprehensive thread retrieval
|
||||||
|
|
||||||
|
### 7. EOI Document Generation Issues
|
||||||
|
- **Problem**: EOI documents were created but stuck in draft status with non-working links
|
||||||
|
- **Solution**:
|
||||||
|
- Fixed response structure to match actual Documenso API response
|
||||||
|
- Added proper document send step to move from draft to active
|
||||||
|
- Changed `sendEmail` to `true` to ensure recipients receive signing emails
|
||||||
|
- Correctly extract signing URLs from the response
|
||||||
|
|
||||||
## Required Environment Variables
|
## Required Environment Variables
|
||||||
|
|
||||||
Make sure these are set in your `.env` file:
|
Make sure these are set in your `.env` file:
|
||||||
|
|
|
||||||
|
|
@ -163,30 +163,53 @@ async function fetchImapEmails(
|
||||||
};
|
};
|
||||||
|
|
||||||
imap.once('ready', () => {
|
imap.once('ready', () => {
|
||||||
imap.openBox('INBOX', true, (err, box) => {
|
// Search in both INBOX and Sent folders
|
||||||
if (err) {
|
const foldersToSearch = ['INBOX', 'Sent', 'Sent Items', 'Sent Mail'];
|
||||||
|
let currentFolderIndex = 0;
|
||||||
|
const allEmails: EmailMessage[] = [];
|
||||||
|
|
||||||
|
const searchNextFolder = () => {
|
||||||
|
if (currentFolderIndex >= foldersToSearch.length) {
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(err);
|
resolve(allEmails);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const folderName = foldersToSearch[currentFolderIndex];
|
||||||
|
currentFolderIndex++;
|
||||||
|
|
||||||
|
imap.openBox(folderName, true, (err, box) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(`Folder ${folderName} not found, trying next...`);
|
||||||
|
searchNextFolder();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Searching in folder: ${folderName}`);
|
||||||
|
|
||||||
|
// Search for emails both sent and received with this client
|
||||||
const searchCriteria = [
|
const searchCriteria = [
|
||||||
['OR', ['FROM', clientEmail], ['TO', clientEmail]]
|
'OR',
|
||||||
|
['FROM', clientEmail],
|
||||||
|
['TO', clientEmail],
|
||||||
|
['CC', clientEmail],
|
||||||
|
['BCC', clientEmail]
|
||||||
];
|
];
|
||||||
|
|
||||||
imap.search(searchCriteria, (err, results) => {
|
imap.search(searchCriteria, (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
cleanup();
|
console.error(`Search error in ${folderName}:`, err);
|
||||||
reject(err);
|
searchNextFolder();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!results || results.length === 0) {
|
if (!results || results.length === 0) {
|
||||||
cleanup();
|
console.log(`No emails found in ${folderName}`);
|
||||||
resolve(emails);
|
searchNextFolder();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${results.length} emails in ${folderName}`);
|
||||||
const messagesToFetch = results.slice(-limit);
|
const messagesToFetch = results.slice(-limit);
|
||||||
let messagesProcessed = 0;
|
let messagesProcessed = 0;
|
||||||
|
|
||||||
|
|
@ -203,8 +226,7 @@ async function fetchImapEmails(
|
||||||
console.error('Parse error:', err);
|
console.error('Parse error:', err);
|
||||||
messagesProcessed++;
|
messagesProcessed++;
|
||||||
if (messagesProcessed === messagesToFetch.length) {
|
if (messagesProcessed === messagesToFetch.length) {
|
||||||
cleanup();
|
searchNextFolder();
|
||||||
resolve(emails);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -219,19 +241,18 @@ async function fetchImapEmails(
|
||||||
body: parsed.text || '',
|
body: parsed.text || '',
|
||||||
html: parsed.html || undefined,
|
html: parsed.html || undefined,
|
||||||
timestamp: parsed.date?.toISOString() || new Date().toISOString(),
|
timestamp: parsed.date?.toISOString() || new Date().toISOString(),
|
||||||
direction: parsed.from?.text.includes(userEmail) ? 'sent' : 'received'
|
direction: parsed.from?.text.toLowerCase().includes(userEmail.toLowerCase()) ? 'sent' : 'received'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (parsed.headers.has('in-reply-to')) {
|
if (parsed.headers.has('in-reply-to')) {
|
||||||
email.threadId = parsed.headers.get('in-reply-to') as string;
|
email.threadId = parsed.headers.get('in-reply-to') as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
emails.push(email);
|
allEmails.push(email);
|
||||||
messagesProcessed++;
|
messagesProcessed++;
|
||||||
|
|
||||||
if (messagesProcessed === messagesToFetch.length) {
|
if (messagesProcessed === messagesToFetch.length) {
|
||||||
cleanup();
|
searchNextFolder();
|
||||||
resolve(emails);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -239,18 +260,19 @@ async function fetchImapEmails(
|
||||||
|
|
||||||
fetch.once('error', (err) => {
|
fetch.once('error', (err) => {
|
||||||
console.error('Fetch error:', err);
|
console.error('Fetch error:', err);
|
||||||
cleanup();
|
searchNextFolder();
|
||||||
reject(err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fetch.once('end', () => {
|
fetch.once('end', () => {
|
||||||
if (messagesProcessed === 0) {
|
if (messagesProcessed === 0) {
|
||||||
cleanup();
|
searchNextFolder();
|
||||||
resolve(emails);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
searchNextFolder();
|
||||||
});
|
});
|
||||||
|
|
||||||
imap.once('error', (err: any) => {
|
imap.once('error', (err: any) => {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,16 @@ interface DocumensoRecipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DocumensoResponse {
|
interface DocumensoResponse {
|
||||||
id: string;
|
documentId: number;
|
||||||
recipients: DocumensoRecipient[];
|
recipients: Array<{
|
||||||
|
recipientId: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
token: string;
|
||||||
|
role: 'SIGNER' | 'APPROVER';
|
||||||
|
signingOrder: number;
|
||||||
|
signingUrl: string;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
|
@ -186,27 +194,33 @@ export default defineEventHandler(async (event) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Setup completion emails
|
// 3. Send document (moves from draft to active and sends emails)
|
||||||
try {
|
try {
|
||||||
const completionResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documentResponse.id}/send`, {
|
const sendResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documentResponse.documentId}/send`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${documensoApiKey}`
|
'Authorization': `Bearer ${documensoApiKey}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
sendEmail: false,
|
sendEmail: true,
|
||||||
sendCompletionEmails: true
|
sendCompletionEmails: true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!completionResponse.ok) {
|
if (!sendResponse.ok) {
|
||||||
console.error('Failed to setup completion emails:', await completionResponse.text());
|
const errorText = await sendResponse.text();
|
||||||
// Don't fail the whole process if this fails
|
console.error('Failed to send document:', errorText);
|
||||||
|
throw new Error(`Failed to send document: ${sendResponse.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Document sent successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Completion email setup error:', error);
|
console.error('Document send error:', error);
|
||||||
// Continue anyway
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: "Document created but failed to send. Please check Documenso dashboard."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract signing URLs from recipients
|
// Extract signing URLs from recipients
|
||||||
|
|
@ -261,7 +275,7 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
documentId: documentResponse.id,
|
documentId: documentResponse.documentId,
|
||||||
clientSigningUrl: signingLinks['Client'] || '',
|
clientSigningUrl: signingLinks['Client'] || '',
|
||||||
signingLinks: signingLinks
|
signingLinks: signingLinks
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue