shoc-frontend-new/src/domain/uplifts/api/uplifts-api.ts
2026-09-16 22:40:48 -03:00

125 lines
4.3 KiB
TypeScript

import { HTTPError } from "ky";
import { API_PATHS } from "@/api/api-paths";
import { apiGet, apiPost, apiRequestRaw } from "@/api/api";
import { handleApiResponse } from "@/api/handle-api-response";
import {
mapUpliftQueueExposureTotal,
mapUpliftQueueItems,
mapUpliftRequests,
} from "@/domain/uplifts/mappers/uplift-mapper";
import type { UpliftsQueueParams, UpliftsQueueResult } from "@/domain/uplifts/types/uplift";
import type { UpliftRequest } from "@/domain/uplifts/types/uplift";
function buildQueueParams(params: UpliftsQueueParams): Record<string, string | number> {
const searchParams: Record<string, string | number> = {
page: params.page ?? 1,
pageSize: params.pageSize ?? 25,
};
if (params.status) searchParams.status = params.status;
if (params.tier) searchParams.tier = params.tier;
return searchParams;
}
function evidenceUrl(id: string | number): string {
return `${API_PATHS.rest.uplifts}/${id}/evidence`;
}
function readContentDispositionFilename(response: Response, fallback: string): string {
const disposition = response.headers.get("Content-Disposition") ?? "";
const match = /filename="?([^";]+)"?/i.exec(disposition);
return match ? match[1] : fallback;
}
function evidenceHttpError(status: number): Error {
if (status === 423) {
return new Error("Evidence is still being scanned. Try again shortly.");
}
if (status === 403) {
return new Error("You are not authorized to view this evidence.");
}
return new Error("Unable to download evidence right now.");
}
export const upliftsApi = {
list: async (params: UpliftsQueueParams = {}): Promise<UpliftsQueueResult> => {
const data = await apiGet<unknown>(API_PATHS.rest.uplifts, {
searchParams: buildQueueParams(params),
});
const response = handleApiResponse<Record<string, unknown>>(data);
const items = Array.isArray(response?.items)
? response.items
: Array.isArray((response as { data?: unknown[] })?.data)
? (response as { data: unknown[] }).data
: [];
const total =
(typeof response?.total === "number" ? response.total : undefined) ??
(typeof response?.totalCount === "number" ? response.totalCount : undefined) ??
items.length;
return {
items: mapUpliftQueueItems(items),
totalCount: total,
page: params.page ?? 1,
pageSize: params.pageSize ?? 25,
pendingExposureTotal: mapUpliftQueueExposureTotal(response),
};
},
listForDispatch: async (dispatchId: string | number): Promise<UpliftRequest[]> => {
const data = await apiGet<unknown>(`${API_PATHS.rest.uplifts}/dispatch/${dispatchId}`);
const response = handleApiResponse<unknown>(data);
const items = Array.isArray(response)
? response
: typeof response === "object" &&
response !== null &&
Array.isArray((response as { data?: unknown[] }).data)
? (response as { data: unknown[] }).data
: [];
return mapUpliftRequests(items);
},
approve: async (id: string | number, note?: string): Promise<void> => {
await apiPost<unknown>(`${API_PATHS.rest.uplifts}/${id}/approve`, { note });
},
reject: async (id: string | number, note: string): Promise<void> => {
await apiPost<unknown>(`${API_PATHS.rest.uplifts}/${id}/reject`, { note });
},
requestChanges: async (id: string | number, note: string): Promise<void> => {
await apiPost<unknown>(`${API_PATHS.rest.uplifts}/${id}/request-changes`, { note });
},
revoke: async (id: string | number, reason: string): Promise<void> => {
await apiPost<unknown>(`${API_PATHS.rest.uplifts}/${id}/revoke`, { note: reason });
},
downloadEvidence: async (
id: string | number,
fallbackFileName = "uplift-evidence",
): Promise<void> => {
let response: Response;
try {
response = await apiRequestRaw(
"get",
evidenceUrl(id),
undefined,
"upliftsApi.downloadEvidence",
);
} catch (error) {
if (error instanceof HTTPError) {
throw evidenceHttpError(error.response.status);
}
throw error;
}
const blob = await response.blob();
const fileName = readContentDispositionFilename(response, fallbackFileName);
const url = URL.createObjectURL(blob);
const anchor = window.document.createElement("a");
anchor.href = url;
anchor.download = fileName;
anchor.click();
URL.revokeObjectURL(url);
},
};