- 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>
121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import { GHLClient } from '../client';
|
|
import {
|
|
GHLContact,
|
|
CreateContactDTO,
|
|
UpdateContactDTO,
|
|
GHLPaginatedResponse
|
|
} from '@/types/ghl';
|
|
|
|
export class ContactsService {
|
|
constructor(private client: GHLClient) {}
|
|
|
|
// Get all contacts with pagination
|
|
async getAll(params?: {
|
|
limit?: number;
|
|
offset?: number;
|
|
query?: string;
|
|
locationId?: string;
|
|
}): Promise<GHLPaginatedResponse<GHLContact>> {
|
|
const searchParams: Record<string, string> = {
|
|
locationId: params?.locationId || this.client.locationID,
|
|
limit: String(params?.limit || 20),
|
|
};
|
|
if (params?.offset) searchParams.startAfterId = String(params.offset);
|
|
if (params?.query) searchParams.query = params.query;
|
|
|
|
// v1 API returns { contacts: [...], meta: {...} }
|
|
const response = await this.client.get<{ contacts: GHLContact[]; meta: any }>('/contacts/', searchParams);
|
|
|
|
// Normalize to { data: [...], meta: {...} } format
|
|
return {
|
|
data: response.contacts || [],
|
|
meta: response.meta,
|
|
};
|
|
}
|
|
|
|
// Get a single contact by ID
|
|
async getById(contactId: string): Promise<GHLContact> {
|
|
// v1 API returns { contact: {...} }
|
|
const response = await this.client.get<{ contact: GHLContact }>(`/contacts/${contactId}`);
|
|
return response.contact;
|
|
}
|
|
|
|
// Create a new contact
|
|
async create(data: CreateContactDTO): Promise<GHLContact> {
|
|
return this.client.post('/contacts/', {
|
|
...data,
|
|
locationId: this.client.locationID,
|
|
});
|
|
}
|
|
|
|
// Update an existing contact
|
|
async update(contactId: string, data: UpdateContactDTO): Promise<GHLContact> {
|
|
return this.client.put(`/contacts/${contactId}`, data);
|
|
}
|
|
|
|
// Delete a contact
|
|
async delete(contactId: string): Promise<void> {
|
|
await this.client.delete(`/contacts/${contactId}`);
|
|
}
|
|
|
|
// Search contacts
|
|
async search(query: string, limit = 20): Promise<GHLPaginatedResponse<GHLContact>> {
|
|
return this.getAll({ query, limit });
|
|
}
|
|
|
|
// Add tags to a contact
|
|
async addTags(contactId: string, tags: string[]): Promise<GHLContact> {
|
|
return this.client.post(`/contacts/${contactId}/tags`, { tags });
|
|
}
|
|
|
|
// Remove tags from a contact
|
|
async removeTags(contactId: string, tags: string[]): Promise<GHLContact> {
|
|
return this.client.delete(`/contacts/${contactId}/tags`);
|
|
}
|
|
|
|
// Add contact to a workflow
|
|
async addToWorkflow(contactId: string, workflowId: string): Promise<void> {
|
|
await this.client.post(`/contacts/${contactId}/workflow/${workflowId}`, {});
|
|
}
|
|
|
|
// Remove contact from a workflow
|
|
async removeFromWorkflow(contactId: string, workflowId: string): Promise<void> {
|
|
await this.client.delete(`/contacts/${contactId}/workflow/${workflowId}`);
|
|
}
|
|
|
|
// Get contact by email
|
|
async getByEmail(email: string): Promise<GHLContact | null> {
|
|
const result = await this.search(email, 1);
|
|
return result.data?.[0] || null;
|
|
}
|
|
|
|
// Get contact by phone
|
|
async getByPhone(phone: string): Promise<GHLContact | null> {
|
|
const result = await this.search(phone, 1);
|
|
return result.data?.[0] || null;
|
|
}
|
|
|
|
// Bulk create contacts
|
|
async bulkCreate(contacts: CreateContactDTO[]): Promise<{ created: number; errors: any[] }> {
|
|
const results = { created: 0, errors: [] as any[] };
|
|
|
|
for (const contact of contacts) {
|
|
try {
|
|
await this.create(contact);
|
|
results.created++;
|
|
} catch (error) {
|
|
results.errors.push({ contact, error });
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// Update contact's custom field value
|
|
async updateCustomField(contactId: string, fieldKey: string, value: any): Promise<GHLContact> {
|
|
return this.update(contactId, {
|
|
customFields: [{ key: fieldKey, value }]
|
|
} as any);
|
|
}
|
|
}
|