Merge remote-tracking branch 'origin/sub_laptop_3' into sub_laptop_1

This commit is contained in:
2026-09-29 12:34:06 +09:00
3 changed files with 5 additions and 123 deletions
@@ -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();
});
},
@@ -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<SummaryItem[]> {
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<ItemPickResult> {
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;
});
}
@@ -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);
}