import type { ApiResponse, CompanyProfileDto, CompanyProfileInput, FileAttachmentDto, GanttLinkDto, GanttTaskDto, LoginRequest, LoginResponse, } from "@mrp/shared"; import type { CrmContactDto, CrmContactInput, CrmContactEntryDto, CrmContactEntryInput, CrmCustomerHierarchyOptionDto, CrmRecordDetailDto, CrmRecordInput, CrmRecordStatus, CrmRecordSummaryDto, } from "@mrp/shared/dist/crm/types.js"; export class ApiError extends Error { constructor(message: string, public readonly code: string) { super(message); } } async function request(input: string, init?: RequestInit, token?: string): Promise { const response = await fetch(input, { ...init, headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(init?.headers ?? {}), }, }); const json = (await response.json()) as ApiResponse; if (!json.ok) { throw new ApiError(json.error.message, json.error.code); } return json.data; } function buildQueryString(params: Record) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value) { searchParams.set(key, value); } } const queryString = searchParams.toString(); return queryString ? `?${queryString}` : ""; } export const api = { login(payload: LoginRequest) { return request("/api/v1/auth/login", { method: "POST", body: JSON.stringify(payload), }); }, me(token: string) { return request("/api/v1/auth/me", undefined, token); }, getCompanyProfile(token: string) { return request("/api/v1/company-profile", undefined, token); }, updateCompanyProfile(token: string, payload: CompanyProfileInput) { return request( "/api/v1/company-profile", { method: "PUT", body: JSON.stringify(payload), }, token ); }, async uploadFile(token: string, file: File, ownerType: string, ownerId: string) { const formData = new FormData(); formData.append("file", file); formData.append("ownerType", ownerType); formData.append("ownerId", ownerId); const response = await fetch("/api/v1/files/upload", { method: "POST", headers: { Authorization: `Bearer ${token}`, }, body: formData, }); const json = (await response.json()) as ApiResponse; if (!json.ok) { throw new ApiError(json.error.message, json.error.code); } return json.data; }, async getFileContentBlob(token: string, fileId: string) { const response = await fetch(`/api/v1/files/${fileId}/content`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to load file content.", "FILE_CONTENT_FAILED"); } return response.blob(); }, getAttachments(token: string, ownerType: string, ownerId: string) { return request( `/api/v1/files${buildQueryString({ ownerType, ownerId, })}`, undefined, token ); }, getCustomers(token: string, filters?: { q?: string; status?: CrmRecordStatus; state?: string }) { return request( `/api/v1/crm/customers${buildQueryString({ q: filters?.q, status: filters?.status, state: filters?.state, })}`, undefined, token ); }, getCustomer(token: string, customerId: string) { return request(`/api/v1/crm/customers/${customerId}`, undefined, token); }, getCustomerHierarchyOptions(token: string, excludeCustomerId?: string) { return request( `/api/v1/crm/customers/hierarchy-options${buildQueryString({ excludeCustomerId, })}`, undefined, token ); }, createCustomer(token: string, payload: CrmRecordInput) { return request( "/api/v1/crm/customers", { method: "POST", body: JSON.stringify(payload), }, token ); }, updateCustomer(token: string, customerId: string, payload: CrmRecordInput) { return request( `/api/v1/crm/customers/${customerId}`, { method: "PUT", body: JSON.stringify(payload), }, token ); }, createCustomerContactEntry(token: string, customerId: string, payload: CrmContactEntryInput) { return request( `/api/v1/crm/customers/${customerId}/contact-history`, { method: "POST", body: JSON.stringify(payload), }, token ); }, createCustomerContact(token: string, customerId: string, payload: CrmContactInput) { return request( `/api/v1/crm/customers/${customerId}/contacts`, { method: "POST", body: JSON.stringify(payload), }, token ); }, getVendors(token: string, filters?: { q?: string; status?: CrmRecordStatus; state?: string }) { return request( `/api/v1/crm/vendors${buildQueryString({ q: filters?.q, status: filters?.status, state: filters?.state, })}`, undefined, token ); }, getVendor(token: string, vendorId: string) { return request(`/api/v1/crm/vendors/${vendorId}`, undefined, token); }, createVendor(token: string, payload: CrmRecordInput) { return request( "/api/v1/crm/vendors", { method: "POST", body: JSON.stringify(payload), }, token ); }, updateVendor(token: string, vendorId: string, payload: CrmRecordInput) { return request( `/api/v1/crm/vendors/${vendorId}`, { method: "PUT", body: JSON.stringify(payload), }, token ); }, createVendorContactEntry(token: string, vendorId: string, payload: CrmContactEntryInput) { return request( `/api/v1/crm/vendors/${vendorId}/contact-history`, { method: "POST", body: JSON.stringify(payload), }, token ); }, createVendorContact(token: string, vendorId: string, payload: CrmContactInput) { return request( `/api/v1/crm/vendors/${vendorId}/contacts`, { method: "POST", body: JSON.stringify(payload), }, token ); }, getGanttDemo(token: string) { return request<{ tasks: GanttTaskDto[]; links: GanttLinkDto[] }>("/api/v1/gantt/demo", undefined, token); }, async getCompanyProfilePreviewPdf(token: string) { const response = await fetch("/api/v1/documents/company-profile-preview.pdf", { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render company profile preview PDF.", "PDF_PREVIEW_FAILED"); } return response.blob(); }, };