/* ============================================================================= * M01_MasterData_UI_Combo_Api.ts * 일위대가 조합 화면이 부르는 서버 길 — 조합 마스터 `일위대가조합.json`(키 UA) * 길 = M02 층 길(`/api/m02/layers/system/combos` · PLAN 27-2a) · 모양 = `_화면_계약.md` 9장 * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; import { ApiError } from "./M01_MasterData_UI_Logic_Api"; /** 담은 로직 한 줄 — 로직 키와 메모뿐(수량·계산은 마스터에 두지 않음 · 계약 9장) */ export interface ComboLogic { 로직: string; 메모: string | null; } /** 조합 한 줄 통째 — `_틀.md` 10장 · 키·소유는 서버 것이 이김 */ export interface Combo { 키: string; 이름: string; 구분: string | null; 상세구분: string | null; 단위: string | null; 담은로직: ComboLogic[]; 출처: string | null; 소유: string | null; /** 이 조합을 본떠 만든 원본 키 — 비면 원본(M02 가 갈라 낸 조합에 채워짐) */ 본뜬키: string | null; 비고: string | null; } export interface ComboSummary extends Combo { count: number; blocked: boolean; reasons: string[]; } /** `GET /combo` 의 `줄` — 담은 로직에 이름을 붙인 것 */ export interface ComboLine { 차례: number; 로직: string; 메모: string | null; ok: boolean; 까닭?: string; 이름?: string; 구분?: string; 상세구분?: string; 결과단위?: string; } export interface ComboOne { file: string; version: string; combo: Combo; 줄: ComboLine[]; } export interface PreviewLine { 차례: number; 로직: string; 이름: string; ok: boolean; 까닭?: string; 갈래?: string; 결과?: number; 노무비?: number; 재료비?: number; 경비?: number; 계?: number; } export interface PreviewAnswer { 줄: PreviewLine[]; 노무비: number; 재료비: number; 경비: number; 계: number; 멈춤: string[]; } /** 조합 길 = M02 층 길 마스터(`/api/m02/layers/system/combos` · PLAN 27-2a) */ async function request(path: string, method = "GET", body?: unknown): Promise { const response = await fetch(`${API_BASE_URL}/m02/layers/system/combos${path}`, { method, credentials: "include", headers: { "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), }); const data = (await response.json().catch(() => ({}))) as { detail?: unknown } & T; if (!response.ok) throw new ApiError(response.status, data.detail ?? response.statusText); return data; } const FILE = "일위대가조합.json"; const q = (key: string): string => `/${encodeURIComponent(key)}`; export const fetchCombos = (): Promise => request<{ 줄: ComboSummary[] }>("").then((d) => d.줄); export const fetchCombo = (key: string): Promise => request & { 판: string }>(q(key)).then((d) => ({ ...d, file: FILE, version: d.판, })); export const newCombo = (combo: Combo): Promise<{ file: string; version: string; key: string }> => request<{ 키: string; 판: string }>("", "POST", { combo: { ...combo, 소유: "현장" } }).then( (d) => ({ file: FILE, version: d.판, key: d.키 }), ); export const editCombo = ( key: string, version: string, combo: Combo, ): Promise<{ file: string; version: string }> => request<{ 판: string }>(q(key), "PUT", { combo, 판: version }).then((d) => ({ file: FILE, version: d.판, })); export const deleteCombo = (key: string, version: string): Promise => request(`${q(key)}?판=${encodeURIComponent(version)}`, "DELETE"); /** 보기만 — 수량(inputs)은 이 부름에만 실림 · 저장 안 함 */ export const previewCombo = ( combo: Combo | string, inputs: Record>, ): Promise => request("/preview", "POST", { combo, inputs });