- 담은 로직 줄 = {로직, 메모} 뿐 · 입력값(수량)은 화면 안 메모로만 두고 조합에 안 저장
- 서버 길 실제 이름으로 맞춤: /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
107 lines
3.3 KiB
TypeScript
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 });
|