Deploy rule, CRM enhancements, Company Employee category, bilingual, contacts module completion

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Talal Sharabi
2026-02-19 14:59:34 +04:00
parent 0b126cb676
commit 680ba3871e
51 changed files with 11456 additions and 477 deletions

View File

@@ -0,0 +1,65 @@
import { api } from '../api'
export interface Category {
id: string
name: string
nameAr?: string
parentId?: string
parent?: Category
children?: Category[]
description?: string
isActive: boolean
createdAt: string
updatedAt: string
_count?: {
contacts: number
}
}
export interface CreateCategoryData {
name: string
nameAr?: string
parentId?: string
description?: string
}
export interface UpdateCategoryData extends Partial<CreateCategoryData> {
isActive?: boolean
}
export const categoriesAPI = {
// Get all categories (flat list)
getAll: async (): Promise<Category[]> => {
const response = await api.get('/contacts/categories')
return response.data.data
},
// Get category tree (hierarchical)
getTree: async (): Promise<Category[]> => {
const response = await api.get('/contacts/categories/tree')
return response.data.data
},
// Get single category by ID
getById: async (id: string): Promise<Category> => {
const response = await api.get(`/contacts/categories/${id}`)
return response.data.data
},
// Create new category
create: async (data: CreateCategoryData): Promise<Category> => {
const response = await api.post('/contacts/categories', data)
return response.data.data
},
// Update existing category
update: async (id: string, data: UpdateCategoryData): Promise<Category> => {
const response = await api.put(`/contacts/categories/${id}`, data)
return response.data.data
},
// Delete category
delete: async (id: string): Promise<void> => {
await api.delete(`/contacts/categories/${id}`)
}
}

View File

@@ -25,6 +25,9 @@ export interface Contact {
customFields?: any
categories?: any[]
parent?: any
parentId?: string
employeeId?: string | null
employee?: { id: string; firstName: string; lastName: string; email: string; uniqueEmployeeId?: string }
createdAt: string
updatedAt: string
createdBy?: any
@@ -49,6 +52,7 @@ export interface CreateContactData {
categories?: string[]
tags?: string[]
parentId?: string
employeeId?: string | null
source: string
customFields?: any
}
@@ -143,11 +147,13 @@ export const contactsAPI = {
},
// Export contacts
export: async (filters: ContactFilters = {}): Promise<Blob> => {
export: async (filters: ContactFilters & { excludeCompanyEmployees?: boolean } = {}): Promise<Blob> => {
const params = new URLSearchParams()
if (filters.search) params.append('search', filters.search)
if (filters.type) params.append('type', filters.type)
if (filters.status) params.append('status', filters.status)
if (filters.category) params.append('category', filters.category)
if (filters.excludeCompanyEmployees) params.append('excludeCompanyEmployees', 'true')
const response = await api.get(`/contacts/export?${params.toString()}`, {
responseType: 'blob'
@@ -156,7 +162,12 @@ export const contactsAPI = {
},
// Import contacts
import: async (file: File): Promise<{ success: number; errors: any[] }> => {
import: async (file: File): Promise<{
success: number
failed: number
duplicates: number
errors: Array<{ row: number; field: string; message: string; data?: any }>
}> => {
const formData = new FormData()
formData.append('file', file)
@@ -166,6 +177,51 @@ export const contactsAPI = {
}
})
return response.data.data
},
// Check for duplicates
checkDuplicates: async (data: {
email?: string
phone?: string
mobile?: string
taxNumber?: string
commercialRegister?: string
excludeId?: string
}): Promise<Contact[]> => {
const response = await api.post('/contacts/check-duplicates', data)
return response.data.data
},
// Relationship management
getRelationships: async (contactId: string): Promise<any[]> => {
const response = await api.get(`/contacts/${contactId}/relationships`)
return response.data.data
},
addRelationship: async (contactId: string, data: {
toContactId: string
type: string
startDate: string
endDate?: string
notes?: string
}): Promise<any> => {
const response = await api.post(`/contacts/${contactId}/relationships`, data)
return response.data.data
},
updateRelationship: async (contactId: string, relationshipId: string, data: {
type?: string
startDate?: string
endDate?: string
notes?: string
isActive?: boolean
}): Promise<any> => {
const response = await api.put(`/contacts/${contactId}/relationships/${relationshipId}`, data)
return response.data.data
},
deleteRelationship: async (contactId: string, relationshipId: string): Promise<void> => {
await api.delete(`/contacts/${contactId}/relationships/${relationshipId}`)
}
}

View File

@@ -0,0 +1,30 @@
import { api } from '../api'
export interface PipelineStage {
name: string
nameAr?: string
order: number
}
export interface Pipeline {
id: string
name: string
nameAr?: string
structure: string
stages: PipelineStage[]
isActive: boolean
}
export const pipelinesAPI = {
getAll: async (structure?: string): Promise<Pipeline[]> => {
const params = new URLSearchParams()
if (structure) params.append('structure', structure)
const response = await api.get(`/crm/pipelines?${params.toString()}`)
return response.data.data
},
getById: async (id: string): Promise<Pipeline> => {
const response = await api.get(`/crm/pipelines/${id}`)
return response.data.data
}
}

View File

@@ -0,0 +1,74 @@
import { api } from '../api'
export interface QuoteItem {
description: string
quantity: number
unitPrice: number
total: number
}
export interface Quote {
id: string
quoteNumber: string
dealId: string
deal?: any
version: number
items: QuoteItem[] | any
subtotal: number
discountType?: string
discountValue?: number
taxRate: number
taxAmount: number
total: number
validUntil: string
paymentTerms?: string
deliveryTerms?: string
notes?: string
status: string
sentAt?: string
viewedAt?: string
approvedBy?: string
approvedAt?: string
createdAt: string
updatedAt: string
}
export interface CreateQuoteData {
dealId: string
items: QuoteItem[] | any[]
subtotal: number
taxRate: number
taxAmount: number
total: number
validUntil: string
paymentTerms?: string
deliveryTerms?: string
notes?: string
}
export const quotesAPI = {
getByDeal: async (dealId: string): Promise<Quote[]> => {
const response = await api.get(`/crm/deals/${dealId}/quotes`)
return response.data.data || []
},
getById: async (id: string): Promise<Quote> => {
const response = await api.get(`/crm/quotes/${id}`)
return response.data.data
},
create: async (data: CreateQuoteData): Promise<Quote> => {
const response = await api.post('/crm/quotes', data)
return response.data.data
},
approve: async (id: string): Promise<Quote> => {
const response = await api.post(`/crm/quotes/${id}/approve`)
return response.data.data
},
send: async (id: string): Promise<Quote> => {
const response = await api.post(`/crm/quotes/${id}/send`)
return response.data.data
}
}