/* ============================================================================= * M01_MasterData_Api_Fetch.ts * 마스터 요소 화면 서버 호출 — 계약 `resources/master_data/_화면_계약.md` · 시스템 관리자만 * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; /** 줄·표 한 개 — 파일 그대로(한글 칸). */ export type Row = Record; 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; 값칸: Record; 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(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(path: string, params: Record): Promise { const query = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])); const { status, body } = await call(`${path}?${query}`); if (status !== 200) throw new Error(String(body.detail ?? `HTTP ${status}`)); return body; } export const fetchGroups = async (): Promise => (await get<{ groups: GroupInfo[] }>("/groups", {})).groups; export const fetchFiles = async (group: string): Promise => (await get<{ files: FileInfo[] }>(`/groups/${encodeURIComponent(group)}/files`, {})).files; export const fetchRows = (file: string, page: number, size: number, q: string): Promise => get("/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 { const { status, body } = await call>("/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) }; }