@lp-ai/types
v1.0.3
Published
Shared TypeScript interfaces for the LPai App ecosystem. Used by both the React Native mobile app and Next.js backend.
Readme
@lpai/types
Shared TypeScript interfaces for the LPai App ecosystem. Used by both the React Native mobile app and Next.js backend.
Overview
This package contains all TypeScript interfaces that map to our MongoDB collections. The types are the single source of truth for data structures across all applications.
Key Principles
- MongoDB is the source of truth - All data is stored in MongoDB first
- Multi-tenant isolation - Every request must include
locationId - GHL sync is secondary - We sync TO GoHighLevel, not FROM it
- Types match the database - Interface fields map directly to MongoDB documents
All Available Interfaces
Core Business Objects
- Project - Jobs/opportunities with milestones, photos, documents, timeline
- Contact - Customers with full profile, tags, custom fields
- User - System users with preferences, permissions, GHL sync status
- Quote - Full proposals with sections, line items, signatures, activity tracking
- Payment - Payment records with proof photos, GHL invoice links
- Invoice - Complete GHL invoices with items, taxes, payment methods
Communication & CRM
- Message - All message types (email, SMS, etc.) with threading
- Conversation - Threaded conversations with unread counts
- Note - Internal notes linked to contacts/opportunities
- EmailStat - Detailed email tracking (delivered, opened, clicked)
Scheduling & Tasks
- Appointment - Calendar events with location types, resources, recurring
- Calendar - Calendar configurations with colors and permissions
- Task - Assignable tasks with due dates and contact details
Configuration & Multi-tenant
- Location - Complete tenant setup with OAuth, settings, social, counts
- Agency - Parent company managing multiple locations
- Template - Quote templates with blocks, styling, company overrides
- Library - Product catalogs with categories and pricing
- Tag - Contact tags with colors and slugs
- Pipeline - CRM pipeline stages configuration
Supporting Types
- QuoteSection, QuoteLineItem - Quote structure
- QuoteActivity, QuoteEmail - Quote tracking
- Milestone, ProjectPhoto, ProjectDocument - Project assets
- UserPreferences, DashboardWidget - User customization
- TemplateBlock types - All template content blocks
- SignedQuoteFile, PaymentProofFile - GridFS metadata
Common ID Fields
// Every document has these
_id: string; // MongoDB ObjectId
locationId: string; // REQUIRED for multi-tenancy
// GHL integration IDs
ghlContactId: string; // GoHighLevel contact ID
ghlOpportunityId: string; // GoHighLevel opportunity ID
ghlTaskId: string; // GoHighLevel task IDEntity Relationships
Location (Tenant)
├── Users
├── Contacts
│ └── Projects (Opportunities)
│ ├── Quotes
│ │ └── Payments
│ ├── Tasks
│ └── Appointments
└── Libraries (Product Catalogs)Common Patterns
Always Include LocationId
// ✅ CORRECT - Always filter by locationId
const projects = await api.get<Project[]>('/projects', {
params: { locationId: user.locationId }
});
// ❌ WRONG - Never fetch without locationId
const projects = await api.get<Project[]>('/projects');Type Guards
// Check if a quote is signed
function isQuoteSigned(quote: Quote): boolean {
return !!quote.signatures?.customer?.signature;
}
// Check if a payment has proof
function hasPaymentProof(payment: Payment): boolean {
return !!payment.proofPhotoId && payment.status === 'completed';
}
// Check if user needs re-auth
function needsReauth(user: User): boolean {
return user.requiresReauth || !!user.reauthReason;
}Working with Dates
// Most dates are ISO strings
const appointment: Appointment = {
start: '2024-01-15T09:00:00Z',
end: '2024-01-15T10:00:00Z',
// ...
};
// Parse when needed
const startDate = new Date(appointment.start);Optional Fields
Many fields are optional (?) because:
- They're added by the backend after creation
- They're only present in certain states
- They're computed/denormalized fields
interface Quote {
// Always present
_id: string;
quoteNumber: string;
// Optional - added after signing
signatures?: {
customer?: { signature: string; signedAt: string; }
consultant?: { signature: string; signedAt: string; }
};
// Optional - added after payment
paymentSummary?: { ... };
}File Storage (GridFS)
For large files, we use MongoDB GridFS:
// Quote PDFs
interface SignedQuoteFile {
_id: string; // Use this ID to fetch the file
filename: string; // 'quote_123_signed_timestamp.pdf'
metadata: { ... }; // Quote details
}
// Payment proof photos
interface PaymentProofFile {
_id: string; // Use this ID to fetch the file
filename: string; // 'payment_123_proof_timestamp.jpg'
metadata: { ... }; // Payment details
}Status Values
Project Status
open- Active projectwon- Completed successfullylost- Cancelled/lostabandoned- No activity
Quote Status
draft- Being createdpublished- Sent to customerviewed- Customer opened itsigned- Customer signedrecalled- Taken backexpired- Past valid daterevised- New version createddeposit_paid- Deposit received
Payment Status
pending- Awaiting paymentcompleted- Payment received
Task Status
pending- Not completedcompleted- Done
Appointment Status
scheduled- Upcomingcompleted- Finishedcancelled- Cancelledno_show- Customer didn't showrescheduled- Moved to new time
Message Direction
inbound- From contactoutbound- To contact
Email Event Types
accepted- Email accepted by serverdelivered- Email delivered to inboxopened- Email opened by recipient
Migration Notes
Recent Changes (June 2024)
- Added
Agencyinterface (renamed fromCompany) - Enhanced
Quotewith payment tracking - Added
Templatesystem for quote generation - Added
EmailStatfor email tracking
Breaking Changes
None - all changes have been additive with optional fields.
Common Gotchas
User.userId vs User._id
userIdis the GHL user ID (what we usually want)_idis the MongoDB document ID
Dates are strings
- All dates are ISO strings, not Date objects
- Parse with
new Date()when needed
GHL fields may be null
- Even "required" GHL fields might be null during sync
- Always use optional chaining:
project?.ghlOpportunityId
Arrays default to empty
tags: []nottags: undefinedmilestones: []notmilestones: undefined
Need Help?
- Check the main README for setup instructions
- See backend API docs
- Ask in the team chat
Contributing
When adding new types:
- Match the MongoDB collection structure exactly
- Use optional (
?) for fields that might not exist - Add JSDoc comments for unclear fields
- Update this README if adding new patterns
