- B01 옛 「마스터 데이터」(Z01) 단추 제거 · 「마스터 요소」 이름을 「마스터 데이터」 로
- 재료 › 시중물가 줄마다 「조달 찾기」 — 나라장터자재 + 시중물가 조달 줄을 이름·규격으로 찾아 「나라장터:<열쇠>」 연결 · 노랑 표시 · 연결 끊기
- GET /api/m01/procurement 추가 · 값 묶음(조달{…}) 확정 전이라 연결은 조달›연결 칸에 임시로 담음
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
/* =============================================================================
|
|
* M01_MasterData_Api_Fetch.ts
|
|
* 마스터 요소 화면 서버 호출 — 계약 `resources/master_data/_화면_계약.md` · 시스템 관리자만
|
|
* ========================================================================== */
|
|
|
|
import { API_BASE_URL } from "@config/config_frontend";
|
|
|
|
/** 줄·표 한 개 — 파일 그대로(한글 칸). */
|
|
export type Row = Record<string, unknown>;
|
|
|
|
export interface GroupInfo {
|
|
group: string;
|
|
files: number;
|
|
rows: number;
|
|
}
|
|
|
|
export interface FileInfo {
|
|
file: string;
|
|
book: string | null;
|
|
chapter: string;
|
|
edition: string | null;
|
|
rows: number;
|
|
version: string;
|
|
}
|
|
|
|
export interface RowsPage {
|
|
file: string;
|
|
version: string;
|
|
total: number;
|
|
page: number;
|
|
size: number;
|
|
rows: Row[];
|
|
}
|
|
|
|
export interface TableHead {
|
|
열쇠: string;
|
|
이름: string;
|
|
기준: string;
|
|
출처: string;
|
|
조건: Record<string, string>;
|
|
값칸: Record<string, string>;
|
|
count: number;
|
|
}
|
|
|
|
export interface Change {
|
|
op: "edit" | "add" | "delete";
|
|
key?: string;
|
|
row?: Row;
|
|
}
|
|
|
|
export interface FileChanges {
|
|
file: string;
|
|
version: string;
|
|
changes: Change[];
|
|
}
|
|
|
|
/** 저장 결과 — 200 은 새 판본 · 나머지는 화면이 그대로 안내할 상태. */
|
|
export type SaveResult =
|
|
| { status: 200; files: { file: string; version: string }[] }
|
|
| { status: 409; stale: string[] }
|
|
| { status: 422; errors: string[] }
|
|
| { status: number; detail: string };
|
|
|
|
async function call<T>(path: string, init: RequestInit = {}): Promise<{ status: number; body: T }> {
|
|
const response = await fetch(`${API_BASE_URL}/m01${path}`, {
|
|
credentials: "include",
|
|
headers: { "Content-Type": "application/json" },
|
|
...init,
|
|
});
|
|
return { status: response.status, body: (await response.json()) as T };
|
|
}
|
|
|
|
async function get<T>(path: string, params: Record<string, string | number>): Promise<T> {
|
|
const query = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)]));
|
|
const { status, body } = await call<T & { detail?: unknown }>(`${path}?${query}`);
|
|
if (status !== 200) throw new Error(String(body.detail ?? `HTTP ${status}`));
|
|
return body;
|
|
}
|
|
|
|
export const fetchGroups = async (): Promise<GroupInfo[]> =>
|
|
(await get<{ groups: GroupInfo[] }>("/groups", {})).groups;
|
|
|
|
export const fetchFiles = async (group: string): Promise<FileInfo[]> =>
|
|
(await get<{ files: FileInfo[] }>(`/groups/${encodeURIComponent(group)}/files`, {})).files;
|
|
|
|
export const fetchRows = (file: string, page: number, size: number, q: string): Promise<RowsPage> =>
|
|
get<RowsPage>("/rows", { file, page, size, q });
|
|
|
|
export const fetchTables = (
|
|
file: string,
|
|
q: string,
|
|
): Promise<{ version: string; tables: TableHead[] }> => get("/tables", { file, q });
|
|
|
|
export const fetchTable = (
|
|
file: string,
|
|
key: string,
|
|
): Promise<{ version: string; table: Row & { 줄: Row[] } }> => get("/table", { file, key });
|
|
|
|
export async function saveFiles(files: FileChanges[]): Promise<SaveResult> {
|
|
const { status, body } = await call<Record<string, unknown>>("/save", {
|
|
method: "POST",
|
|
body: JSON.stringify({ files }),
|
|
});
|
|
const detail = body.detail as { stale?: string[]; errors?: string[] } | string | undefined;
|
|
if (status === 200) return { status, files: body.files as { file: string; version: string }[] };
|
|
if (status === 409 && typeof detail === "object") return { status, stale: detail.stale ?? [] };
|
|
if (status === 422 && typeof detail === "object") return { status, errors: detail.errors ?? [] };
|
|
return { status, detail: typeof detail === "string" ? detail : JSON.stringify(detail) };
|
|
}
|
|
|
|
export interface ProcureItem {
|
|
ref: string;
|
|
이름: string;
|
|
규격: string;
|
|
단위: string;
|
|
값: unknown;
|
|
}
|
|
|
|
export const fetchProcurement = (q: string): Promise<{ total: number; items: ProcureItem[] }> =>
|
|
get("/procurement", { q, limit: 50 });
|