108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
const API_BASE = 'https://cfx9z50wj2.execute-api.ca-central-1.amazonaws.com/prod';
|
|
|
|
async function http<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const base = API_BASE ? API_BASE.replace(/\/+$/, '') : '';
|
|
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
const url = `${base}${normalizedPath}`;
|
|
|
|
const res = await fetch(url, {
|
|
...init,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(init?.headers || {})
|
|
}
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => "");
|
|
throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`);
|
|
}
|
|
|
|
return (await res.json()) as T;
|
|
}
|
|
|
|
export type CaseStatus = {
|
|
case: {
|
|
case_id: string;
|
|
status: string;
|
|
current_step: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
};
|
|
steps: Array<{
|
|
step_name: string;
|
|
step_order: number;
|
|
status: string;
|
|
started_at: string | null;
|
|
finished_at: string | null;
|
|
error_message: string | null;
|
|
}>;
|
|
};
|
|
|
|
export type CaseAssets = {
|
|
caseId: string;
|
|
apImageUrl: string;
|
|
bucket?: string;
|
|
key?: string;
|
|
};
|
|
|
|
export type SubmitLandmarksRequest = {
|
|
// Backend Lambda requires caseId in body (even though it's also in the URL)
|
|
caseId?: string;
|
|
view: "ap";
|
|
landmarks: Record<string, { x: number; y: number }>;
|
|
};
|
|
|
|
export type SubmitLandmarksResponse = {
|
|
ok: boolean;
|
|
caseId: string;
|
|
resumedPipeline: boolean;
|
|
values: {
|
|
pelvis_offset_px: number;
|
|
t1_offset_px: number;
|
|
tp_offset_px: number;
|
|
dominant_curve: string;
|
|
};
|
|
};
|
|
|
|
export type UploadUrlResponse = {
|
|
uploadUrl: string;
|
|
key?: string;
|
|
s3Key?: string;
|
|
};
|
|
|
|
export const api = {
|
|
createCase: (body: { notes?: string } = {}) =>
|
|
http<{ caseId: string }>(`/cases`, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
}),
|
|
|
|
startCase: (caseId: string) =>
|
|
http<{ caseId: string; executionArn?: string; status?: string }>(
|
|
`/cases/${encodeURIComponent(caseId)}/start`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
}
|
|
),
|
|
|
|
getUploadUrl: (caseId: string, body: { view: string; contentType?: string; filename?: string }) =>
|
|
http<UploadUrlResponse>(`/cases/${encodeURIComponent(caseId)}/upload-url`, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
}),
|
|
|
|
getCaseStatus: (caseId: string) => http<CaseStatus>(`/cases/${encodeURIComponent(caseId)}`),
|
|
getCaseAssets: (caseId: string) => http<CaseAssets>(`/cases/${encodeURIComponent(caseId)}/assets`),
|
|
|
|
// FIX: include caseId in JSON body to satisfy backend Lambda contract
|
|
submitLandmarks: (caseId: string, body: SubmitLandmarksRequest) =>
|
|
http<SubmitLandmarksResponse>(`/cases/${encodeURIComponent(caseId)}/landmarks`, {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
...body,
|
|
}),
|
|
}),
|
|
};
|