import { Elysia, t } from 'elysia'; import { db } from '../db'; import { clients, events, communications, interactions } from '../db/schema'; import { eq, and, desc } from 'drizzle-orm'; import type { User } from '../lib/auth'; export interface ActivityItem { id: string; type: 'email_sent' | 'email_drafted' | 'event_created' | 'client_contacted' | 'client_created' | 'client_updated' | 'interaction'; title: string; description?: string; date: string; metadata?: Record; } export const activityRoutes = new Elysia({ prefix: '/clients' }) // Get activity timeline for a client .get('/:id/activity', async ({ params, user }: { params: { id: string }; user: User }) => { // Verify client belongs to user const [client] = await db.select() .from(clients) .where(and(eq(clients.id, params.id), eq(clients.userId, user.id))) .limit(1); if (!client) { throw new Error('Client not found'); } const activities: ActivityItem[] = []; // Client creation activities.push({ id: `created-${client.id}`, type: 'client_created', title: 'Client added to network', date: client.createdAt.toISOString(), }); // Client updated (if different from created) if (client.updatedAt.getTime() - client.createdAt.getTime() > 60000) { activities.push({ id: `updated-${client.id}`, type: 'client_updated', title: 'Client profile updated', date: client.updatedAt.toISOString(), }); } // Last contacted if (client.lastContactedAt) { activities.push({ id: `contacted-${client.id}`, type: 'client_contacted', title: 'Marked as contacted', date: client.lastContactedAt.toISOString(), }); } // Communications (emails) const comms = await db.select() .from(communications) .where(and( eq(communications.clientId, params.id), eq(communications.userId, user.id), )) .orderBy(desc(communications.createdAt)); for (const comm of comms) { if (comm.status === 'sent' && comm.sentAt) { activities.push({ id: `email-sent-${comm.id}`, type: 'email_sent', title: `Email sent: ${comm.subject || 'No subject'}`, description: comm.content.substring(0, 150) + (comm.content.length > 150 ? '...' : ''), date: comm.sentAt.toISOString(), metadata: { emailId: comm.id, aiGenerated: comm.aiGenerated }, }); } // Also show drafts if (comm.status === 'draft') { activities.push({ id: `email-draft-${comm.id}`, type: 'email_drafted', title: `Email drafted: ${comm.subject || 'No subject'}`, description: comm.content.substring(0, 150) + (comm.content.length > 150 ? '...' : ''), date: comm.createdAt.toISOString(), metadata: { emailId: comm.id, aiGenerated: comm.aiGenerated }, }); } } // Events const clientEvents = await db.select() .from(events) .where(and( eq(events.clientId, params.id), eq(events.userId, user.id), )) .orderBy(desc(events.createdAt)); for (const event of clientEvents) { activities.push({ id: `event-${event.id}`, type: 'event_created', title: `Event: ${event.title}`, description: `${event.type}${event.recurring ? ' (recurring)' : ''}`, date: event.createdAt.toISOString(), metadata: { eventId: event.id, eventType: event.type, eventDate: event.date.toISOString() }, }); } // Interactions const clientInteractions = await db.select() .from(interactions) .where(and( eq(interactions.clientId, params.id), eq(interactions.userId, user.id), )) .orderBy(desc(interactions.contactedAt)); for (const interaction of clientInteractions) { const typeLabels: Record = { call: '📞 Phone Call', meeting: '🤝 Meeting', email: '✉️ Email', note: '📝 Note', other: '📌 Interaction', }; activities.push({ id: `interaction-${interaction.id}`, type: 'interaction', title: `${typeLabels[interaction.type] || typeLabels.other}: ${interaction.title}`, description: interaction.description || undefined, date: interaction.contactedAt.toISOString(), metadata: { interactionId: interaction.id, interactionType: interaction.type, duration: interaction.duration, }, }); } // Sort by date descending activities.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); return activities; }, { params: t.Object({ id: t.String({ format: 'uuid' }), }), });