mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 05:43:12 +00:00
* feat(vendors): support company roster workflows * fix(vendors): address roster review feedback * test(vendors): preserve draft on roster load failure * fix(vendors): preserve current roster selection
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import { isHTTPError } from "ky";
|
|
import { API_PATHS } from "@/api/api-paths";
|
|
import { apiGet, apiPost, apiPut } from "@/api/api";
|
|
import { handleApiResponse } from "@/api/handle-api-response";
|
|
import {
|
|
mapRosterConflict,
|
|
mapVendorCompanyRoster,
|
|
mapVendorRosterToBackend,
|
|
} from "@/domain/vendors/mappers/vendor-roster-mapper";
|
|
import { VendorRosterConflictError } from "@/domain/vendors/lib/vendor-roster-conflict";
|
|
import type { VendorCompanyRoster } from "@/domain/vendors/types/vendor";
|
|
|
|
const STALE_MESSAGE =
|
|
"This company was changed by another session. Reload the latest version and try again.";
|
|
const OPEN_WORK_ORDER_MESSAGE =
|
|
"This company cannot be saved because it still has open work orders.";
|
|
|
|
async function readConflictBody(error: unknown): Promise<unknown> {
|
|
if (!isHTTPError(error)) return undefined;
|
|
try {
|
|
return await error.response.json();
|
|
} catch {
|
|
try {
|
|
const text = await error.response.text();
|
|
return text ? JSON.parse(text) : undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function throwRosterConflict(error: unknown): Promise<never> {
|
|
const status = isHTTPError(error) ? error.response.status : 0;
|
|
const body = await readConflictBody(error);
|
|
const message = status === 409 ? STALE_MESSAGE : "Failed to save vendor company";
|
|
const conflict = mapRosterConflict(body, message);
|
|
if (conflict.kind === "open-work-orders" && conflict.blockedWorkOrders.length === 0) {
|
|
conflict.message = OPEN_WORK_ORDER_MESSAGE;
|
|
}
|
|
throw new VendorRosterConflictError(conflict, status || 409);
|
|
}
|
|
|
|
export interface RosterQuery {
|
|
vendorId?: string | number;
|
|
companyId?: string | number;
|
|
}
|
|
|
|
function buildRosterSearchParams(query: RosterQuery): Record<string, string> {
|
|
const params: Record<string, string> = {};
|
|
if (query.vendorId !== undefined && query.vendorId !== "") {
|
|
params.vendorId = String(query.vendorId);
|
|
}
|
|
if (query.companyId !== undefined && query.companyId !== "") {
|
|
params.companyId = String(query.companyId);
|
|
}
|
|
return params;
|
|
}
|
|
|
|
export const vendorCompanyRosterApi = {
|
|
get: async (query: RosterQuery): Promise<VendorCompanyRoster> => {
|
|
const data = await apiGet<unknown>(API_PATHS.vendorCompanyRoster.base, {
|
|
searchParams: buildRosterSearchParams(query),
|
|
});
|
|
return mapVendorCompanyRoster(handleApiResponse(data));
|
|
},
|
|
|
|
create: async (payload: unknown): Promise<VendorCompanyRoster> => {
|
|
try {
|
|
const data = await apiPost<unknown>(
|
|
API_PATHS.vendorCompanyRoster.base,
|
|
mapVendorRosterToBackend(payload),
|
|
);
|
|
return mapVendorCompanyRoster(handleApiResponse(data));
|
|
} catch (error) {
|
|
if (isHTTPError(error) && error.response.status === 409) {
|
|
await throwRosterConflict(error);
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
update: async (
|
|
companyId: string | number,
|
|
payload: unknown,
|
|
rowVersion: string,
|
|
): Promise<VendorCompanyRoster> => {
|
|
try {
|
|
const data = await apiPut<unknown>(
|
|
API_PATHS.vendorCompanyRoster.byCompany(companyId),
|
|
mapVendorRosterToBackend({ ...(payload as Record<string, unknown>), rowVersion }),
|
|
);
|
|
return mapVendorCompanyRoster(handleApiResponse(data));
|
|
} catch (error) {
|
|
if (isHTTPError(error) && error.response.status === 409) {
|
|
await throwRosterConflict(error);
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
};
|