cre-sync/lib/ghl/services/contacts.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

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);
}
}