feat: add CSV import, activity timeline, and AI insights widget
- CSV Import: modal with file picker, auto column mapping, preview table, import progress - Activity Timeline: new tab on client detail showing all communications, events, status changes - AI Insights Widget: dashboard card showing stale clients, upcoming birthdays, suggested follow-ups - Import button on Clients page header
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Profile, Client, ClientCreate, Event, EventCreate, Email, EmailGenerate, User, Invite } from '@/types';
|
||||
import type { Profile, Client, ClientCreate, Event, EventCreate, Email, EmailGenerate, User, Invite, ActivityItem, InsightsData, ImportPreview, ImportResult, NetworkMatch, NetworkStats } from '@/types';
|
||||
|
||||
const API_BASE = import.meta.env.PROD
|
||||
? 'https://api.thenetwork.donovankelly.xyz/api'
|
||||
@@ -157,6 +157,54 @@ class ApiClient {
|
||||
return this.fetch(`/clients/${id}/contacted`, { method: 'POST' });
|
||||
}
|
||||
|
||||
// CSV Import
|
||||
async importPreview(file: File): Promise<ImportPreview> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const token = this.getToken();
|
||||
const headers: HeadersInit = token ? { Authorization: `Bearer ${token}` } : {};
|
||||
const response = await fetch(`${API_BASE}/clients/import/preview`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData,
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ error: 'Preview failed' }));
|
||||
throw new Error(error.error || 'Preview failed');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async importClients(file: File, mapping: Record<number, string>): Promise<ImportResult> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('mapping', JSON.stringify(mapping));
|
||||
const token = this.getToken();
|
||||
const headers: HeadersInit = token ? { Authorization: `Bearer ${token}` } : {};
|
||||
const response = await fetch(`${API_BASE}/clients/import`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData,
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ error: 'Import failed' }));
|
||||
throw new Error(error.error || 'Import failed');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Activity Timeline
|
||||
async getClientActivity(clientId: string): Promise<ActivityItem[]> {
|
||||
return this.fetch(`/clients/${clientId}/activity`);
|
||||
}
|
||||
|
||||
// Insights
|
||||
async getInsights(): Promise<InsightsData> {
|
||||
return this.fetch('/insights');
|
||||
}
|
||||
|
||||
// Events
|
||||
async getEvents(params?: { clientId?: string; type?: string; upcoming?: number }): Promise<Event[]> {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
Reference in New Issue
Block a user