feat(M01): 조달 찾기 확정 모양 · 품셈재료 가격 연결 · 건설노임 준용 직종 고르기

- 고르기 API GET /api/m01/pick (나라장터자재 · 시중물가 · 공표 직종) — 조달 찾기 옛 API 대체
- 시중물가 조달›출처 「나라장터:<열쇠>」 연결 · 끊기 뒤 변화 없음 · 조달 값·기준 칸
- 품셈재료 「가격 연결」 모달(이름·규격 미리 찾기 · 관급 표시 · 후보 조건 · 끊기) · 못 이은 줄만 보기
- 건설노임 미공표 줄 「준용」 직종 고르기

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-19 23:53:28 +09:00
co-authored by Claude Sonnet 5
parent 796b0f8efc
commit d2a6646fc4
10 changed files with 367 additions and 135 deletions
+10
View File
@@ -33,6 +33,16 @@ export function withCell(row: Row, path: string, value: unknown): Row {
return { ...row, [head]: { ...(row[head] as Row), [sub]: value } };
}
/** 줄 안 깊은 자리 읽기 · 넣은 새 줄(중간 묶음 없으면 만듦). */
export const deep = (row: Row, path: string[]): unknown =>
path.reduce<unknown>((o, k) => (o as Row | null | undefined)?.[k], row);
export function withDeep(row: Row, path: string[], value: unknown): Row {
const [head, ...rest] = path;
if (!rest.length) return { ...row, [head]: value };
return { ...row, [head]: withDeep((row[head] ?? {}) as Row, rest, value) };
}
export function show(v: unknown): string {
if (v === null || v === undefined) return "";
if (Array.isArray(v)) return v.map((x) => (x === null ? "" : String(x))).join(" ~ ");