File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use client';
import Image from 'next/image';
import { useEffect, useRef, useState } from 'react';
import { logout } from '@/app/(protected)/_actions';
import {
FoxBellSimpleIcon,
FoxCaretDownIcon,
FoxMagnifyingGlassIcon,
FoxUserIcon,
} from '@fox/core/icons';
import styles from './admin-header.module.scss';
import { HeaderIconButton } from './header-icon-button';
interface AdminHeaderProps {
/** 프로필에 표시할 이름. 세션의 관리자 이름을 상위에서 받아온다. */
adminName: string;
}
/**
* Basic 레이아웃 헤더 — 시안 header(7028:7614)를 그대로 옮겼다. 좌측 로고, 우측에 아이콘
* 버튼 3개와 프로필 pill이 온다.
*
* 시안과 다른 점 셋(전부 사용자 확정):
* - 로고: 시안은 28px 정사각 심볼이지만 에듀페이 로고가 170×62 워드마크라 높이만 맞췄다.
* - 사이드바 토글 버튼을 뺐다 — 시안에 없다.
* - 프로필 pill을 눌러 로그아웃한다. 시안에 로그아웃 자리가 없는데 다른 경로도 없어서,
* CaretDown이 이미 "열린다"고 알리고 있는 이 자리에 메뉴를 붙였다.
*
* 검색·알림·사용자 버튼은 시안에 있으나 연결할 기능이 아직 없어 `onAction` 없이 둔다.
* 시안의 모양을 유지하려고 비활성으로 만들지는 않았다 — 기능이 정해지면 핸들러만 붙인다.
*/
export function AdminHeader({ adminName }: AdminHeaderProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const profileRef = useRef<HTMLDivElement>(null);
// 바깥을 누르거나 Esc를 누르면 닫는다 — 메뉴가 열린 동안에만 듣는다.
useEffect(() => {
if (!isMenuOpen) {
return;
}
function handlePointerDown(event: MouseEvent) {
if (!profileRef.current?.contains(event.target as Node)) {
setIsMenuOpen(false);
}
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
setIsMenuOpen(false);
}
}
document.addEventListener('mousedown', handlePointerDown);
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('mousedown', handlePointerDown);
document.removeEventListener('keydown', handleKeyDown);
};
}, [isMenuOpen]);
return (
<header className={styles.header}>
<div className={styles.logo}>
<Image
src="/images/logo-foxpay.svg"
alt="에듀페이"
width={170}
height={62}
className={styles.logoMark}
priority
/>
<span className={styles.title}>에듀페이 관리자</span>
</div>
<div className={styles.fnc}>
<div className={styles.btnGroup}>
<HeaderIconButton icon={<FoxMagnifyingGlassIcon />} label="검색" />
<HeaderIconButton icon={<FoxBellSimpleIcon />} label="알림" hasBadge />
<HeaderIconButton icon={<FoxUserIcon />} label="사용자" />
</div>
<div className={styles.profile} ref={profileRef}>
<button
type="button"
className={styles.profileButton}
aria-haspopup="menu"
aria-expanded={isMenuOpen}
onClick={() => setIsMenuOpen((open) => !open)}
>
{/* 시안은 프로필 사진 자리지만 관리자 사진을 받는 경로가 없어 사람 아이콘으로 채운다. */}
<span className={styles.avatar}>
<FoxUserIcon weight="fill" />
</span>
<span className={styles.profileName}>{adminName}</span>
<span className={styles.caret}>
<FoxCaretDownIcon />
</span>
</button>
{isMenuOpen && (
<div className={styles.menu} role="menu">
<form action={logout}>
<button type="submit" role="menuitem" className={styles.menuItem}>
로그아웃
</button>
</form>
</div>
)}
</div>
</div>
</header>
);
}