Files
mrp/client/src/lib/api.ts

379 lines
10 KiB
TypeScript
Raw Normal View History

2026-03-14 14:44:40 -05:00
import type {
ApiResponse,
CompanyProfileDto,
CompanyProfileInput,
FileAttachmentDto,
GanttLinkDto,
GanttTaskDto,
LoginRequest,
LoginResponse,
} from "@mrp/shared";
2026-03-14 16:08:29 -05:00
import type {
2026-03-14 18:46:06 -05:00
CrmContactDto,
CrmContactInput,
2026-03-14 16:50:03 -05:00
CrmContactEntryDto,
CrmContactEntryInput,
2026-03-14 18:46:06 -05:00
CrmCustomerHierarchyOptionDto,
2026-03-14 16:08:29 -05:00
CrmRecordDetailDto,
CrmRecordInput,
2026-03-14 18:58:23 -05:00
CrmLifecycleStage,
2026-03-14 16:08:29 -05:00
CrmRecordStatus,
CrmRecordSummaryDto,
} from "@mrp/shared/dist/crm/types.js";
2026-03-14 21:10:35 -05:00
import type {
InventoryItemDetailDto,
InventoryItemInput,
InventoryItemOptionDto,
InventoryItemStatus,
InventoryItemSummaryDto,
2026-03-14 22:37:09 -05:00
InventoryTransactionInput,
2026-03-14 21:10:35 -05:00
InventoryItemType,
2026-03-14 21:23:22 -05:00
WarehouseDetailDto,
WarehouseInput,
2026-03-14 22:37:09 -05:00
WarehouseLocationOptionDto,
2026-03-14 21:23:22 -05:00
WarehouseSummaryDto,
2026-03-14 21:10:35 -05:00
} from "@mrp/shared/dist/inventory/types.js";
2026-03-14 14:44:40 -05:00
export class ApiError extends Error {
constructor(message: string, public readonly code: string) {
super(message);
}
}
async function request<T>(input: string, init?: RequestInit, token?: string): Promise<T> {
const response = await fetch(input, {
...init,
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(init?.headers ?? {}),
},
});
const json = (await response.json()) as ApiResponse<T>;
if (!json.ok) {
throw new ApiError(json.error.message, json.error.code);
}
return json.data;
}
2026-03-14 16:08:29 -05:00
function buildQueryString(params: Record<string, string | undefined>) {
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}` : "";
}
2026-03-14 14:44:40 -05:00
export const api = {
login(payload: LoginRequest) {
return request<LoginResponse>("/api/v1/auth/login", {
method: "POST",
body: JSON.stringify(payload),
});
},
me(token: string) {
return request<LoginResponse["user"]>("/api/v1/auth/me", undefined, token);
},
getCompanyProfile(token: string) {
return request<CompanyProfileDto>("/api/v1/company-profile", undefined, token);
},
updateCompanyProfile(token: string, payload: CompanyProfileInput) {
return request<CompanyProfileDto>(
"/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<FileAttachmentDto>;
if (!json.ok) {
throw new ApiError(json.error.message, json.error.code);
}
return json.data;
},
2026-03-14 16:17:04 -05:00
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();
},
2026-03-14 17:32:00 -05:00
getAttachments(token: string, ownerType: string, ownerId: string) {
return request<FileAttachmentDto[]>(
`/api/v1/files${buildQueryString({
ownerType,
ownerId,
})}`,
undefined,
token
);
},
2026-03-14 18:58:23 -05:00
deleteAttachment(token: string, fileId: string) {
return request<FileAttachmentDto>(
`/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";
}
) {
2026-03-14 16:08:29 -05:00
return request<CrmRecordSummaryDto[]>(
`/api/v1/crm/customers${buildQueryString({
q: filters?.q,
status: filters?.status,
2026-03-14 18:58:23 -05:00
lifecycleStage: filters?.lifecycleStage,
2026-03-14 16:08:29 -05:00
state: filters?.state,
2026-03-14 18:58:23 -05:00
flag: filters?.flag,
2026-03-14 16:08:29 -05:00
})}`,
undefined,
token
);
},
getCustomer(token: string, customerId: string) {
return request<CrmRecordDetailDto>(`/api/v1/crm/customers/${customerId}`, undefined, token);
},
2026-03-14 18:46:06 -05:00
getCustomerHierarchyOptions(token: string, excludeCustomerId?: string) {
return request<CrmCustomerHierarchyOptionDto[]>(
`/api/v1/crm/customers/hierarchy-options${buildQueryString({
excludeCustomerId,
})}`,
undefined,
token
);
},
2026-03-14 16:08:29 -05:00
createCustomer(token: string, payload: CrmRecordInput) {
return request<CrmRecordDetailDto>(
"/api/v1/crm/customers",
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
updateCustomer(token: string, customerId: string, payload: CrmRecordInput) {
return request<CrmRecordDetailDto>(
`/api/v1/crm/customers/${customerId}`,
{
method: "PUT",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 16:50:03 -05:00
createCustomerContactEntry(token: string, customerId: string, payload: CrmContactEntryInput) {
return request<CrmContactEntryDto>(
`/api/v1/crm/customers/${customerId}/contact-history`,
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 18:46:06 -05:00
createCustomerContact(token: string, customerId: string, payload: CrmContactInput) {
return request<CrmContactDto>(
`/api/v1/crm/customers/${customerId}/contacts`,
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 18:58:23 -05:00
getVendors(
token: string,
filters?: {
q?: string;
status?: CrmRecordStatus;
lifecycleStage?: CrmLifecycleStage;
state?: string;
flag?: "PREFERRED" | "STRATEGIC" | "REQUIRES_APPROVAL" | "BLOCKED";
}
) {
2026-03-14 16:08:29 -05:00
return request<CrmRecordSummaryDto[]>(
`/api/v1/crm/vendors${buildQueryString({
q: filters?.q,
status: filters?.status,
2026-03-14 18:58:23 -05:00
lifecycleStage: filters?.lifecycleStage,
2026-03-14 16:08:29 -05:00
state: filters?.state,
2026-03-14 18:58:23 -05:00
flag: filters?.flag,
2026-03-14 16:08:29 -05:00
})}`,
undefined,
token
);
},
getVendor(token: string, vendorId: string) {
return request<CrmRecordDetailDto>(`/api/v1/crm/vendors/${vendorId}`, undefined, token);
2026-03-14 14:44:40 -05:00
},
2026-03-14 16:08:29 -05:00
createVendor(token: string, payload: CrmRecordInput) {
return request<CrmRecordDetailDto>(
"/api/v1/crm/vendors",
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
updateVendor(token: string, vendorId: string, payload: CrmRecordInput) {
return request<CrmRecordDetailDto>(
`/api/v1/crm/vendors/${vendorId}`,
{
method: "PUT",
body: JSON.stringify(payload),
},
token
);
2026-03-14 14:44:40 -05:00
},
2026-03-14 16:50:03 -05:00
createVendorContactEntry(token: string, vendorId: string, payload: CrmContactEntryInput) {
return request<CrmContactEntryDto>(
`/api/v1/crm/vendors/${vendorId}/contact-history`,
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 18:46:06 -05:00
createVendorContact(token: string, vendorId: string, payload: CrmContactInput) {
return request<CrmContactDto>(
`/api/v1/crm/vendors/${vendorId}/contacts`,
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 21:10:35 -05:00
getInventoryItems(token: string, filters?: { q?: string; status?: InventoryItemStatus; type?: InventoryItemType }) {
return request<InventoryItemSummaryDto[]>(
`/api/v1/inventory/items${buildQueryString({
q: filters?.q,
status: filters?.status,
type: filters?.type,
})}`,
undefined,
token
);
},
getInventoryItem(token: string, itemId: string) {
return request<InventoryItemDetailDto>(`/api/v1/inventory/items/${itemId}`, undefined, token);
},
getInventoryItemOptions(token: string) {
return request<InventoryItemOptionDto[]>("/api/v1/inventory/items/options", undefined, token);
},
2026-03-14 22:37:09 -05:00
getWarehouseLocationOptions(token: string) {
return request<WarehouseLocationOptionDto[]>("/api/v1/inventory/locations/options", undefined, token);
},
2026-03-14 21:10:35 -05:00
createInventoryItem(token: string, payload: InventoryItemInput) {
return request<InventoryItemDetailDto>(
"/api/v1/inventory/items",
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
updateInventoryItem(token: string, itemId: string, payload: InventoryItemInput) {
return request<InventoryItemDetailDto>(
`/api/v1/inventory/items/${itemId}`,
{
method: "PUT",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 22:37:09 -05:00
createInventoryTransaction(token: string, itemId: string, payload: InventoryTransactionInput) {
return request<InventoryItemDetailDto>(
`/api/v1/inventory/items/${itemId}/transactions`,
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 21:23:22 -05:00
getWarehouses(token: string) {
return request<WarehouseSummaryDto[]>("/api/v1/inventory/warehouses", undefined, token);
},
getWarehouse(token: string, warehouseId: string) {
return request<WarehouseDetailDto>(`/api/v1/inventory/warehouses/${warehouseId}`, undefined, token);
},
createWarehouse(token: string, payload: WarehouseInput) {
return request<WarehouseDetailDto>(
"/api/v1/inventory/warehouses",
{
method: "POST",
body: JSON.stringify(payload),
},
token
);
},
updateWarehouse(token: string, warehouseId: string, payload: WarehouseInput) {
return request<WarehouseDetailDto>(
`/api/v1/inventory/warehouses/${warehouseId}`,
{
method: "PUT",
body: JSON.stringify(payload),
},
token
);
},
2026-03-14 14:44:40 -05:00
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();
},
};