From a136867f524a6040bb72a76a8fad4595a7994acf Mon Sep 17 00:00:00 2001 From: hjjeong Date: Wed, 13 May 2026 17:17:24 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B3=B5=EC=9A=A9=20=E2=80=94=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=C2=B7=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=9D=84=20PageHeader=20=EC=9A=B0=EC=B8=A1=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=98=81=EC=97=AD=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 보고: "초기화, 검색 버튼은 상단의 메뉴이름 쪽에 다른 버튼들이랑 같이 있으면 될거같아" CompactFilterBar 안에 있던 [초기화][검색] 버튼이 자리 차지 + 시선 분산. PageHeader 의 actions 슬롯 옆으로 통합하면서 11개 페이지 일괄 적용. PageHeader 확장: - onSearch / onReset / loading / searchLabel / resetLabel prop 추가 - actions 뒤에 [초기화][검색] 버튼 자동 렌더 (h-8 / text-xs) CompactFilterBar 단순화: - onSearch / onReset / loading / searchLabel / resetLabel prop 제거 - children + totalText 만 유지 (필드 컨테이너 + 합계 텍스트) 11개 페이지: 3 prop 을 로 이동 메모리: feedback_compact_search_pattern.md 에 "검색·초기화 위치 = PageHeader" 박제 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../development/change-list/page.tsx | 9 ++-- .../development/ebom-regist/page.tsx | 13 +++--- .../development/ebom-search/page.tsx | 6 +-- .../development/part-regist/page.tsx | 13 +++--- .../development/part-search/page.tsx | 13 +++--- .../COMPANY_16/production/mbom/page.tsx | 5 ++- .../COMPANY_16/project/progress/page.tsx | 9 ++-- .../COMPANY_16/project/wbs-template/page.tsx | 13 +++--- .../(main)/COMPANY_16/sales/estimate/page.tsx | 9 ++-- .../(main)/COMPANY_16/sales/order/page.tsx | 13 +++--- .../(main)/COMPANY_16/sales/revenue/page.tsx | 13 +++--- .../app/(main)/COMPANY_16/sales/sale/page.tsx | 13 +++--- .../components/common/CompactFilterBar.tsx | 36 ++-------------- frontend/components/common/PageHeader.tsx | 41 +++++++++++++++++-- 14 files changed, 102 insertions(+), 104 deletions(-) diff --git a/frontend/app/(main)/COMPANY_16/development/change-list/page.tsx b/frontend/app/(main)/COMPANY_16/development/change-list/page.tsx index 29699a58..18db5ddb 100644 --- a/frontend/app/(main)/COMPANY_16/development/change-list/page.tsx +++ b/frontend/app/(main)/COMPANY_16/development/change-list/page.tsx @@ -92,14 +92,13 @@ export default function EoHistoryPage() { return (
- - - fetchList()} onReset={() => { setFilter(EMPTY_FILTER); fetchList(EMPTY_FILTER); }} - totalText={<>총 {total.toLocaleString()}건 (read-only)} - > + /> + + 총 {total.toLocaleString()}건 (read-only)}> - fetchList()} + onReset={() => { setFilter(EMPTY_FILTER); fetchList(EMPTY_FILTER); }} + actions={ <> } /> - 총 {rows.length.toLocaleString()}건 (라인 단위)} - > + 총 {rows.length.toLocaleString()}건 (라인 단위)}> void; - onReset?: () => void; /** 우측에 표시할 합계/통계 텍스트 (예: "총 12,345건 · 합계 12,000,000원") */ totalText?: React.ReactNode; - loading?: boolean; - searchLabel?: string; - resetLabel?: string; className?: string; } -export function CompactFilterBar({ - children, - onSearch, - onReset, - totalText, - loading, - searchLabel = "검색", - resetLabel = "초기화", - className, -}: CompactFilterBarProps) { +export function CompactFilterBar({ children, totalText, className }: CompactFilterBarProps) { + // 검색/초기화 버튼은 PageHeader 의 우측 액션 영역으로 통합. + // CompactFilterBar 는 필드 컨테이너 + 합계 텍스트만 담당. return (
{children} - {(onReset || onSearch) && ( -
- {onReset && ( - - )} - {onSearch && ( - - )} -
- )} {totalText != null && ( {totalText} )} diff --git a/frontend/components/common/PageHeader.tsx b/frontend/components/common/PageHeader.tsx index 0d78e813..dea24e55 100644 --- a/frontend/components/common/PageHeader.tsx +++ b/frontend/components/common/PageHeader.tsx @@ -23,12 +23,23 @@ 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; description?: string; + /** 업무 액션 슬롯 (등록/삭제/상신 등). 검색·초기화는 onSearch/onReset 로 전달. */ actions?: React.ReactNode; + /** 검색 핸들러. 지정 시 우측에 검색 버튼 자동 렌더. */ + onSearch?: () => void; + /** 초기화 핸들러. 지정 시 우측에 초기화 버튼 자동 렌더. */ + onReset?: () => void; + /** 검색 중 로딩 표시 */ + loading?: boolean; + searchLabel?: string; + resetLabel?: string; className?: string; } @@ -56,7 +67,10 @@ function findByUrl(menus: MenuItem[], strippedUrl: string): MenuItem | null { return best; } -export function PageHeader({ title, description, actions, className }: PageHeaderProps) { +export function PageHeader({ + title, description, actions, onSearch, onReset, loading, + searchLabel = "검색", resetLabel = "초기화", className, +}: PageHeaderProps) { const pathname = usePathname() ?? ""; const tabs = useTabStore(selectTabs); const activeTabId = useTabStore(selectActiveTabId); @@ -81,7 +95,8 @@ export function PageHeader({ title, description, actions, className }: PageHeade const resolvedTitle = title ?? menu?.menu_name_kor ?? ""; const resolvedDesc = description ?? menu?.menu_desc ?? ""; - if (!resolvedTitle && !resolvedDesc && !actions) return null; + const hasSearchButtons = !!(onSearch || onReset); + if (!resolvedTitle && !resolvedDesc && !actions && !hasSearchButtons) return null; return (
@@ -93,7 +108,27 @@ export function PageHeader({ title, description, actions, className }: PageHeade

{resolvedDesc}

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