- 열 한 벌에 「본뜬키」(소유 다음 · 비고 앞) — 비면 원본 · 있으면 본뜬 원본 조합 키 - 서버가 없는 키 · 자기 키를 400 으로 막고 검사(check_master)도 같은 규칙 - 정본 조합 37 줄에 본뜬키 null 채움 · 목록 「본뜬키」 열 · 상세 「원본 / 본뜬키」 줄 - _틀.md 10장 · _화면_계약.md 9장 갱신 · 시험 하나 더함 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoyUwuTFiDJfvCfHpvKKzp
109 lines
3.4 KiB
TypeScript
109 lines
3.4 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;
|
|
/** 이 조합을 본떠 만든 원본 키 — 비면 원본(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[];
|
|
}
|
|
|
|
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 });
|