import type { AdminDiagnosticsDto, AdminPermissionOptionDto, AdminRoleDto, AdminRoleInput, AdminUserDto, AdminUserInput, ApiResponse, CompanyProfileDto, CompanyProfileInput, FileAttachmentDto, PlanningTimelineDto, LoginRequest, LoginResponse, } from "@mrp/shared"; import type { CrmContactDto, CrmContactInput, CrmContactEntryDto, CrmContactEntryInput, CrmCustomerHierarchyOptionDto, CrmRecordDetailDto, CrmRecordInput, CrmLifecycleStage, CrmRecordStatus, CrmRecordSummaryDto, } from "@mrp/shared/dist/crm/types.js"; import type { InventoryItemDetailDto, InventoryItemInput, InventoryItemOptionDto, InventoryReservationInput, InventoryItemStatus, InventoryItemSummaryDto, InventoryTransferInput, InventoryTransactionInput, InventoryItemType, WarehouseDetailDto, WarehouseInput, WarehouseLocationOptionDto, WarehouseSummaryDto, } from "@mrp/shared/dist/inventory/types.js"; import type { ManufacturingStationDto, ManufacturingStationInput, ManufacturingItemOptionDto, ManufacturingProjectOptionDto, WorkOrderCompletionInput, WorkOrderDetailDto, WorkOrderInput, WorkOrderMaterialIssueInput, WorkOrderStatus, WorkOrderSummaryDto, } from "@mrp/shared"; import type { ProjectCustomerOptionDto, ProjectDetailDto, ProjectDocumentOptionDto, ProjectInput, ProjectOwnerOptionDto, ProjectPriority, ProjectShipmentOptionDto, ProjectStatus, ProjectSummaryDto, } from "@mrp/shared/dist/projects/types.js"; import type { SalesCustomerOptionDto, SalesDocumentDetailDto, SalesDocumentInput, SalesDocumentRevisionDto, SalesDocumentStatus, SalesDocumentSummaryDto, } from "@mrp/shared/dist/sales/types.js"; import type { PurchaseOrderDetailDto, PurchaseOrderInput, PurchaseOrderStatus, PurchaseOrderSummaryDto, PurchaseVendorOptionDto, } from "@mrp/shared"; import type { PurchaseReceiptInput } from "@mrp/shared/dist/purchasing/types.js"; import type { ShipmentDetailDto, ShipmentInput, ShipmentOrderOptionDto, ShipmentStatus, ShipmentSummaryDto, } from "@mrp/shared/dist/shipping/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); }, getAdminDiagnostics(token: string) { return request("/api/v1/admin/diagnostics", undefined, token); }, getAdminPermissions(token: string) { return request("/api/v1/admin/permissions", undefined, token); }, getAdminRoles(token: string) { return request("/api/v1/admin/roles", undefined, token); }, createAdminRole(token: string, payload: AdminRoleInput) { return request("/api/v1/admin/roles", { method: "POST", body: JSON.stringify(payload) }, token); }, updateAdminRole(token: string, roleId: string, payload: AdminRoleInput) { return request(`/api/v1/admin/roles/${roleId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, getAdminUsers(token: string) { return request("/api/v1/admin/users", undefined, token); }, createAdminUser(token: string, payload: AdminUserInput) { return request("/api/v1/admin/users", { method: "POST", body: JSON.stringify(payload) }, token); }, updateAdminUser(token: string, userId: string, payload: AdminUserInput) { return request(`/api/v1/admin/users/${userId}`, { method: "PUT", body: JSON.stringify(payload) }, 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 ); }, deleteAttachment(token: string, fileId: string) { return request( `/api/v1/files/${fileId}`, { method: "DELETE", }, token ); }, getCustomers( token: string, filters?: { q?: string; status?: CrmRecordStatus; lifecycleStage?: CrmLifecycleStage; state?: string; flag?: "PREFERRED" | "STRATEGIC" | "REQUIRES_APPROVAL" | "BLOCKED"; } ) { return request( `/api/v1/crm/customers${buildQueryString({ q: filters?.q, status: filters?.status, lifecycleStage: filters?.lifecycleStage, state: filters?.state, flag: filters?.flag, })}`, 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; lifecycleStage?: CrmLifecycleStage; state?: string; flag?: "PREFERRED" | "STRATEGIC" | "REQUIRES_APPROVAL" | "BLOCKED"; } ) { return request( `/api/v1/crm/vendors${buildQueryString({ q: filters?.q, status: filters?.status, lifecycleStage: filters?.lifecycleStage, state: filters?.state, flag: filters?.flag, })}`, 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 ); }, getInventoryItems(token: string, filters?: { q?: string; status?: InventoryItemStatus; type?: InventoryItemType }) { return request( `/api/v1/inventory/items${buildQueryString({ q: filters?.q, status: filters?.status, type: filters?.type, })}`, undefined, token ); }, getInventoryItem(token: string, itemId: string) { return request(`/api/v1/inventory/items/${itemId}`, undefined, token); }, getInventoryItemOptions(token: string) { return request("/api/v1/inventory/items/options", undefined, token); }, getWarehouseLocationOptions(token: string) { return request("/api/v1/inventory/locations/options", undefined, token); }, createInventoryItem(token: string, payload: InventoryItemInput) { return request( "/api/v1/inventory/items", { method: "POST", body: JSON.stringify(payload), }, token ); }, updateInventoryItem(token: string, itemId: string, payload: InventoryItemInput) { return request( `/api/v1/inventory/items/${itemId}`, { method: "PUT", body: JSON.stringify(payload), }, token ); }, createInventoryTransaction(token: string, itemId: string, payload: InventoryTransactionInput) { return request( `/api/v1/inventory/items/${itemId}/transactions`, { method: "POST", body: JSON.stringify(payload), }, token ); }, createInventoryTransfer(token: string, itemId: string, payload: InventoryTransferInput) { return request( `/api/v1/inventory/items/${itemId}/transfers`, { method: "POST", body: JSON.stringify(payload), }, token ); }, createInventoryReservation(token: string, itemId: string, payload: InventoryReservationInput) { return request( `/api/v1/inventory/items/${itemId}/reservations`, { method: "POST", body: JSON.stringify(payload), }, token ); }, getWarehouses(token: string) { return request("/api/v1/inventory/warehouses", undefined, token); }, getWarehouse(token: string, warehouseId: string) { return request(`/api/v1/inventory/warehouses/${warehouseId}`, undefined, token); }, createWarehouse(token: string, payload: WarehouseInput) { return request( "/api/v1/inventory/warehouses", { method: "POST", body: JSON.stringify(payload), }, token ); }, updateWarehouse(token: string, warehouseId: string, payload: WarehouseInput) { return request( `/api/v1/inventory/warehouses/${warehouseId}`, { method: "PUT", body: JSON.stringify(payload), }, token ); }, getProjects( token: string, filters?: { q?: string; status?: ProjectStatus; priority?: ProjectPriority; customerId?: string; ownerId?: string } ) { return request( `/api/v1/projects${buildQueryString({ q: filters?.q, status: filters?.status, priority: filters?.priority, customerId: filters?.customerId, ownerId: filters?.ownerId, })}`, undefined, token ); }, getProject(token: string, projectId: string) { return request(`/api/v1/projects/${projectId}`, undefined, token); }, createProject(token: string, payload: ProjectInput) { return request("/api/v1/projects", { method: "POST", body: JSON.stringify(payload) }, token); }, updateProject(token: string, projectId: string, payload: ProjectInput) { return request(`/api/v1/projects/${projectId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, getProjectCustomerOptions(token: string) { return request("/api/v1/projects/customers/options", undefined, token); }, getProjectOwnerOptions(token: string) { return request("/api/v1/projects/owners/options", undefined, token); }, getProjectQuoteOptions(token: string, customerId?: string) { return request( `/api/v1/projects/quotes/options${buildQueryString({ customerId })}`, undefined, token ); }, getProjectOrderOptions(token: string, customerId?: string) { return request( `/api/v1/projects/orders/options${buildQueryString({ customerId })}`, undefined, token ); }, getProjectShipmentOptions(token: string, customerId?: string) { return request( `/api/v1/projects/shipments/options${buildQueryString({ customerId })}`, undefined, token ); }, getManufacturingItemOptions(token: string) { return request("/api/v1/manufacturing/items/options", undefined, token); }, getManufacturingProjectOptions(token: string) { return request("/api/v1/manufacturing/projects/options", undefined, token); }, getManufacturingStations(token: string) { return request("/api/v1/manufacturing/stations", undefined, token); }, createManufacturingStation(token: string, payload: ManufacturingStationInput) { return request("/api/v1/manufacturing/stations", { method: "POST", body: JSON.stringify(payload) }, token); }, getWorkOrders(token: string, filters?: { q?: string; status?: WorkOrderStatus; projectId?: string; itemId?: string }) { return request( `/api/v1/manufacturing/work-orders${buildQueryString({ q: filters?.q, status: filters?.status, projectId: filters?.projectId, itemId: filters?.itemId, })}`, undefined, token ); }, getWorkOrder(token: string, workOrderId: string) { return request(`/api/v1/manufacturing/work-orders/${workOrderId}`, undefined, token); }, createWorkOrder(token: string, payload: WorkOrderInput) { return request("/api/v1/manufacturing/work-orders", { method: "POST", body: JSON.stringify(payload) }, token); }, updateWorkOrder(token: string, workOrderId: string, payload: WorkOrderInput) { return request(`/api/v1/manufacturing/work-orders/${workOrderId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, updateWorkOrderStatus(token: string, workOrderId: string, status: WorkOrderStatus) { return request( `/api/v1/manufacturing/work-orders/${workOrderId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }, token ); }, issueWorkOrderMaterial(token: string, workOrderId: string, payload: WorkOrderMaterialIssueInput) { return request( `/api/v1/manufacturing/work-orders/${workOrderId}/issues`, { method: "POST", body: JSON.stringify(payload) }, token ); }, recordWorkOrderCompletion(token: string, workOrderId: string, payload: WorkOrderCompletionInput) { return request( `/api/v1/manufacturing/work-orders/${workOrderId}/completions`, { method: "POST", body: JSON.stringify(payload) }, token ); }, getPlanningTimeline(token: string) { return request("/api/v1/gantt/timeline", undefined, token); }, getSalesCustomers(token: string) { return request("/api/v1/sales/customers/options", undefined, token); }, getPurchaseVendors(token: string) { return request("/api/v1/purchasing/vendors/options", undefined, token); }, getQuotes(token: string, filters?: { q?: string; status?: SalesDocumentStatus }) { return request( `/api/v1/sales/quotes${buildQueryString({ q: filters?.q, status: filters?.status, })}`, undefined, token ); }, getQuote(token: string, quoteId: string) { return request(`/api/v1/sales/quotes/${quoteId}`, undefined, token); }, createQuote(token: string, payload: SalesDocumentInput) { return request("/api/v1/sales/quotes", { method: "POST", body: JSON.stringify(payload) }, token); }, updateQuote(token: string, quoteId: string, payload: SalesDocumentInput) { return request(`/api/v1/sales/quotes/${quoteId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, updateQuoteStatus(token: string, quoteId: string, status: SalesDocumentStatus) { return request( `/api/v1/sales/quotes/${quoteId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }, token ); }, approveQuote(token: string, quoteId: string) { return request(`/api/v1/sales/quotes/${quoteId}/approve`, { method: "POST" }, token); }, getQuoteRevisions(token: string, quoteId: string) { return request(`/api/v1/sales/quotes/${quoteId}/revisions`, undefined, token); }, convertQuoteToSalesOrder(token: string, quoteId: string) { return request(`/api/v1/sales/quotes/${quoteId}/convert`, { method: "POST" }, token); }, getSalesOrders(token: string, filters?: { q?: string; status?: SalesDocumentStatus }) { return request( `/api/v1/sales/orders${buildQueryString({ q: filters?.q, status: filters?.status, })}`, undefined, token ); }, getSalesOrder(token: string, orderId: string) { return request(`/api/v1/sales/orders/${orderId}`, undefined, token); }, createSalesOrder(token: string, payload: SalesDocumentInput) { return request("/api/v1/sales/orders", { method: "POST", body: JSON.stringify(payload) }, token); }, updateSalesOrder(token: string, orderId: string, payload: SalesDocumentInput) { return request(`/api/v1/sales/orders/${orderId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, updateSalesOrderStatus(token: string, orderId: string, status: SalesDocumentStatus) { return request( `/api/v1/sales/orders/${orderId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }, token ); }, approveSalesOrder(token: string, orderId: string) { return request(`/api/v1/sales/orders/${orderId}/approve`, { method: "POST" }, token); }, getSalesOrderRevisions(token: string, orderId: string) { return request(`/api/v1/sales/orders/${orderId}/revisions`, undefined, token); }, getPurchaseOrders(token: string, filters?: { q?: string; status?: PurchaseOrderStatus; vendorId?: string }) { return request( `/api/v1/purchasing/orders${buildQueryString({ q: filters?.q, status: filters?.status, vendorId: filters?.vendorId, })}`, undefined, token ); }, getPurchaseOrder(token: string, orderId: string) { return request(`/api/v1/purchasing/orders/${orderId}`, undefined, token); }, createPurchaseOrder(token: string, payload: PurchaseOrderInput) { return request("/api/v1/purchasing/orders", { method: "POST", body: JSON.stringify(payload) }, token); }, updatePurchaseOrder(token: string, orderId: string, payload: PurchaseOrderInput) { return request(`/api/v1/purchasing/orders/${orderId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, updatePurchaseOrderStatus(token: string, orderId: string, status: PurchaseOrderStatus) { return request( `/api/v1/purchasing/orders/${orderId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }, token ); }, createPurchaseReceipt(token: string, orderId: string, payload: PurchaseReceiptInput) { return request( `/api/v1/purchasing/orders/${orderId}/receipts`, { method: "POST", body: JSON.stringify(payload) }, token ); }, getShipmentOrderOptions(token: string) { return request("/api/v1/shipping/orders/options", undefined, token); }, getShipments(token: string, filters?: { q?: string; status?: ShipmentStatus; salesOrderId?: string }) { return request( `/api/v1/shipping/shipments${buildQueryString({ q: filters?.q, status: filters?.status, salesOrderId: filters?.salesOrderId, })}`, undefined, token ); }, getShipment(token: string, shipmentId: string) { return request(`/api/v1/shipping/shipments/${shipmentId}`, undefined, token); }, createShipment(token: string, payload: ShipmentInput) { return request("/api/v1/shipping/shipments", { method: "POST", body: JSON.stringify(payload) }, token); }, updateShipment(token: string, shipmentId: string, payload: ShipmentInput) { return request(`/api/v1/shipping/shipments/${shipmentId}`, { method: "PUT", body: JSON.stringify(payload) }, token); }, updateShipmentStatus(token: string, shipmentId: string, status: ShipmentStatus) { return request( `/api/v1/shipping/shipments/${shipmentId}/status`, { method: "PATCH", body: JSON.stringify({ status }) }, token ); }, async getShipmentPackingSlipPdf(token: string, shipmentId: string) { const response = await fetch(`/api/v1/documents/shipping/shipments/${shipmentId}/packing-slip.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render packing slip PDF.", "PACKING_SLIP_FAILED"); } return response.blob(); }, async getShipmentLabelPdf(token: string, shipmentId: string) { const response = await fetch(`/api/v1/documents/shipping/shipments/${shipmentId}/shipping-label.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render shipping label PDF.", "SHIPPING_LABEL_FAILED"); } return response.blob(); }, async getShipmentBillOfLadingPdf(token: string, shipmentId: string) { const response = await fetch(`/api/v1/documents/shipping/shipments/${shipmentId}/bill-of-lading.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render bill of lading PDF.", "BILL_OF_LADING_FAILED"); } return response.blob(); }, async getQuotePdf(token: string, quoteId: string) { const response = await fetch(`/api/v1/documents/sales/quotes/${quoteId}/document.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render quote PDF.", "QUOTE_PDF_FAILED"); } return response.blob(); }, async getSalesOrderPdf(token: string, orderId: string) { const response = await fetch(`/api/v1/documents/sales/orders/${orderId}/document.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render sales order PDF.", "SALES_ORDER_PDF_FAILED"); } return response.blob(); }, async getPurchaseOrderPdf(token: string, orderId: string) { const response = await fetch(`/api/v1/documents/purchasing/orders/${orderId}/document.pdf`, { headers: { Authorization: `Bearer ${token}`, }, }); if (!response.ok) { throw new ApiError("Unable to render purchase order PDF.", "PURCHASE_ORDER_PDF_FAILED"); } return response.blob(); }, 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(); }, };