Add file attachment support to email composer

- Add file browser integration for selecting attachments
- Display attached files with remove functionality in composer
- Include attachments in email sending process
- Show attachment info in email thread display
- Update server endpoints to handle file attachments
- Minor UI improvements to EOI status badge layout
This commit is contained in:
2025-06-10 02:39:00 +02:00
parent bb2f5d37c8
commit b332f913a6
5 changed files with 134 additions and 51 deletions

View File

@@ -157,9 +157,9 @@ export default defineEventHandler(async (event) => {
new Map(allEmails.map(email => [email.id, email])).values()
);
// Sort by timestamp
// Sort by timestamp (newest first)
uniqueEmails.sort((a, b) =>
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
);
// Group into threads
@@ -414,12 +414,14 @@ function groupIntoThreads(emails: EmailMessage[]): any[] {
}
});
// Convert to array format
return Array.from(threads.entries()).map(([threadId, emails]) => ({
id: threadId,
subject: emails[0].subject,
emailCount: emails.length,
latestTimestamp: emails[emails.length - 1].timestamp,
emails: emails
}));
// Convert to array format and sort threads by latest timestamp (newest first)
return Array.from(threads.entries())
.map(([threadId, emails]) => ({
id: threadId,
subject: emails[0].subject,
emailCount: emails.length,
latestTimestamp: emails[0].timestamp, // First email is newest since we sorted desc
emails: emails
}))
.sort((a, b) => new Date(b.latestTimestamp).getTime() - new Date(a.latestTimestamp).getTime());
}

View File

@@ -1,6 +1,6 @@
import nodemailer from 'nodemailer';
import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption';
import { uploadFile } from '~/server/utils/minio';
import { uploadFile, getMinioClient } from '~/server/utils/minio';
import { updateInterest } from '~/server/utils/nocodb';
export default defineEventHandler(async (event) => {
@@ -19,7 +19,8 @@ export default defineEventHandler(async (event) => {
interestId,
sessionId,
includeSignature = true,
signatureConfig
signatureConfig,
attachments = []
} = body;
if (!to || !subject || !emailBody || !sessionId) {
@@ -82,6 +83,36 @@ export default defineEventHandler(async (event) => {
}
});
// Prepare email attachments
const emailAttachments = [];
if (attachments && attachments.length > 0) {
const client = getMinioClient();
for (const attachment of attachments) {
try {
// Download file from MinIO
const stream = await client.getObject('portnimaradev', attachment.path);
const chunks: Buffer[] = [];
await new Promise((resolve, reject) => {
stream.on('data', (chunk) => chunks.push(chunk));
stream.on('end', resolve);
stream.on('error', reject);
});
const content = Buffer.concat(chunks);
emailAttachments.push({
filename: attachment.name,
content: content
});
} catch (error) {
console.error(`Failed to attach file ${attachment.name}:`, error);
// Continue with other attachments
}
}
}
// Send email
const fromName = sig.name || defaultName;
const info = await transporter.sendMail({
@@ -89,7 +120,8 @@ export default defineEventHandler(async (event) => {
to: to,
subject: subject,
text: emailBody, // Plain text version
html: htmlBody // HTML version with signature
html: htmlBody, // HTML version with signature
attachments: emailAttachments
});
// Store email in MinIO for thread history and update EOI Time Sent