/* ============================================================================= * 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; 값칸?: Record; 용도?: { 공종?: string; 대상?: string[]; 로직키?: string[] }; } /** 표 조건 칸의 값만(`/table/options`) — 줄을 통째로 받지 않음 */ export interface TableInfo { 키: string; 이름: string; 조건?: Record; 값칸?: Record; /** 고르기 조건 = 줄에 나온 값 차례 · 범위 조건 = [[아래, 위]…] */ 값?: Record; } async function postJson(path: string, body: unknown): Promise { 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(path: string, params: Record): Promise { 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 => getJson<{ sections: SectionBrief[] }>("/sections", { book, ...(division ? { division } : {}), limit: "2000", }).then((d) => d.sections); /** 그 절(아래 절 포함)의 표 — 소요량·계수 둘을 다 봄 */ export async function fetchSectionTables(book: string, section: string): Promise { 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>(); export function fetchTableInfo(file: string, key: string): Promise { let got = tableCache.get(key); if (!got) { got = getJson("/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 => postJson("/logic/new", { logic }); export const copyLogic = (key: string, name: string): Promise => 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 });