Files
Aislo/M01_MasterData/M01_MasterData_UI_Logic_Wizard_Api.ts
T

118 lines
4.1 KiB
TypeScript

/* =============================================================================
* M01_MasterData_UI_Logic_Wizard_Api.ts
* 「새로 만들기」 모달이 쓰는 읽기 길 — 계약 `resources/master_data/_화면_계약.md` 2장
* 절 목록 API 가 서기 전 = `/tables` 를 절 번호로 찾아 원문번호가 같은 표만 남김
* ========================================================================== */
import { API_BASE_URL } from "@config/config_frontend";
import { ApiError, type ElementBrief, type LogicRow } from "./M01_MasterData_UI_Logic_Api";
export interface TableBrief {
file: string;
: string;
원문번호: string;
구분: string;
이름: string;
기준?: string;
조건?: Record<string, string>;
값칸?: Record<string, string>;
용도?: { 공종?: string; 대상?: string[]; 로직키?: string[] };
}
/** 표 조건 칸의 값만(`/table/options`) — 줄을 통째로 받지 않음 */
export interface TableInfo {
: string;
이름: string;
조건?: Record<string, string>;
값칸?: Record<string, string>;
/** 고르기 조건 = 줄에 나온 값 차례 · 범위 조건 = [[아래, 위]…] */
값?: Record<string, (string | number)[] | [number, number][]>;
}
async function postJson<T>(path: string, body: unknown): Promise<T> {
const response = await fetch(`${API_BASE_URL}/m01${path}`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: 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;
}
async function getJson<T>(path: string, params: Record<string, string>): Promise<T> {
const response = await fetch(`${API_BASE_URL}/m01${path}?${new URLSearchParams(params)}`, {
credentials: "include",
});
const data = (await response.json().catch(() => ({}))) as { detail?: unknown } & T;
if (!response.ok) throw new ApiError(response.status, data.detail ?? response.statusText);
return data;
}
/** 원문 절 한 줄 — `/sections` */
export interface SectionBrief {
book: string;
division: string;
chapter: string;
chapterNo: string;
section: string;
title: string;
/** 그 절(아래 절 포함)의 마스터 표·로직 수 — 0 이면 아직 안 올린 절 */
tables: number;
logics: number;
}
export const fetchSections = (book: string, division = ""): Promise<SectionBrief[]> =>
getJson<{ sections: SectionBrief[] }>("/sections", {
book,
...(division ? { division } : {}),
limit: "2000",
}).then((d) => d.sections);
/** 그 절(아래 절 포함)의 표 — 소요량·계수 둘을 다 봄 */
export async function fetchSectionTables(book: string, section: string): Promise<TableBrief[]> {
const found = await Promise.all(
["소요량", "계수"].map((group) =>
getJson<{ tables: TableBrief[] }>("/tables", { group, section, size: "100" }),
),
);
return found.flatMap((f) => f.tables).filter((t) => t.구분 === book);
}
const tableCache = new Map<string, Promise<TableInfo>>();
export function fetchTableInfo(file: string, key: string): Promise<TableInfo> {
let got = tableCache.get(key);
if (!got) {
got = getJson<TableInfo>("/table/options", { file, key });
tableCache.set(key, got);
}
return got;
}
/** 자체 로직 만들기 — 키(GX)는 서버가 붙임 */
export interface Made {
file: string;
version: string;
key: string;
logic: LogicRow;
}
export const createLogic = (logic: LogicRow): Promise<Made> => postJson("/logic/new", { logic });
export const copyLogic = (key: string, name: string): Promise<Made> =>
postJson("/logic/copy", { key, 이름: name });
/** 공표 직종 찾기 */
export const searchJobs = (q: string): Promise<{ items: ElementBrief[] }> =>
getJson("/pick", { kind: "job", q, limit: "100" });
/** 로직 이름·번호 찾기 — 하위 로직 고르기 */
export interface LogicBrief {
: string;
원문번호: string;
이름: string;
결과단위: string;
}
export const searchLogics = (q: string): Promise<{ logics: LogicBrief[] }> =>
getJson("/logics", { q });