cre-sync/lib/api/client.ts
BusyBee3333 4e6467ffb0 Add CRESync CRM application with Setup page
- Build complete Next.js CRM for commercial real estate
- Add authentication with JWT sessions and role-based access
- Add GoHighLevel API integration for contacts, conversations, opportunities
- Add AI-powered Control Center with tool calling
- Add Setup page with onboarding checklist (/setup)
- Add sidebar navigation with Setup menu item
- Fix type errors in onboarding API, GHL services, and control center tools
- Add Prisma schema with SQLite for local development
- Add UI components with clay morphism design system

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 17:30:55 -05:00

140 lines
5.3 KiB
TypeScript

const API_BASE = '/api/v1';
class APIClient {
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const response = await fetch(`${API_BASE}${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new APIError(response.status, error.error || 'Request failed');
}
return response.json();
}
private get<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
const url = params
? `${endpoint}?${new URLSearchParams(params)}`
: endpoint;
return this.request(url);
}
private post<T>(endpoint: string, body?: unknown): Promise<T> {
return this.request(endpoint, {
method: 'POST',
body: body ? JSON.stringify(body) : undefined,
});
}
private put<T>(endpoint: string, body?: unknown): Promise<T> {
return this.request(endpoint, {
method: 'PUT',
body: body ? JSON.stringify(body) : undefined,
});
}
private delete<T>(endpoint: string): Promise<T> {
return this.request(endpoint, { method: 'DELETE' });
}
// Auth
auth = {
login: (email: string, password: string) =>
this.post<{ user: any; token: string }>('/auth/login', { email, password }),
signup: (data: { email: string; password: string; firstName: string; lastName: string }) =>
this.post<{ user: any; token: string }>('/auth/signup', data),
logout: () => this.post('/auth/logout'),
me: () => this.get<{ user: any }>('/auth/me'),
updateProfile: (data: { firstName?: string; lastName?: string; brokerage?: string }) =>
this.put('/auth/me', data),
};
// Contacts
contacts = {
getAll: (params?: { limit?: number; offset?: number; query?: string }) =>
this.get<{ data: any[]; meta: any }>('/contacts', params as any),
getById: (id: string) => this.get<any>(`/contacts/${id}`),
create: (data: any) => this.post<any>('/contacts', data),
update: (id: string, data: any) => this.put<any>(`/contacts/${id}`, data),
delete: (id: string) => this.delete(`/contacts/${id}`),
addTags: (id: string, tags: string[]) => this.post(`/contacts/${id}/tags`, { tags }),
removeTags: (id: string, tags: string[]) => this.delete(`/contacts/${id}/tags`),
addToWorkflow: (contactId: string, workflowId: string) =>
this.post(`/contacts/${contactId}/workflow/${workflowId}`),
};
// Conversations
conversations = {
getAll: (params?: { limit?: number; status?: string; contactId?: string }) =>
this.get<{ data: any[] }>('/conversations', params as any),
getById: (id: string) => this.get<any>(`/conversations/${id}`),
getMessages: (id: string, limit = 50) =>
this.get<{ data: any[] }>(`/conversations/${id}/messages`, { limit: String(limit) }),
sendSMS: (contactId: string, message: string) =>
this.post('/conversations/send', { type: 'SMS', contactId, message }),
sendEmail: (contactId: string, subject: string, htmlBody: string) =>
this.post('/conversations/send', { type: 'EMAIL', contactId, subject, htmlBody }),
markAsRead: (id: string) => this.put(`/conversations/${id}/status`, { status: 'read' }),
delete: (id: string) => this.delete(`/conversations/${id}`),
};
// Opportunities
opportunities = {
getAll: (params?: { pipelineId?: string; stageId?: string; status?: string }) =>
this.get<{ data: any[] }>('/opportunities', params as any),
getById: (id: string) => this.get<any>(`/opportunities/${id}`),
create: (data: any) => this.post<any>('/opportunities', data),
update: (id: string, data: any) => this.put<any>(`/opportunities/${id}`, data),
delete: (id: string) => this.delete(`/opportunities/${id}`),
moveToStage: (id: string, stageId: string) =>
this.put(`/opportunities/${id}`, { pipelineStageId: stageId }),
};
// Pipelines
pipelines = {
getAll: () => this.get<{ pipelines: any[] }>('/opportunities/pipelines'),
getById: (id: string) => this.get<any>(`/opportunities/pipelines/${id}`),
create: (data: { name: string; stages: { name: string }[] }) =>
this.post('/opportunities/pipelines', data),
addStage: (pipelineId: string, name: string) =>
this.post(`/opportunities/pipelines/${pipelineId}/stages`, { name }),
};
// Onboarding
onboarding = {
get: () => this.get<{ onboarding: any }>('/onboarding'),
save: (data: any) => this.post('/onboarding', data),
getStatus: () => this.get<{ status: any }>('/onboarding/status'),
updateStatus: (data: any) => this.put('/onboarding/status', data),
};
// Admin
admin = {
getStats: () => this.get<{ stats: any }>('/admin/stats'),
getUsers: (params?: { page?: number; limit?: number; search?: string; filter?: string }) =>
this.get<{ users: any[]; pagination: any }>('/users', params as any),
getSettings: () => this.get<{ settings: any }>('/admin/settings'),
updateSettings: (data: any) => this.put('/admin/settings', data),
testConnection: (service: 'ghl' | 'stripe') =>
this.post<{ success: boolean; error?: string }>('/admin/settings/test', { service }),
};
}
export class APIError extends Error {
constructor(public status: number, message: string) {
super(message);
this.name = 'APIError';
}
}
export const api = new APIClient();