Files
Aislo/M01_MasterData/M01_MasterData_UI_Combo_Api.ts
T
eomsangdonandClaude Sonnet 5 ca805b3076 fix(M01): 일위대가 조합 화면 — 계약 9장대로 맞춤 · 수량은 저장하지 않고 미리 보기 때만 보냄
- 담은 로직 줄 = {로직, 메모} 뿐 · 입력값(수량)은 화면 안 메모로만 두고 조합에 안 저장
- 서버 길 실제 이름으로 맞춤: /combo/new · /combo/edit(판본) · /combo/delete(판본) · 422 검사 글·409 낡은 판본 안내
- 미리 보기는 화면이 로직마다 부르지 않고 POST /combo/preview 한 번에 받아 비목별 합계를 찍음

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
2026-09-22 00:01:48 +09:00

107 lines
3.3 KiB
TypeScript

/* =============================================================================
* M01_MasterData_UI_Combo_Api.ts
* 일위대가 조합 화면이 부르는 서버 길 — 조합 마스터 `일위대가조합.json`(키 UA)
* 길 = `_화면_계약.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;
비고: 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[];
}
async function request<T>(path: string, body?: unknown): Promise<T> {
const response = await fetch(`${API_BASE_URL}/m01${path}`, {
method: body === undefined ? "GET" : "POST",
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;
}
export const fetchCombos = (): Promise<ComboSummary[]> =>
request<{ combos: ComboSummary[] }>("/combos").then((d) => d.combos);
export const fetchCombo = (key: string): Promise<ComboOne> =>
request(`/combo?key=${encodeURIComponent(key)}`);
export const newCombo = (combo: Combo): Promise<{ file: string; version: string; key: string }> =>
request("/combo/new", { combo, owner: "현장" });
export const editCombo = (
key: string,
version: string,
combo: Combo,
): Promise<{ file: string; version: string }> => request("/combo/edit", { key, version, combo });
export const deleteCombo = (key: string, version: string): Promise<unknown> =>
request("/combo/delete", { key, version });
/** 보기만 — 수량(inputs)은 이 부름에만 실림 · 저장 안 함 */
export const previewCombo = (
combo: Combo | string,
inputs: Record<string, Record<string, unknown>>,
): Promise<PreviewAnswer> => request("/combo/preview", { combo, inputs });