/* ============================================================================= * B01_Dashboard_UI_View_SysCompanies.ts * 시스템 관리 › 회사(549-2) — 회사 목록(찾기 · 줄 끝 ⋯) → 회사 상세(정보 · 사람 · 프로젝트 탭) · [+ 회사]. * 상세 = 주소 `&company=`(뒤로 가기 됨) · 수정 · 로고 · 새 회사 = 지금 창(Modals) 그대로. * ========================================================================== */ import { createButton } from "@ui/ui_template_elements"; import { currentQuery } from "../A00_Common/router"; import { fetchAllCompanies, fetchAllUsers, fetchCompanyProjects, setCompanyLogo, type CompanyInfo, type ProjectItem, } from "./B01_Dashboard_Api_Fetch"; import { openAssetPicker } from "./B01_Dashboard_UI_AssetPicker"; import { formatDate } from "./B01_Dashboard_UI_Common"; import { filterCompanies } from "./B01_Dashboard_UI_CompanyPick"; import { openCreateCompanyModal, openEditCompanyModal } from "./B01_Dashboard_UI_Modals"; import { viewHref } from "./B01_Dashboard_UI_Nav"; import { badge, dataTable, emptyState, nameCell, pageFrame, primaryButton, rowMenu, settingCard, settingRow, tabBar, valueText, } from "./B01_Dashboard_UI_PageKit"; import { el, loadPage, searchBox, toolbar } from "./B01_Dashboard_UI_View_Parts"; import type { ViewCtx } from "./B01_Dashboard_UI_Views"; import { roleText, statusBadgeOf } from "./logic/B01_Dashboard_Logic_Pages"; export const KIND_KO: Record = { ROAD: "임도설계", PLAN: "예정노선", STABILITY: "안정성평가", EROSION: "사방설계", }; const statusTag = (s?: string | null): HTMLElement => badge(s || "-", s === "활동중" ? "success" : "neutral"); const logoLabel = (c: CompanyInfo): string => (c.logo_asset_id ? "로고 변경…" : "로고 지정…"); function pickLogo(ctx: ViewCtx, company: CompanyInfo, after?: () => void): void { void openAssetPicker("LOGO", company.id, ctx.me, company.logo_asset_id ?? null, async (id) => { await setCompanyLogo(id, company.id); company.logo_asset_id = id; after?.(); }); } /** 이름 칸 — 이름은 상세로 가는 링크(뒤로 가기 · 새 탭 됨) + 아래 작은 주소 */ function linkedName(c: CompanyInfo): HTMLElement { const link = el("a", "b01-pg-name__main", c.name); link.href = viewHref("sys-companies", { company: String(c.id) }); const cell = nameCell(c.name, c.business_address); cell.firstElementChild!.replaceWith(link); return cell; } const LIST_COLUMNS = [ { label: "회사" }, { label: "사업자등록번호" }, { label: "대표자" }, { label: "상태" }, { label: "사용자", align: "end" as const }, { label: "프로젝트", align: "end" as const }, { label: "" }, ]; function companyList(stage: HTMLElement, ctx: ViewCtx, companies: CompanyInfo[]): void { const page = pageFrame( stage, "회사", "등록된 회사를 보고 정보 · 로고를 고칩니다. 이름을 누르면 상세입니다.", ); page.setAction(primaryButton("회사", () => openCreateCompanyModal())); const body = el("div"); const draw = (query: string): void => { const shown = filterCompanies(companies, query); if (!shown.length) { body.replaceChildren( companies.length ? emptyState({ icon: "search", text: "찾는 회사가 없습니다." }) : emptyState({ icon: "building", text: "등록된 회사가 없습니다.", action: primaryButton("회사", () => openCreateCompanyModal()), }), ); return; } body.replaceChildren( dataTable( LIST_COLUMNS, shown.map((c) => [ linkedName(c), c.business_registration_number ?? "-", c.business_owner ?? "-", statusTag(c.business_status), String(c.user_count ?? 0), String(c.project_count ?? 0), rowMenu([ { label: "상세 보기", onClick: () => ctx.go("sys-companies", { company: String(c.id) }), }, { label: "정보 수정", onClick: () => openEditCompanyModal(c) }, { label: logoLabel(c), onClick: () => pickLogo(ctx, c) }, ]), ]), ), ); }; draw(""); page.body.append(toolbar(searchBox("회사 이름 · 사업자번호 찾기", draw)), body); } const projectTable = (projects: ProjectItem[]): HTMLElement => projects.length ? dataTable( [ { label: "프로젝트" }, { label: "종류" }, { label: "지역" }, { label: "단계" }, { label: "담당" }, { label: "수정일" }, ], projects.map((p) => [ p.name, KIND_KO[p.project_kind ?? "ROAD"] ?? "-", p.region ?? "-", `${p.workflow_stage} 단계`, p.owner_name ?? "-", formatDate(p.updated_at), ]), ) : emptyState({ icon: "folder", text: "이 회사의 프로젝트가 없습니다." }); async function companyDetail( stage: HTMLElement, ctx: ViewCtx, company: CompanyInfo, ): Promise { const [users, projects] = await Promise.all([ fetchAllUsers(company.id).catch(() => []), fetchCompanyProjects(company.id).catch(() => []), ]); const page = pageFrame(stage, company.name, "회사 상세"); page.setAction( createButton({ label: "정보 수정", onClick: () => openEditCompanyModal(company) }), ); const back = el("button", "b01-sys-back", "‹ 회사 목록"); back.type = "button"; back.addEventListener("click", () => ctx.go("sys-companies")); const logoBtn = createButton({ label: logoLabel(company), variant: "ghost", onClick: () => pickLogo(ctx, company, () => (logoBtn.textContent = logoLabel(company))), }); const info = settingCard([ settingRow("회사 이름", "", valueText(company.name)), settingRow("사업자등록번호", "", valueText(company.business_registration_number)), settingRow("대표자", "", valueText(company.business_owner)), settingRow("주소", "", valueText(company.business_address)), settingRow("상태", "", statusTag(company.business_status)), settingRow("로고", "도면 표제란에 들어갑니다.", logoBtn), ]); const people = users.length ? dataTable( [ { label: "이름" }, { label: "직급" }, { label: "부서" }, { label: "역할" }, { label: "상태" }, { label: "" }, ], users.map((u) => { const st = statusBadgeOf(u.status); return [ nameCell(u.name, u.email), u.position ?? "-", u.department ?? "-", roleText(u.role), badge(st.text, st.tone), rowMenu([ { label: "사용자 페이지에서 보기", onClick: () => ctx.go("sys-users", { company: String(company.id), q: u.email }), }, ]), ]; }), ) : emptyState({ icon: "users", text: "이 회사에 소속된 사용자가 없습니다." }); const panes: Record = { info, people, projects: projectTable(projects) }; const pane = el("div"); pane.append(info); const tabs = tabBar( [ { key: "info", label: "정보" }, { key: "people", label: `사람 ${users.length}` }, { key: "projects", label: `프로젝트 ${projects.length}` }, ], "info", (key) => pane.replaceChildren(panes[key]), ); page.body.append(back, tabs.root, pane); } export function mountSysCompanies(host: HTMLElement, ctx: ViewCtx): void { const id = Number(currentQuery().get("company")); loadPage(host, async (stage) => { const companies = await fetchAllCompanies(); const company = id ? companies.find((c) => c.id === id) : undefined; if (company) await companyDetail(stage, ctx, company); else companyList(stage, ctx, companies); }); }