Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
/* =============================================================================
|
|
* Z01_MasterData_Api_Fetch.ts
|
|
* 마스터 데이터 읽기 — 서버 `Z01_MasterData_Router.py`(랩탑 메인) · 시스템 관리자만
|
|
*
|
|
* 응답 모양은 2026-09-15 브레인이 못박은 약속 그대로. 읽기 전용 — 고치기는 다음 차례.
|
|
* ========================================================================== */
|
|
|
|
import { API_BASE_URL } from "@config/config_frontend";
|
|
import type { MasterColumn, OverrideState, TableMeta } from "./Z01_MasterData_UI_Cells";
|
|
|
|
export interface MasterTable {
|
|
id: string;
|
|
label: string;
|
|
key: string;
|
|
row_count: number;
|
|
}
|
|
|
|
export interface MasterFile {
|
|
id: string;
|
|
label: string;
|
|
key: string;
|
|
tables: MasterTable[];
|
|
}
|
|
|
|
/** 표가 아닌 낱값(한 값짜리 요율 따위)을 모은 마디 — 여느 표처럼 이름·값 두 칸으로 옴. */
|
|
export const VALUES_TABLE_ID = "@values";
|
|
|
|
export interface MasterGroup {
|
|
/** fingerprint = 폴더 지문(_manifest) — 어느 판으로 셈했는지 못박는 자리(부산물 아님). */
|
|
key: "logic" | "base_value" | "byproduct" | "seed" | "fingerprint";
|
|
label: string;
|
|
files: MasterFile[];
|
|
}
|
|
|
|
/** 기초단가 표만 editable·locked·formula 를 실음 — 트리 표는 읽기 전용이라 안 옴. */
|
|
export interface MasterRows extends TableMeta {
|
|
columns: MasterColumn[];
|
|
rows: Record<string, unknown>[];
|
|
total: number;
|
|
}
|
|
|
|
export interface RowsQuery {
|
|
page: number;
|
|
size: number;
|
|
q: string;
|
|
}
|
|
|
|
/** 기초단가 다섯 — 고친 값은 원본이 아니라 덮개 파일에 쌓임(품셈이 갱신돼도 안 날아감). */
|
|
export const BASE_PRICE_KINDS = ["labor", "machine", "material", "oil", "rate"] as const;
|
|
export type BasePriceKind = (typeof BASE_PRICE_KINDS)[number];
|
|
|
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
const response = await fetch(`${API_BASE_URL}${path}`, {
|
|
credentials: "include",
|
|
headers: { "Content-Type": "application/json" },
|
|
...init,
|
|
});
|
|
const data = (await response.json()) as { detail?: string } & T;
|
|
if (!response.ok) throw new Error(data.detail ?? "Request failed");
|
|
return data;
|
|
}
|
|
|
|
const pageParams = (query: RowsQuery): Record<string, string> => ({
|
|
page: String(query.page),
|
|
size: String(query.size),
|
|
q: query.q,
|
|
});
|
|
|
|
export function fetchBasePrices(kind: BasePriceKind, query: RowsQuery): Promise<MasterRows> {
|
|
const params = new URLSearchParams(pageParams(query));
|
|
return request<MasterRows>(`/master-data/base-prices/${kind}?${params}`);
|
|
}
|
|
|
|
export interface OverrideItem {
|
|
kind: BasePriceKind;
|
|
row_id: string;
|
|
row_label: string;
|
|
column: string;
|
|
column_label: string;
|
|
original: unknown;
|
|
current?: unknown;
|
|
value: unknown;
|
|
state: OverrideState;
|
|
}
|
|
|
|
/** 고친 것 모아 보기 — 서버가 source_changed → orphan → edited 차례로 정렬해 줌. */
|
|
export async function fetchOverrides(): Promise<OverrideItem[]> {
|
|
return (await request<{ items: OverrideItem[] }>("/master-data/overrides")).items;
|
|
}
|
|
|
|
/** 칸 고치기 — 값 null = 덮개에서 그 칸을 빼 원래 값으로 되돌림. 응답은 고친 뒤 그 줄. */
|
|
export function saveBasePrice(
|
|
kind: BasePriceKind,
|
|
rowId: string,
|
|
values: Record<string, unknown>,
|
|
): Promise<Record<string, unknown>> {
|
|
return request(`/master-data/base-prices/${kind}/${encodeURIComponent(rowId)}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ values }),
|
|
});
|
|
}
|
|
|
|
export async function fetchMasterTree(): Promise<MasterGroup[]> {
|
|
return (await request<{ groups: MasterGroup[] }>("/master-data/tree")).groups;
|
|
}
|
|
|
|
export function fetchMasterRows(
|
|
file: string,
|
|
table: string,
|
|
query: RowsQuery,
|
|
): Promise<MasterRows> {
|
|
const params = new URLSearchParams({ file, table, ...pageParams(query) });
|
|
return request<MasterRows>(`/master-data/rows?${params}`);
|
|
}
|