import { useState, useRef, useEffect } from "react"; import { Link, useLocation } from "react-router-dom"; import CategoryBadge from "@/components/common/CategoryBadge"; /** 경로 → breadcrumb 레이블 매핑 (정적 경로) */ const pathLabels: Record = { "/dashboard": "대시보드", "/services": "서비스 관리", "/services/register": "서비스 등록", "/messages": "메시지 관리", "/messages/register": "메시지 등록", "/statistics": "발송 통계", "/statistics/history": "발송 이력", "/devices": "기기 관리", "/tags": "태그 관리", "/settings": "마이 페이지", "/settings/profile": "프로필 수정", "/settings/notifications": "알림", }; /** * 동적 경로 패턴 매칭 규칙 * pattern으로 pathname을 매칭 → crumbs 함수가 추가할 브레드크럼 배열 반환 */ const dynamicPatterns: { pattern: RegExp; crumbs: (match: RegExpMatchArray) => { path: string; label: string }[]; }[] = [ { // /services/:id 또는 /services/:id/edit (register 제외) pattern: /^\/services\/(?!register$)([^/]+)(\/edit)?$/, crumbs: (match) => { const id = match[1]; const isEdit = !!match[2]; const result = [{ path: `/services/${id}`, label: "서비스 상세" }]; if (isEdit) { result.push({ path: `/services/${id}/edit`, label: "서비스 수정" }); } return result; }, }, ]; /** pathname → breadcrumb 배열 생성 */ function buildBreadcrumbs(pathname: string) { const segments = pathname.split("/").filter(Boolean); const crumbs: { path: string; label: string }[] = []; // 1) 정적 경로 매칭 (누적 경로 기반) let currentPath = ""; for (const segment of segments) { currentPath += `/${segment}`; const label = pathLabels[currentPath]; if (label) { crumbs.push({ path: currentPath, label }); } } // 2) 동적 경로 패턴 매칭 for (const { pattern, crumbs: buildDynamic } of dynamicPatterns) { const match = pathname.match(pattern); if (match) { crumbs.push(...buildDynamic(match)); } } return crumbs; } /* ── 알림 더미 데이터 ── */ interface NotificationItem { variant: "success" | "warning" | "error" | "info" | "default"; icon: string; category: string; title: string; description: string; time: string; } const MOCK_NOTIFICATIONS: NotificationItem[] = [ { variant: "success", icon: "check_circle", category: "발송", title: "마케팅 발송", description: "마케팅 캠페인 발송이 완료되었습니다 (1,234건 중 1,230건 성공)", time: "14:30" }, { variant: "warning", icon: "verified", category: "인증서", title: "iOS Production", description: "인증서가 7일 후 만료 예정입니다. 갱신이 필요합니다.", time: "09:15" }, { variant: "error", icon: "error", category: "실패", title: "프로모션 알림", description: "FCM 토큰 만료로 인해 320건 발송에 실패했습니다", time: "1일 전" }, { variant: "info", icon: "cloud", category: "서비스", title: "마케팅 발송", description: "신규 서비스가 정상 등록되었습니다", time: "2일 전" }, { variant: "default", icon: "settings", category: "시스템", title: "SPMS", description: "정기 점검이 완료되어 정상 운영 중입니다", time: "3일 전" }, ]; export default function AppHeader() { const { pathname } = useLocation(); const crumbs = buildBreadcrumbs(pathname); const [notiOpen, setNotiOpen] = useState(false); const notiRef = useRef(null); // 외부 클릭 시 드롭다운 닫기 useEffect(() => { function handleClick(e: MouseEvent) { if (notiRef.current && !notiRef.current.contains(e.target as Node)) { setNotiOpen(false); } } document.addEventListener("click", handleClick); return () => document.removeEventListener("click", handleClick); }, []); // 페이지 이동 시 닫기 useEffect(() => { setNotiOpen(false); }, [pathname]); return (
{/* 브레드크럼 (다단계) */} {/* 우측 아이콘 */}
{/* 알림 버튼 + 드롭다운 */}
{/* 알림 드롭다운 패널 */} {notiOpen && (
{/* 헤더 */}

알림

전체 보기
{/* 알림 목록 */}
{MOCK_NOTIFICATIONS.map((noti, i) => (

{noti.title}

{noti.time}

{noti.description}

))}
)}
{/* 구분선 */}
{/* 프로필 */} account_circle
); }