import { apiClient, ensureCsrfCookie } from './client'; /** * API-вызовы для DealController. * * На MVP без auth — tenant_id передаётся параметром (на prod возьмётся из * middleware → backend параметр исчезнет). */ export interface CreateDealPayload { tenant_id: number; project_name: string; phone: string; contact_name?: string; status?: string; manager_id?: number; comment?: string; } export interface CreatedDeal { id: number; tenant_id: number; project_id: number; phone: string; status: string; contact_name: string | null; manager_id: number | null; received_at: string; } export async function createDeal(payload: CreateDealPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post<{ deal: CreatedDeal; message: string }>('/api/deals', payload); return data.deal; } export interface BulkDeleteDealsPayload { tenant_id: number; ids: number[]; } export interface BulkDeleteDealsResponse { deleted: number; requested: number; } /** Bulk soft-delete. Возвращает {deleted, requested} — deleted может быть < requested * если часть id чужие или уже удалены (NO-OP). */ export async function bulkDeleteDeals(payload: BulkDeleteDealsPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.delete('/api/deals', { data: payload }); return data; } export interface BulkRestoreDealsPayload { tenant_id: number; ids: number[]; } export interface BulkRestoreDealsResponse { restored: number; requested: number; } /** Bulk restore soft-deleted сделок. NO-OP idempotent для не-удалённых. */ export async function bulkRestoreDeals(payload: BulkRestoreDealsPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post('/api/deals/restore', payload); return data; } export interface ExportDealsPayload { tenant_id: number; ids: number[]; format?: 'csv' | 'xlsx'; } export interface UpdateDealPayload { tenant_id: number; comment?: string | null; manager_id?: number | null; status?: string; } export async function updateDeal(id: number, payload: UpdateDealPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.patch<{ deal: ApiDealDetail }>(`/api/deals/${id}`, payload); return data.deal; } export interface TransitionDealsPayload { tenant_id: number; ids: number[]; status: string; } export interface TransitionDealsResponse { updated: number; requested: number; status: string; } /** Bulk status-update. Возвращает {updated, requested} — updated может быть < requested * если часть id'шников чужие или уже в этом статусе (NO-OP). */ export async function transitionDeals(payload: TransitionDealsPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post('/api/deals/transition', payload); return data; } /** CSV-export. Возвращает строку с BOM — caller заворачивает в Blob+download. */ export async function exportDeals(payload: ExportDealsPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post( '/api/deals/export', { ...payload, format: 'csv' }, { responseType: 'text' }, ); return data; } /** XLSX-export. Возвращает Blob (binary) — caller триггерит download. */ export async function exportDealsXlsx(payload: Omit): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post( '/api/deals/export', { ...payload, format: 'xlsx' }, { responseType: 'blob' }, ); return data; } export interface ApiDeal { id: number; tenant_id: number; project_id: number; project_name: string | null; phone: string; contact_name: string | null; status: string; manager_id: number | null; manager_name: string | null; manager_initials: string | null; received_at: string | null; } export interface ApiDealEvent { id: number; event: string; context: Record | null; created_at: string | null; actor: { id: number; name: string; initials: string } | null; } export interface ApiDealDetail extends ApiDeal { comment: string | null; assigned_at: string | null; } export interface GetDealResponse { deal: ApiDealDetail; events: ApiDealEvent[]; } export async function getDeal(id: number, tenantId: number): Promise { const { data } = await apiClient.get(`/api/deals/${id}`, { params: { tenant_id: tenantId }, }); return data; } export interface ListDealsParams { tenantId: number; statusIn?: string[]; projectId?: number; managerId?: number; search?: string; limit?: number; offset?: number; /** «Корзина» — вернуть ТОЛЬКО soft-deleted сделки. */ onlyDeleted?: boolean; } export interface ListDealsResponse { deals: ApiDeal[]; total: number; limit: number; offset: number; } export async function listDeals(params: ListDealsParams): Promise { const { data } = await apiClient.get('/api/deals', { params: { tenant_id: params.tenantId, status_in: params.statusIn, project_id: params.projectId, manager_id: params.managerId, search: params.search, limit: params.limit, offset: params.offset, only_deleted: params.onlyDeleted ? 'true' : undefined, }, }); return data; } export interface ApiManager { id: number; email: string; first_name: string | null; last_name: string | null; name: string; initials: string; } export async function listManagers(tenantId: number): Promise { const { data } = await apiClient.get<{ managers: ApiManager[] }>('/api/managers', { params: { tenant_id: tenantId }, }); return data.managers; } export interface ApiProject { id: number; name: string; tag: string | null; type: string; } export async function listProjects(tenantId: number): Promise { const { data } = await apiClient.get<{ projects: ApiProject[] }>('/api/projects', { params: { tenant_id: tenantId }, }); return data.projects; }