feat(WEB): 리스트 페이지 UI 레이아웃 표준화
- 공통 레이아웃 패턴 적용: [달력] → [프리셋] → [검색창] → [버튼들] - beforeTableContent → headerActions + createButton 마이그레이션 - DateRangeSelector extraActions prop 활용하여 검색창 통합 - PricingListClient 테이블 행 클릭 → 상세 이동 기능 추가 - 회계 관련 페이지 (입금/출금/매입/매출/어음/카드/예상지출 등) 정리 - 건설 관련 페이지 검색 영역 정리 - 부모 메뉴 리다이렉트 컴포넌트 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
80
src/components/common/ParentMenuRedirect.tsx
Normal file
80
src/components/common/ParentMenuRedirect.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
|
||||
interface ParentMenuRedirectProps {
|
||||
/** 현재 부모 메뉴 경로 (예: '/accounting') */
|
||||
parentPath: string;
|
||||
/** 메뉴 데이터를 찾지 못했을 때 사용할 기본 첫 번째 자식 경로 */
|
||||
fallbackPath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 부모 메뉴 URL 접근 시 첫 번째 자식 메뉴로 동적 리다이렉트
|
||||
*
|
||||
* localStorage에 저장된 메뉴 구조를 읽어서 해당 부모의 첫 번째 자식으로 이동합니다.
|
||||
* 메뉴 구조가 변경되어도 자동으로 대응됩니다.
|
||||
*/
|
||||
export function ParentMenuRedirect({ parentPath, fallbackPath }: ParentMenuRedirectProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
// localStorage에서 user 데이터 읽기
|
||||
const userData = localStorage.getItem('user');
|
||||
if (!userData) {
|
||||
router.replace(fallbackPath);
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(userData);
|
||||
const menuItems = parsed.menu;
|
||||
|
||||
if (!menuItems || !Array.isArray(menuItems)) {
|
||||
router.replace(fallbackPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 현재 부모 메뉴 찾기 (재귀적으로 검색)
|
||||
const findParentMenu = (items: any[], targetPath: string): any | null => {
|
||||
for (const item of items) {
|
||||
// 경로가 일치하는지 확인 (locale prefix 제거 후 비교)
|
||||
const itemPath = item.path?.replace(/^\/[a-z]{2}\//, '/') || '';
|
||||
if (itemPath === targetPath || item.path === targetPath) {
|
||||
return item;
|
||||
}
|
||||
// 자식 메뉴에서 검색
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found = findParentMenu(item.children, targetPath);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parentMenu = findParentMenu(menuItems, parentPath);
|
||||
|
||||
if (parentMenu && parentMenu.children && parentMenu.children.length > 0) {
|
||||
// 첫 번째 자식 메뉴의 경로로 리다이렉트
|
||||
const firstChild = parentMenu.children[0];
|
||||
const firstChildPath = firstChild.path?.replace(/^\/[a-z]{2}\//, '/') || fallbackPath;
|
||||
router.replace(firstChildPath);
|
||||
} else {
|
||||
// 자식이 없으면 fallback으로 이동
|
||||
router.replace(fallbackPath);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ParentMenuRedirect] Error:', error);
|
||||
router.replace(fallbackPath);
|
||||
}
|
||||
}, [router, parentPath, fallbackPath]);
|
||||
|
||||
// 리다이렉트 중 로딩 표시
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[200px]">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user