diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel.ts b/B05_Profile/B05_Profile_UI_Structures_Panel.ts index 8f691b3a..eb4ab118 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel.ts @@ -24,7 +24,7 @@ import type { PipeFacility } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { type FacilityAttributes } from "./B05_Profile_UI_Drainage_Facility"; import { field } from "./B05_Profile_UI_Structures_Fields"; import { renderOptionFields as renderOptionFieldsInto } from "./B05_Profile_UI_Structures_Panel_Options"; -import { openItemPicker } from "./B05_Profile_UI_Structures_Summary_Picker"; +import { openItemPicker } from "@ui/ui_template_item_picker"; import { buildStructuresForm } from "./B05_Profile_UI_Structures_Form"; import { bindStructuresEvents, @@ -474,9 +474,10 @@ export function createStructuresSection( // (새 항목은 종전대로 [추가]가 확정). 안 고른 채 닫으면 그대로. summary.bind({ pick: () => { - void openItemPicker({ current: summary.get() }).then((result) => { - if (!result) return; - summary.set("item" in result ? result.item : null); + // canCopy — 개인 층에 쓸 수 없는 사용자의 복사는 서버가 막은 까닭을 모달이 그대로 보임 + void openItemPicker({ current: summary.get(), canCopy: true }).then((result) => { + if (result.action === "cancel") return; + summary.set(result.action === "apply" ? result.item : null); liveCommit(); }); }, diff --git a/B05_Profile/B05_Profile_UI_Structures_Summary_Picker.ts b/B05_Profile/B05_Profile_UI_Structures_Summary_Picker.ts deleted file mode 100644 index 9ae4edfe..00000000 --- a/B05_Profile/B05_Profile_UI_Structures_Summary_Picker.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* ============================================================================= - * B05_Profile_UI_Structures_Summary_Picker.ts - * ⚠ **임시(가짜) 항목 고르기 모달** — 21-5(sub1) `ui_template/ui_template_item_picker.ts` 의 - * `openItemPicker()` 가 오면 이 파일을 지우고 `Summary_Row` 를 쓰는 패널의 import 한 줄만 바꾼다. - * - * 이름 · 돌려주는 값은 21-5 와 맞춘 자리다: - * openItemPicker({ current }) → Promise<{ item } | { clear: true } | null> - * (item = 21-1 `summary_item` · clear = [해제] · null = 닫음) - * 목록은 `GET /api/m02/items?all=1`(21-2) 을 읽고, 아직 없으면(404 등) 같은 21-1 모양의 가짜 세 줄. - * ========================================================================== */ - -import { API_BASE_URL } from "@config/config_frontend"; -import { createButton, el } from "@ui/ui_template_elements"; -import { openModal } from "@ui/ui_template_modal"; -import { - sameSummaryItem, - summaryLabel, - type SummaryItem, - type SummaryLayer, -} from "../common_util/common_util_summary_item"; - -export type ItemPickResult = { item: SummaryItem } | { clear: true } | null; - -interface ItemRow { - key: string; - 이름?: string; - 종류?: string; - 분류1?: string; - layer?: SummaryLayer; - owner?: string | null; -} - -/** 21-2 서버가 없을 때의 가짜 응답 — 21-1 모양. */ -const FAKE_ITEMS: SummaryItem[] = [ - { layer: "system", owner: null, key: "SS000001", name: "돌기슭막이 메쌓기(가짜)" }, - { layer: "company", owner: "가짜회사", key: "SS000002", name: "옹벽 콘크리트(가짜)" }, - { layer: "personal", owner: "가짜회사/가짜사용자", key: "SS000003", name: "돌쌓기 찰쌓기(가짜)" }, -]; - -async function loadItems(): Promise { - try { - const response = await fetch(`${API_BASE_URL}/m02/items?all=1`, { credentials: "include" }); - if (!response.ok) return FAKE_ITEMS; - const rows = (await response.json()) as ItemRow[]; - if (!Array.isArray(rows) || !rows.length) return FAKE_ITEMS; - return rows.map((row) => ({ - layer: row.layer ?? "system", - owner: row.owner ?? null, - key: row.key, - name: row.분류1 ? `${row.종류 ?? ""} · ${row.분류1} · ${row.key}` : (row.종류 ?? row.key), - })); - } catch { - return FAKE_ITEMS; - } -} - -export function openItemPicker(options: { current: SummaryItem | null }): Promise { - return new Promise((resolve) => { - let settled = false; - const finish = (result: ItemPickResult, close?: () => void): void => { - if (settled) return; - settled = true; - close?.(); - resolve(result); - }; - const modal = openModal({ - title: "집계 항목 고르기(임시)", - closeLabel: "닫기", - mount: (body, close) => { - const list = el("div", { className: "b05-structure__pick-list" }); - const apply = createButton({ label: "적용", variant: "filled" }); - apply.disabled = true; - let chosen: SummaryItem | null = null; - void loadItems().then((items) => { - items.forEach((item) => { - const row = createButton({ label: summaryLabel(item), variant: "ghost" }); - row.dataset.b05PickItem = item.key; - // 지금 지정된 항목은 미리 표시(눌러서 바로 [적용] 가능) - if (sameSummaryItem(item, options.current)) { - chosen = item; - apply.disabled = false; - row.classList.add("is-picked"); - } - row.addEventListener("click", () => { - chosen = item; - apply.disabled = false; - list - .querySelectorAll(".is-picked") - .forEach((node) => node.classList.remove("is-picked")); - row.classList.add("is-picked"); - }); - list.append(row); - }); - }); - apply.addEventListener("click", () => chosen && finish({ item: chosen }, close)); - const clear = createButton({ label: "해제", variant: "ghost" }); - clear.addEventListener("click", () => finish({ clear: true }, close)); - body.append(list, el("div", { children: [apply, clear] })); - }, - beforeClose: () => { - finish(null); - return true; - }, - }); - void modal; - }); -} diff --git a/B05_Profile/B05_Profile_UI_Style_Structures.css b/B05_Profile/B05_Profile_UI_Style_Structures.css index d84fa351..ef31e1d8 100644 --- a/B05_Profile/B05_Profile_UI_Style_Structures.css +++ b/B05_Profile/B05_Profile_UI_Style_Structures.css @@ -484,15 +484,3 @@ .b05-route__irregular-item.is-assigned span { color: color-mix(in srgb, var(--color-success) 65%, var(--color-text)); } - -/* 임시 항목 고르기 모달(21-5 가 오면 사라짐) */ -.b05-structure__pick-list { - display: flex; - flex-direction: column; - gap: var(--spacing-4); - margin-bottom: var(--spacing-8); -} - -.b05-structure__pick-list .is-picked { - outline: 2px solid var(--color-success); -}