- 폼 옵션 아래 「집계 항목」 줄(이름 · 미지정 · [고르기] · [해제]) — 구조물 · 관 계열 하위 폼 · B05 · B06 한 코드 - 옵션 `visible:false` 는 폼에서 숨김(정의 · 옛 값 그대로) · [수정]은 폼이 그린 칸만 갈아 끼우고 안 그린 칸(상세 · 숨긴 칸) 보존 — 옵션 덮어쓰기 흠 고침 - 목록 줄 `.is-assigned` 색 · 툴팁 항목 이름 · 캐시는 지금 초안 기록 칸(structures · pipes) 안 summary_item - 미지정이면 칸을 안 실음 · 관은 서버 응답에 칸이 없으면 같은 측점에서 화면이 이어받음 - 항목 고르기 모달은 21-5 가 오기 전까지 같은 이름의 임시 함수 · Structures_Panel 700줄 넘어 옵션 그리기를 Panel_Options 로 나눔 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TULoa94ZFL26KU6ZqVpjkF
108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
/* =============================================================================
|
|
* 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;
|
|
});
|
|
}
|