feat: real notifications, interaction logging, bulk email compose
Some checks failed
CI/CD / test (push) Failing after 20s
CI/CD / deploy (push) Has been skipped

This commit is contained in:
2026-01-30 00:48:13 +00:00
parent b43bdf3c71
commit 691e8170f3
8 changed files with 863 additions and 118 deletions

View File

@@ -1,4 +1,4 @@
import type { Profile, Client, ClientCreate, ClientNote, Event, EventCreate, Email, EmailGenerate, User, Invite, ActivityItem, InsightsData, ImportPreview, ImportResult, NetworkMatch, NetworkStats } from '@/types';
import type { Profile, Client, ClientCreate, ClientNote, Event, EventCreate, Email, EmailGenerate, User, Invite, ActivityItem, InsightsData, ImportPreview, ImportResult, NetworkMatch, NetworkStats, Notification, Interaction, BulkEmailResult } from '@/types';
const API_BASE = import.meta.env.PROD
? 'https://api.thenetwork.donovankelly.xyz/api'
@@ -448,10 +448,78 @@ class ApiClient {
return this.fetch('/reports/engagement');
}
async getNotifications(): Promise<any> {
async getNotificationsLegacy(): Promise<any> {
return this.fetch('/reports/notifications');
}
// Real notifications (from notifications table)
async getNotifications(params?: { unreadOnly?: boolean; limit?: number }): Promise<{ notifications: Notification[]; unreadCount: number }> {
const searchParams = new URLSearchParams();
if (params?.unreadOnly) searchParams.set('unreadOnly', 'true');
if (params?.limit) searchParams.set('limit', String(params.limit));
const query = searchParams.toString();
return this.fetch(`/notifications${query ? `?${query}` : ''}`);
}
async markNotificationRead(id: string): Promise<Notification> {
return this.fetch(`/notifications/${id}/read`, { method: 'PUT' });
}
async markAllNotificationsRead(): Promise<void> {
await this.fetch('/notifications/mark-all-read', { method: 'POST' });
}
async deleteNotification(id: string): Promise<void> {
await this.fetch(`/notifications/${id}`, { method: 'DELETE' });
}
// Interactions
async getClientInteractions(clientId: string): Promise<Interaction[]> {
return this.fetch(`/clients/${clientId}/interactions`);
}
async createInteraction(clientId: string, data: {
type: string; title: string; description?: string; duration?: number; contactedAt: string;
}): Promise<Interaction> {
return this.fetch(`/clients/${clientId}/interactions`, {
method: 'POST',
body: JSON.stringify(data),
});
}
async updateInteraction(id: string, data: Partial<{
type: string; title: string; description?: string; duration?: number; contactedAt: string;
}>): Promise<Interaction> {
return this.fetch(`/interactions/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async deleteInteraction(id: string): Promise<void> {
await this.fetch(`/interactions/${id}`, { method: 'DELETE' });
}
async getRecentInteractions(limit?: number): Promise<Interaction[]> {
const query = limit ? `?limit=${limit}` : '';
return this.fetch(`/interactions/recent${query}`);
}
// Bulk Email
async bulkGenerateEmails(clientIds: string[], purpose: string, provider?: 'anthropic' | 'openai'): Promise<BulkEmailResult> {
return this.fetch('/emails/bulk-generate', {
method: 'POST',
body: JSON.stringify({ clientIds, purpose, provider }),
});
}
async bulkSendEmails(batchId: string): Promise<{ batchId: string; total: number; sent: number; failed: number }> {
return this.fetch('/emails/bulk-send', {
method: 'POST',
body: JSON.stringify({ batchId }),
});
}
async exportClientsCSV(): Promise<void> {
const token = this.getToken();
const headers: HeadersInit = token ? { Authorization: `Bearer ${token}` } : {};