98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import React, { useMemo } from "react";
|
|
import type { MenuItem } from "@/lib/api/menu";
|
|
|
|
interface Props {
|
|
menus: MenuItem[];
|
|
selectedId: string;
|
|
}
|
|
|
|
export function MenuOverviewPanel({ menus, selectedId }: Props) {
|
|
const stats = useMemo(() => {
|
|
// 선택된 노드가 없으면 전체 기준, 있으면 L1이면 그 하위 전체, L2면 그 하위 자손
|
|
const selected = menus.find((m) => String(m.objid ?? m.OBJID) === selectedId);
|
|
const selectedLev = selected ? Number(selected.lev ?? selected.LEV ?? 1) : 0;
|
|
|
|
const descendants = (rootId: string): MenuItem[] => {
|
|
const collected: MenuItem[] = [];
|
|
const visit = (id: string) => {
|
|
for (const m of menus) {
|
|
const mid = String(m.objid ?? m.OBJID);
|
|
const pid = String(m.parent_obj_id ?? m.PARENT_OBJ_ID ?? "0");
|
|
if (pid === id) {
|
|
collected.push(m);
|
|
visit(mid);
|
|
}
|
|
}
|
|
};
|
|
visit(rootId);
|
|
return collected;
|
|
};
|
|
|
|
const scope = selected ? [selected, ...descendants(selectedId)] : menus;
|
|
|
|
const total = scope.length;
|
|
const active = scope.filter((m) => (m.status ?? m.STATUS ?? "").toString().toLowerCase() === "active").length;
|
|
const inactive = total - active;
|
|
const linked = scope.filter((m) => (m.menu_url ?? m.MENU_URL ?? "").trim().length > 0).length;
|
|
|
|
return { total, active, inactive, linked, selectedLev, selectedName: selected ? (selected.menu_name_kor ?? selected.MENU_NAME_KOR ?? "") : "" };
|
|
}, [menus, selectedId]);
|
|
|
|
return (
|
|
<div className="v5-mm-pane on v5-mm-pane-wrap">
|
|
<div style={{ marginBottom: "1.2rem" }}>
|
|
<div className="v5-mm-step" style={{ marginBottom: ".4rem" }}>
|
|
<span className="num">03</span>Overview
|
|
</div>
|
|
<h2 style={{ margin: 0, fontSize: "1.35rem", fontWeight: 700, letterSpacing: "-.025em" }}>
|
|
{stats.selectedName || "전체 메뉴"}
|
|
<span style={{ fontSize: ".72rem", color: "var(--v5-text-muted)", fontWeight: 500, marginLeft: ".55rem" }}>
|
|
· {stats.total}개 항목
|
|
</span>
|
|
</h2>
|
|
<p style={{ margin: ".35rem 0 0", fontSize: ".7rem", color: "var(--v5-text-muted)" }}>
|
|
트리에서 L1을 선택하면 해당 카테고리의 하위 전체 통계가 표시됩니다.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="v5-mm-stats">
|
|
<div className="v5-mm-stat">
|
|
<div className="v5-mm-stat-lbl">전체</div>
|
|
<div className="v5-mm-stat-val">
|
|
<span className="n">{stats.total}</span>
|
|
<span className="u">items</span>
|
|
</div>
|
|
</div>
|
|
<div className="v5-mm-stat">
|
|
<div className="v5-mm-stat-lbl">활성</div>
|
|
<div className="v5-mm-stat-val">
|
|
<span className="n ok">{stats.active}</span>
|
|
<span className="u">/ {stats.total}</span>
|
|
</div>
|
|
</div>
|
|
<div className="v5-mm-stat">
|
|
<div className="v5-mm-stat-lbl">비활성</div>
|
|
<div className="v5-mm-stat-val">
|
|
<span className="n off">{stats.inactive}</span>
|
|
<span className="u">disabled</span>
|
|
</div>
|
|
</div>
|
|
<div className="v5-mm-stat">
|
|
<div className="v5-mm-stat-lbl">URL 연결</div>
|
|
<div className="v5-mm-stat-val">
|
|
<span className="n">{stats.linked}</span>
|
|
<span className="u">linked</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ padding: "2rem 1rem", textAlign: "center", color: "var(--v5-text-muted)", fontSize: ".72rem",
|
|
border: "1px dashed var(--v5-border)", borderRadius: "10px", background: "var(--v5-surface-solid)" }}>
|
|
L2 메뉴를 클릭하면 Settings 탭으로 자동 전환되어 편집할 수 있습니다.
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|