- 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>
140 lines
5.3 KiB
TypeScript
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();
|