From fdb07fa4abedccc79044db1a91794179529b33d9 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 4 Oct 2026 17:48:58 +0900 Subject: [PATCH 1/2] =?UTF-8?q?style(M02):=20=EB=A9=B4=EC=A0=81=EC=82=B0?= =?UTF-8?q?=EC=B6=9C=20=EC=B9=B4=EB=93=9C=20[=EC=95=8C=EC=95=BD]=20?= =?UTF-8?q?=EC=BC=AC=20=EB=8B=A8=EC=B6=94=20=EA=B0=95=EC=A1=B0=EC=83=89=20?= =?UTF-8?q?=E2=80=94=20=EC=BC=AC=20=C2=B7=20=EB=81=94=EC=9D=B4=20=EB=8B=A8?= =?UTF-8?q?=EC=B6=94=EC=97=90=EC=84=9C=20=EB=B3=B4=EC=9E=84=20(PLAN=2039-4?= =?UTF-8?q?=20D1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4 --- M02_MasterTemplete/M02_MasterTemplete_Structure.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Structure.css b/M02_MasterTemplete/M02_MasterTemplete_Structure.css index 57eaacbce..6b54347fd 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Structure.css +++ b/M02_MasterTemplete/M02_MasterTemplete_Structure.css @@ -252,3 +252,9 @@ .m02-struct__sheet.is-pills-off .ss-links { display: none; } + +/* [알약] 켬 — 눌린 단추를 강조색으로(밝음 · 어두움 같은 토큰) */ +.m02-struct__card-tools .ui-btn[aria-pressed="true"] { + border-color: var(--color-accent); + color: var(--color-accent); +} From d21f258da6be613a7ff24ec83df2bd22b0323e9f Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 4 Oct 2026 17:52:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat(M02):=20=EB=A9=B4=EC=A0=81=EC=82=B0?= =?UTF-8?q?=EC=B6=9C=20=EC=95=8C=EC=95=BD=20=3D=20=EC=9D=BC=EC=9C=84?= =?UTF-8?q?=EB=8C=80=EA=B0=80=20pills=20=EA=B8=B8=20=E2=80=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=AC=BC=EB=9F=89=20=E3=80=8C=EC=9D=B4=EB=A6=84|?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=E3=80=8D=20+=20=EC=9E=90=EC=9E=AC=20?= =?UTF-8?q?=E3=80=8C=EC=9E=90=EC=9E=AC|MT=ED=82=A4=E3=80=8D=20=C2=B7=20?= =?UTF-8?q?=EB=8B=A4=20=EA=B3=A0=EC=A0=95=20=C2=B7=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=95=8C=EC=95=BD=20=EB=B9=A0=EC=A7=90=20?= =?UTF-8?q?(PLAN=2039-4=20D7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4 --- .../M02_MasterTemplete_Api_Fetch.ts | 14 ++++++++ .../M02_MasterTemplete_Structure.ts | 35 +++++++++---------- resources/tester/test_m02_card_sheet_edit.py | 12 +++++++ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts b/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts index 6ac97e28e..801fa9c18 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts @@ -301,6 +301,20 @@ export const copyStructure = async (column: string, from: string): Promise await scoped(`/structures/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" }); }; +/** 면적산출 알약 하나 — 로직 물량 「이름|단위」(`로직` · `배`) · 자재 「자재|MT키」(`자재`) */ +export interface LaborPill { + 키: string; + 이름: string; + 단위: string; + 로직?: string[]; + 배?: number; + 자재?: { 코드: string; 이름: string; 규격: string; 단위: string }; +} + +/** 그 열 일위대가의 면적산출 알약(저장본 · PLAN 39-1a pills 길) — 지금 층 */ +export const fetchLaborPills = (column: string): Promise<{ 열: string; 알약: LaborPill[] }> => + call(owned(activeLayer, `/layers/${activeLayer}/labor/${enc(column)}/pills`)); + /** 다른 열(`from`) 면적산출 내용(알약 연결 포함)을 받는 열(`column`)로 가져옴 — 면적번 · 열은 그대로 */ export const copyArea = async (column: string, from: string): Promise => { await scoped(`/area/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" }); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Structure.ts b/M02_MasterTemplete/M02_MasterTemplete_Structure.ts index 30a1b1bea..6576d077c 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Structure.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Structure.ts @@ -24,14 +24,14 @@ import { import { openModal } from "@ui/ui_template_modal"; import type { LinkOptions } from "../A00_Common/spreadsheet/spreadsheet_links"; import { searchMaterials } from "../M01_MasterData/M01_MasterData_Api_Fetch"; -import { fetchComboInputs, fetchUsableCombos } from "./M02_MasterTemplete_Labor_Api"; +import { fetchUsableCombos } from "./M02_MasterTemplete_Labor_Api"; import { copyArea, copyMaterial, copyStructure, + fetchLaborPills, fetchStructureNumbers, getActiveLayer, - readTemplate, saveTemplate, type StructureNumbers, type TreeEdit, @@ -67,9 +67,8 @@ const TEXT = { pickCopy: "{value} 복사해 가져오기", copied: "복사해 가져옴", copyFailed: "복사 실패 — {value}", - noPrice: "이 열에 일위대가가 없어 알약 없음 — 구조물 조합 일위대가 줄에서 고름", - noInputs: "일위대가 {value} 에 입력값이 없어 알약 없음", - inputsFailed: "일위대가 입력값을 못 받음 — {value}", + noPills: "이 열 일위대가에 담은 로직 · 자재가 없어 알약 없음 — 일위대가 상세에서 담음", + pillsFailed: "일위대가 알약을 못 받음 — {value}", discardCards: "카드(도면 · 면적산출 · 재료산출)에 저장 안 한 고침이 있음 — 버리고 넘어갈까?", }; @@ -275,7 +274,8 @@ export const columnHead = (col: StructureColumn): string => export const structureLabel = (number: string, col: StructureColumn): string => [number, columnHead(col)].filter(Boolean).join(" "); -/** 면적산출 · 재료산출 편집 화면 위 알약 막대 — 면적 = 그 열 일위대가 입력값(물량) · 재료 = 자재DB 찾기. +/** 면적산출 · 재료산출 편집 화면 아래 알약 막대 — 면적 = 그 열 일위대가의 로직 물량 「이름|단위」 + 자재 「자재|MT키」 + * (서버 pills 길 · 39-4 · 다 `고정` — 더하기 · 빼기는 일위대가 쪽) · 재료 = 자재DB 찾기. * 알약이 없으면 까닭 글(`notice`)을 같이 돌려줌 */ export async function pillsFor( kind: SheetKind, @@ -285,20 +285,19 @@ export async function pillsFor( const own = await inputPills(column, "재료"); return { links: { 항목: own, 모드: "자재", 자재찾기: searchMaterials } }; } - const own = await inputPills(column, "면적"); // 선택 입력 카드가 면적산출로 고른 입력 - const none = (notice: string) => ({ links: { 항목: own, 모드: "입력" as const }, notice }); - const table = await readTemplate(getActiveLayer(), "table", STRUCTURE_TABLE, null).catch( - () => null, - ); - const cols = (table?.문서 as { 열?: { id: string; 일위대가?: string | null }[] } | null)?.열; - const combo = cols?.find((c) => c.id === column)?.일위대가; - if (!combo) return none(TEXT.noPrice); + const none = (notice: string) => ({ links: { 항목: [], 모드: "입력" as const }, notice }); try { - const got = await fetchComboInputs(combo); - const 항목 = [...got.물량.map((q) => ({ 키: q.키, 이름: q.이름, 단위: q.단위 })), ...own]; - return 항목.length ? { links: { 항목, 모드: "입력" } } : none(fill(TEXT.noInputs, combo)); + const { 알약 } = await fetchLaborPills(column); + const 항목 = 알약.map((p) => ({ + 키: p.키, + 이름: p.이름, + 단위: p.단위, + ...(p.자재 ? { 자재: p.자재 } : {}), + 고정: true, + })); + return 항목.length ? { links: { 항목, 모드: "입력" } } : none(TEXT.noPills); } catch (error) { - return none(fill(TEXT.inputsFailed, why(error))); + return none(fill(TEXT.pillsFailed, why(error))); } } diff --git a/resources/tester/test_m02_card_sheet_edit.py b/resources/tester/test_m02_card_sheet_edit.py index 6dbe46cf5..8c450a75d 100644 --- a/resources/tester/test_m02_card_sheet_edit.py +++ b/resources/tester/test_m02_card_sheet_edit.py @@ -89,3 +89,15 @@ def test_면적산출_카드_알약_줄_켬끔_기본_숨김(): assert '"m02-card-pills": { bucket: "pref", scope: "global" },' in state # 상세(전체 편집기)는 켬끔 없음 — 늘 보임 assert "is-pills-off" not in _text(M02 / "M02_MasterTemplete_UI_Main.ts") + + +def test_면적산출_알약은_일위대가_pills_길(): + """39-4 D7 — 로직 물량 「이름|단위」 + 자재 「자재|MT키」 · 다 고정 · 선택 입력 알약 빠짐.""" + struct = _text(M02 / "M02_MasterTemplete_Structure.ts") + pills = struct.split("export async function pillsFor")[1].split("async function cardLinks")[0] + area = pills.split('if (kind === "material")')[1].split("\n }\n", 1)[1] + assert "await fetchLaborPills(column)" in area + assert "고정: true" in area and "...(p.자재 ? { 자재: p.자재 } : {})" in area + assert "inputPills" not in area and "fetchComboInputs" not in struct + api = _text(M02 / "M02_MasterTemplete_Api_Fetch.ts") + assert "/layers/${activeLayer}/labor/${enc(column)}/pills" in api