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 { const searchParams: Record = { 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 => { const data = await apiGet(API_PATHS.rest.uplifts, { searchParams: buildQueueParams(params), }); const response = handleApiResponse>(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 => { const data = await apiGet(`${API_PATHS.rest.uplifts}/dispatch/${dispatchId}`); const response = handleApiResponse(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 => { await apiPost(`${API_PATHS.rest.uplifts}/${id}/approve`, { note }); }, reject: async (id: string | number, note: string): Promise => { await apiPost(`${API_PATHS.rest.uplifts}/${id}/reject`, { note }); }, requestChanges: async (id: string | number, note: string): Promise => { await apiPost(`${API_PATHS.rest.uplifts}/${id}/request-changes`, { note }); }, revoke: async (id: string | number, reason: string): Promise => { await apiPost(`${API_PATHS.rest.uplifts}/${id}/revoke`, { note: reason }); }, downloadEvidence: async ( id: string | number, fallbackFileName = "uplift-evidence", ): Promise => { 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); }, };