"use client"; /** * PageHeader — 페이지 상단 "대메뉴_중메뉴" 제목 + 액션/검색 슬롯. * * 모든 RPS 메뉴 페이지의 상단에 의무 배치. * * 자동 매칭 (탭 시스템 대응): * - RPS 는 탭 기반이라 usePathname() 이 /main 으로 고정됨. * - useTabStore 의 활성 탭 adminUrl → /COMPANY_NN prefix 제거 → menu_info.menu_url 매칭. * - 매칭된 menu 의 parent_obj_id 로 부모 메뉴를 찾아 "{부모}_{자식}" 으로 표기 (wace 컨벤션). * - 루트 그룹(parent_obj_id 가 0뎁스)이면 자식만 단독 표기. * * 명시 지정: * * * 원칙: menu_info 에 등록만 되어 있으면 props 없이도 자동 매칭. */ import React from "react"; import { usePathname } from "next/navigation"; import { useMenu } from "@/contexts/MenuContext"; import { useTabStore, selectTabs, selectActiveTabId } from "@/stores/tabStore"; import type { MenuItem } from "@/lib/api/menu"; import { Button } from "@/components/ui/button"; import { Search, Loader2, RotateCcw } from "lucide-react"; import { cn } from "@/lib/utils"; interface PageHeaderProps { title?: string; /** 업무 액션 슬롯 (등록/삭제/상신 등). 검색·초기화는 onSearch/onReset 로 전달. */ actions?: React.ReactNode; /** 검색 핸들러. 지정 시 우측에 검색 버튼 자동 렌더. */ onSearch?: () => void; /** 초기화 핸들러. 지정 시 우측에 초기화 버튼 자동 렌더. */ onReset?: () => void; /** 검색 중 로딩 표시 */ loading?: boolean; searchLabel?: string; resetLabel?: string; className?: string; } function stripCompanyPrefix(p: string): string { return p.replace(/^\/COMPANY_\d+/, "") || "/"; } function findParentMenu(menus: MenuItem[], menu: MenuItem | null): MenuItem | null { if (!menu) return null; const pid = menu.parent_obj_id ?? menu.PARENT_OBJ_ID; if (!pid) return null; for (const m of menus) { const oid = m.objid ?? m.OBJID; if (oid && String(oid) === String(pid)) return m; } return null; } function findByUrl(menus: MenuItem[], strippedUrl: string): MenuItem | null { // menu_info.menu_url 이 /COMPANY_16/... 으로 저장되어 있으므로 양쪽 비교 for (const m of menus) { if (!m.menu_url) continue; if (m.menu_url === strippedUrl) return m; if (stripCompanyPrefix(m.menu_url) === strippedUrl) return m; } let best: MenuItem | null = null; let bestLen = 0; for (const m of menus) { if (!m.menu_url) continue; const stripped = stripCompanyPrefix(m.menu_url); if (strippedUrl.startsWith(stripped) && stripped.length > bestLen) { best = m; bestLen = stripped.length; } } return best; } export function PageHeader({ title, actions, onSearch, onReset, loading, searchLabel = "검색", resetLabel = "초기화", className, }: PageHeaderProps) { const pathname = usePathname() ?? ""; const tabs = useTabStore(selectTabs); const activeTabId = useTabStore(selectActiveTabId); let menu: MenuItem | null = null; let parentMenu: MenuItem | null = null; try { const { userMenus, adminMenus } = useMenu(); // RPS 탭 시스템: pathname=/main 이면 활성 탭의 adminUrl 사용 let targetUrl = stripCompanyPrefix(pathname); const isRootLike = pathname === "/main" || pathname === "/" || pathname === ""; if (isRootLike) { const activeTab = tabs.find((t: any) => t.id === activeTabId); if (activeTab?.adminUrl) { targetUrl = stripCompanyPrefix(activeTab.adminUrl); } } const allMenus = [...(userMenus as MenuItem[]), ...(adminMenus as MenuItem[])]; menu = findByUrl(userMenus as MenuItem[], targetUrl) ?? findByUrl(adminMenus as MenuItem[], targetUrl); parentMenu = findParentMenu(allMenus, menu); } catch { /* Provider 밖 — 자동 매칭 생략 */ } // wace 컨벤션: "대메뉴_중메뉴" (parent_obj_id 가 루트 그룹이면 단독 표기) const parentName = parentMenu?.menu_name_kor ?? parentMenu?.MENU_NAME_KOR ?? ""; const ownName = menu?.menu_name_kor ?? menu?.MENU_NAME_KOR ?? ""; const parentParentPid = parentMenu?.parent_obj_id ?? parentMenu?.PARENT_OBJ_ID; // 부모의 부모가 있어야 (즉, 부모가 1뎁스 그룹) "부모_자식" 표기. 부모 없거나 부모가 루트이면 자식만. const autoTitle = parentName && parentParentPid && ownName ? `${parentName}_${ownName}` : ownName; const resolvedTitle = title ?? autoTitle; const hasSearchButtons = !!(onSearch || onReset); if (!resolvedTitle && !actions && !hasSearchButtons) return null; return (
{resolvedTitle && (

{resolvedTitle}

)}
{(actions || hasSearchButtons) && (
{actions} {hasSearchButtons && ( <> {onReset && ( )} {onSearch && ( )} )}
)}
); }