/* ============================================================================= * M02_MasterTemplete_TreePanel_Roots.ts * 라이브러리 층 단추 셋(마스터 · 회사 · 개인 · PLAN 29-2) — 좌측 패널 「일위대가 라이브러리」 · * 「구조물 조합」 아래 한 줄 · 나무 종류와 무관한 공용 부품. 고른 층의 나무는 부르는 쪽이 이 줄 아래에 붙임. * 권한 없는 층 = 단추 글 끝에 「읽기」(권한 = `accessFor` · 서버 `M02_Layer_Access` 와 짝) * 회사 = 볼 회사(`viewCompany` — 시스템 관리자는 해시 `company` · `aislo.adminCompany` · 자기 회사) * 시스템 관리자가 남의 회사를 보면 그 회사 관리자처럼(PLAN 29-6) — 「개인」 = 그 회사 사람 나무 * (`createPeopleTree` · 사람 줄 → 그 사람 라이브러리 · 읽기) · 자기 개인 층은 자기 회사를 고를 때만 * 누르면 `go`(부르는 쪽이 해시로 다시 엶). * ========================================================================== */ import { el } from "@ui/ui_template_elements"; import { t as L } from "@ui/ui_template_locale"; import { createTreeRow } from "@ui/ui_template_tree"; import type { SessionUser } from "../A06_Login/A06_Login_Api_Fetch"; import { fetchOwners, type LayerOwner } from "./M02_MasterTemplete_Api_Fetch"; import { accessFor, buildNeedCompany, layerOfRoot, ownOwner, type Root, } from "./M02_MasterTemplete_Layer"; export interface LayerBarOptions { user: SessionUser | null; /** 지금 고른 층 — `owner` null = 자기 층 */ active: { root: Root; owner: string | null }; /** 볼 회사 id — 없으면 회사 · 개인 단추 없음 */ company: string | null; /** 다른 층을 누름 — `owner` null = 자기 층(남의 회사 「개인」 = 부르는 쪽이 첫 사람으로) */ go: (root: Root, owner: string | null) => void; /** 단추 옆 「+」(고른 층 나무 맨 위 폴더 · 항목 더하기 · PLAN 29-6) */ add?: HTMLElement; /** 켜진 단추를 다시 누름 — 그 층 목록 화면으로(열어 둔 항목 · 조합에서 · PLAN 30-4) */ reselect?: () => void; } /** 회사 단추의 주인 — 볼 회사가 자기 회사면 null(자기 층) */ export const companyOwner = (company: string | null, user: SessionUser | null): string | null => company && company !== ownOwner("company", user) ? company : null; /** 시스템 관리자가 남의 회사를 봄 — 「개인」 단추 = 그 회사 사람 나무(읽기) */ export const seesOthers = (company: string | null, user: SessionUser | null): boolean => user?.role === "SYSTEM_ADMIN" && !!companyOwner(company, user); export function createLayerBar(opt: LayerBarOptions): HTMLElement { const { user, active } = opt; const isSystem = user?.role === "SYSTEM_ADMIN"; const ownPersonal = ownOwner("personal", user); const others = seesOthers(opt.company, user); if (!opt.company && !isSystem) { return el("div", { className: "m02-layers", children: [button("master", null), buildNeedCompany()], }); } function button(root: Root, owner: string | null, people = false): HTMLButtonElement { const readOnly = people || !accessFor(layerOfRoot(root), owner, user).canWrite; const name = L( root === "master" ? "M02_PickerSystem" : root === "company" ? "M02_PickerCompany" : "M02_PickerPersonal", ); const current = root === active.root && (root !== "personal" || people || !active.owner); const b = el("button", { className: `m02-layers__btn${current ? " is-active" : ""}`, text: readOnly ? `${name} · ${L("M02_RootReadOnly")}` : name, attrs: { type: "button", "data-root": root, "aria-pressed": String(current) }, }); b.addEventListener("click", () => { if (!current) opt.go(root, owner); else opt.reselect?.(); }); return b; } const buttons = [ button("master", null), ...(opt.company ? [button("company", companyOwner(opt.company, user))] : []), ...(others ? [button("personal", null, true)] : ownPersonal ? [button("personal", null)] : []), ]; const row = el("div", { className: "m02-layers__row", children: [...buttons, ...(opt.add ? [opt.add] : [])], }); return el("div", { className: "m02-layers", children: [row] }); } export interface PeopleTreeOptions { /** 볼 회사 id */ company: string; /** 지금 고른 사람의 개인 층 주인 `{C}/{U}` */ active: string; /** 고른 사람 줄 아래에 붙일 그 사람 라이브러리 나무 */ panel: HTMLElement; /** 다른 사람을 누름 */ go: (owner: string) => void; } /** 그 회사 사람 나무 — 사람 줄(층 폴더 있는 사람) → 고른 사람 줄 아래 그 사람 라이브러리(읽기) · * 지금 고른 사람은 목록에 없어도 줄을 세움 */ export function createPeopleTree(opt: PeopleTreeOptions): HTMLElement { const box = el("div", { className: "m02-people" }); const paint = (list: LayerOwner[]): void => { const rows = list.some((one) => one.owner === opt.active) ? list : [...list, { owner: opt.active, name: opt.active }]; const wrap = el("div", { className: "m02-people__panel", children: [opt.panel] }); box.replaceChildren( ...rows.flatMap((one) => { const current = one.owner === opt.active; const node = { id: one.owner, label: one.name, count: null, folder: true }; const row = createTreeRow(node, current ? "▾" : "▸", 0, () => { if (!current) return opt.go(one.owner); wrap.hidden = !wrap.hidden; const caret = row.querySelector(".ui-tree__caret"); if (caret) caret.textContent = wrap.hidden ? "▸" : "▾"; }); row.classList.toggle("is-active", current); return current ? [row, wrap] : [row]; }), ); }; paint([]); void fetchOwners("personal", opt.company) .then(paint) .catch(() => undefined); // 목록을 못 받아도 고른 사람 나무는 보임 return box; }