add email threads to companies
This commit is contained in:
100
src/services/company-email.service.js
Normal file
100
src/services/company-email.service.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { and, desc, eq, inArray } from 'drizzle-orm';
|
||||
import { db } from '../config/database.js';
|
||||
import { contacts, emailAccounts, emails, userEmailAccounts } from '../db/schema.js';
|
||||
|
||||
export const getCompanyEmailThreads = async (companyId, userId) => {
|
||||
const accountLinks = await db
|
||||
.select({
|
||||
id: emailAccounts.id,
|
||||
email: emailAccounts.email,
|
||||
isActive: emailAccounts.isActive,
|
||||
})
|
||||
.from(userEmailAccounts)
|
||||
.innerJoin(emailAccounts, eq(userEmailAccounts.emailAccountId, emailAccounts.id))
|
||||
.where(
|
||||
and(
|
||||
eq(userEmailAccounts.userId, userId),
|
||||
eq(emailAccounts.isActive, true)
|
||||
)
|
||||
);
|
||||
|
||||
const accountIds = accountLinks.map((acc) => acc.id);
|
||||
|
||||
if (accountIds.length === 0) {
|
||||
return { accounts: [], threads: [] };
|
||||
}
|
||||
|
||||
const rows = await db
|
||||
.select({
|
||||
threadId: emails.threadId,
|
||||
emailAccountId: emails.emailAccountId,
|
||||
accountEmail: emailAccounts.email,
|
||||
subject: emails.subject,
|
||||
date: emails.date,
|
||||
isRead: emails.isRead,
|
||||
from: emails.from,
|
||||
to: emails.to,
|
||||
contactId: contacts.id,
|
||||
contactEmail: contacts.email,
|
||||
contactName: contacts.name,
|
||||
})
|
||||
.from(emails)
|
||||
.innerJoin(emailAccounts, eq(emails.emailAccountId, emailAccounts.id))
|
||||
.leftJoin(contacts, eq(emails.contactId, contacts.id))
|
||||
.where(
|
||||
and(
|
||||
inArray(emails.emailAccountId, accountIds),
|
||||
eq(emails.companyId, companyId)
|
||||
)
|
||||
)
|
||||
.orderBy(desc(emails.date));
|
||||
|
||||
const threadsMap = new Map();
|
||||
|
||||
for (const row of rows) {
|
||||
const threadKey = `${row.emailAccountId}:${row.threadId}`;
|
||||
const lastDate = row.date ? new Date(row.date) : null;
|
||||
const existing = threadsMap.get(threadKey);
|
||||
|
||||
if (!existing) {
|
||||
threadsMap.set(threadKey, {
|
||||
threadId: row.threadId,
|
||||
emailAccountId: row.emailAccountId,
|
||||
accountEmail: row.accountEmail,
|
||||
contactId: row.contactId,
|
||||
contactEmail: row.contactEmail,
|
||||
contactName: row.contactName,
|
||||
subject: row.subject,
|
||||
lastFrom: row.from,
|
||||
lastTo: row.to,
|
||||
lastMessageAt: lastDate,
|
||||
unreadCount: row.isRead ? 0 : 1,
|
||||
totalCount: 1,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
existing.totalCount += 1;
|
||||
if (!row.isRead) {
|
||||
existing.unreadCount += 1;
|
||||
}
|
||||
|
||||
if (lastDate && (!existing.lastMessageAt || lastDate > existing.lastMessageAt)) {
|
||||
existing.lastMessageAt = lastDate;
|
||||
existing.subject = row.subject;
|
||||
existing.lastFrom = row.from;
|
||||
existing.lastTo = row.to;
|
||||
}
|
||||
}
|
||||
|
||||
const threads = Array.from(threadsMap.values()).sort((a, b) => {
|
||||
const aTime = a.lastMessageAt ? a.lastMessageAt.getTime() : 0;
|
||||
const bTime = b.lastMessageAt ? b.lastMessageAt.getTime() : 0;
|
||||
return bTime - aTime;
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: accountLinks,
|
||||
threads,
|
||||
};
|
||||
};
|
||||
@@ -88,7 +88,10 @@ export const addContact = async (emailAccountId, jmapConfig, email, name = '', n
|
||||
for (const emailToReassign of emailsToReassign) {
|
||||
await db
|
||||
.update(emails)
|
||||
.set({ contactId: newContact.id })
|
||||
.set({
|
||||
contactId: newContact.id,
|
||||
companyId: newContact.companyId || null,
|
||||
})
|
||||
.where(eq(emails.id, emailToReassign.id));
|
||||
}
|
||||
|
||||
@@ -172,6 +175,15 @@ export const linkCompanyToContact = async (contactId, emailAccountId, companyId)
|
||||
.where(eq(contacts.id, contactId))
|
||||
.returning();
|
||||
|
||||
// Propagate company assignment to existing emails for this contact
|
||||
await db
|
||||
.update(emails)
|
||||
.set({
|
||||
companyId,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(emails.contactId, contactId));
|
||||
|
||||
return updated;
|
||||
};
|
||||
|
||||
@@ -190,6 +202,15 @@ export const unlinkCompanyFromContact = async (contactId, emailAccountId) => {
|
||||
.where(eq(contacts.id, contactId))
|
||||
.returning();
|
||||
|
||||
// Remove company assignment from existing emails for this contact
|
||||
await db
|
||||
.update(emails)
|
||||
.set({
|
||||
companyId: null,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(emails.contactId, contactId));
|
||||
|
||||
return updated;
|
||||
};
|
||||
|
||||
@@ -239,6 +260,15 @@ export const createCompanyFromContact = async (contactId, emailAccountId, userId
|
||||
.where(eq(contacts.id, contactId))
|
||||
.returning();
|
||||
|
||||
// Update existing emails for this contact to reference the new company
|
||||
await db
|
||||
.update(emails)
|
||||
.set({
|
||||
companyId: newCompany.id,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(emails.contactId, contactId));
|
||||
|
||||
return {
|
||||
company: newCompany,
|
||||
contact: updatedContact,
|
||||
|
||||
@@ -22,6 +22,7 @@ export const getEmailsForAccount = async (emailAccountId) => {
|
||||
sentByUserId: emails.sentByUserId,
|
||||
date: emails.date,
|
||||
createdAt: emails.createdAt,
|
||||
companyId: emails.companyId,
|
||||
emailAccountId: emails.emailAccountId,
|
||||
contact: {
|
||||
id: contacts.id,
|
||||
|
||||
@@ -358,6 +358,12 @@ export const syncEmailsFromSender = async (
|
||||
try {
|
||||
logger.info(`Syncing emails with contact: ${senderEmail} for account ${emailAccountId}`);
|
||||
|
||||
const [contact] = await db
|
||||
.select({ companyId: contacts.companyId })
|
||||
.from(contacts)
|
||||
.where(eq(contacts.id, contactId))
|
||||
.limit(1);
|
||||
|
||||
// Get Inbox and Sent mailboxes ONLY
|
||||
const mailboxes = await getMailboxes(jmapConfig);
|
||||
const inboxMailbox = mailboxes.find(m => m.role === 'inbox' || m.name === 'Inbox' || m.name === 'INBOX');
|
||||
@@ -510,6 +516,7 @@ export const syncEmailsFromSender = async (
|
||||
await db.insert(emails).values({
|
||||
emailAccountId,
|
||||
contactId,
|
||||
companyId: contact?.companyId || null,
|
||||
jmapId: email.id,
|
||||
messageId,
|
||||
threadId: email.threadId || messageId,
|
||||
@@ -722,6 +729,7 @@ export const sendEmail = async (jmapConfig, userId, emailAccountId, to, subject,
|
||||
await db.insert(emails).values({
|
||||
emailAccountId,
|
||||
contactId: recipientContact?.id || null, // Link to contact if recipient is in contacts
|
||||
companyId: recipientContact?.companyId || null,
|
||||
jmapId: createdEmailId,
|
||||
messageId,
|
||||
threadId: threadId || messageId,
|
||||
|
||||
Reference in New Issue
Block a user