- 지오정보기술로고.png → zioinfo-logo.png - 지오정보기술사옥전경.png → zioinfo-building.png - Header/Footer/Company.jsx 참조 경로 영문 수정 - SFTP 직접 배포로 서버 반영 완료 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
199 lines
7.4 KiB
JavaScript
199 lines
7.4 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
|
import './Header.css';
|
|
|
|
const MENU = [
|
|
{
|
|
id: 'company', label: '회사소개',
|
|
children: [
|
|
{ label: 'CEO 인사말', path: '/company/greeting' },
|
|
{ label: '연혁', path: '/company/history' },
|
|
{ label: '조직도', path: '/company/organization' },
|
|
{ label: 'CI 소개', path: '/company/ci' },
|
|
{ label: '오시는 길', path: '/company/location' },
|
|
]
|
|
},
|
|
{
|
|
id: 'solution', label: '솔루션',
|
|
children: [
|
|
{ label: 'GUARDiA ITSM', path: '/solution/guardia', badge: 'NEW' },
|
|
{ label: 'ERP', path: '/solution/erp' },
|
|
{ label: 'CRM', path: '/solution/crm' },
|
|
{ label: 'BI', path: '/solution/bi' },
|
|
]
|
|
},
|
|
{
|
|
id: 'business', label: '사업실적',
|
|
children: [
|
|
{ label: '구축 레퍼런스', path: '/business/reference' },
|
|
{ label: '파트너', path: '/business/partner' },
|
|
]
|
|
},
|
|
{
|
|
id: 'support', label: '고객지원',
|
|
children: [
|
|
{ label: '공지사항', path: '/support/notice' },
|
|
{ label: 'FAQ', path: '/support/faq' },
|
|
{ label: '카탈로그', path: '/support/catalog' },
|
|
{ label: '문의하기', path: '/support/contact' },
|
|
]
|
|
},
|
|
{
|
|
id: 'recruit', label: '채용',
|
|
children: [
|
|
{ label: '채용공고', path: '/recruit/jobs' },
|
|
{ label: '복리후생', path: '/recruit/welfare' },
|
|
{ label: '지원하기', path: '/recruit/apply' },
|
|
]
|
|
},
|
|
{
|
|
id: 'news', label: '뉴스',
|
|
children: [
|
|
{ label: '뉴스룸', path: '/news/newsroom' },
|
|
{ label: '기술 블로그', path: '/news/blog' },
|
|
]
|
|
},
|
|
];
|
|
|
|
export default function Header() {
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [activeMenu, setActiveMenu] = useState(null);
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const [member, setMember] = useState(null);
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
|
|
// 로그인 상태 동기화
|
|
useEffect(() => {
|
|
const sync = () => {
|
|
const u = localStorage.getItem('member_user');
|
|
setMember(u ? JSON.parse(u) : null);
|
|
};
|
|
sync();
|
|
window.addEventListener('storage', sync);
|
|
return () => window.removeEventListener('storage', sync);
|
|
}, [location]);
|
|
|
|
const logout = () => {
|
|
localStorage.removeItem('member_token');
|
|
localStorage.removeItem('member_user');
|
|
setMember(null);
|
|
navigate('/');
|
|
};
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 60);
|
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', onScroll);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
setMobileOpen(false);
|
|
setActiveMenu(null);
|
|
}, [location]);
|
|
|
|
const isActive = (menu) =>
|
|
menu.children?.some(c => location.pathname.startsWith(c.path));
|
|
|
|
return (
|
|
<>
|
|
{/* 접근성 스킵 링크 */}
|
|
<a href="#main-content" className="skip-link">본문 바로가기</a>
|
|
|
|
<header className={`header ${scrolled ? 'scrolled' : ''} ${mobileOpen ? 'mobile-open' : ''}`}
|
|
role="banner">
|
|
<div className="header-inner container">
|
|
{/* 로고 */}
|
|
<Link to="/" className="logo" aria-label="(주)지오정보기술 홈으로">
|
|
<img src="/zioinfo-logo.png" alt="(주)지오정보기술 로고" height="40"
|
|
onError={e => { e.target.src='/ziologo.png'; e.target.onerror = () => { e.target.style.display='none'; e.target.nextSibling.style.display='flex'; }; }} />
|
|
<span className="logo-text" style={{display:'none'}}>
|
|
<strong>Zio</strong>Info
|
|
</span>
|
|
</Link>
|
|
|
|
{/* 데스크톱 메뉴 */}
|
|
<nav className="nav-desktop" role="navigation" aria-label="주요 메뉴">
|
|
{MENU.map(menu => (
|
|
<div key={menu.id}
|
|
className={`nav-item ${isActive(menu) ? 'active' : ''}`}
|
|
onMouseEnter={() => setActiveMenu(menu.id)}
|
|
onMouseLeave={() => setActiveMenu(null)}>
|
|
<button className="nav-trigger" aria-haspopup="true"
|
|
aria-expanded={activeMenu === menu.id}>
|
|
{menu.label}
|
|
</button>
|
|
{activeMenu === menu.id && (
|
|
<div className="dropdown" role="menu">
|
|
{menu.children.map(child => (
|
|
<Link key={child.path} to={child.path}
|
|
className={`dropdown-item ${location.pathname === child.path ? 'current' : ''}`}
|
|
role="menuitem">
|
|
{child.label}
|
|
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</nav>
|
|
|
|
{/* 우측 버튼 영역 */}
|
|
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
|
<Link to="/support/contact" className="btn btn-outline btn-sm">문의하기</Link>
|
|
{member ? (
|
|
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
|
<span style={{ fontSize:13, color:'var(--gray-600)', maxWidth:100,
|
|
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
|
|
{member.name}님
|
|
</span>
|
|
<button onClick={logout}
|
|
style={{ padding:'6px 14px', background:'none', border:'1px solid #e2e8f0',
|
|
borderRadius:8, fontSize:12, color:'#64748b', cursor:'pointer' }}>
|
|
로그아웃
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<Link to="/login" className="btn btn-primary btn-sm">로그인</Link>
|
|
)}
|
|
</div>
|
|
|
|
{/* 햄버거 (모바일) */}
|
|
<button className="hamburger" aria-label="모바일 메뉴"
|
|
aria-expanded={mobileOpen}
|
|
onClick={() => setMobileOpen(v => !v)}>
|
|
<span/><span/><span/>
|
|
</button>
|
|
</div>
|
|
|
|
{/* 모바일 메뉴 */}
|
|
{mobileOpen && (
|
|
<nav className="nav-mobile" role="navigation" aria-label="모바일 메뉴">
|
|
{MENU.map(menu => (
|
|
<details key={menu.id} className="mobile-group">
|
|
<summary className="mobile-group-header">{menu.label}</summary>
|
|
<div className="mobile-children">
|
|
{menu.children.map(child => (
|
|
<Link key={child.path} to={child.path} className="mobile-child">
|
|
{child.label}
|
|
{child.badge && <span className="badge badge-new">{child.badge}</span>}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</details>
|
|
))}
|
|
<div style={{ display:'flex', gap:8, margin:'16px' }}>
|
|
<Link to="/support/contact" className="btn btn-outline" style={{ flex:1 }}>문의하기</Link>
|
|
{member
|
|
? <button onClick={logout} className="btn btn-primary" style={{ flex:1 }}>로그아웃</button>
|
|
: <Link to="/login" className="btn btn-primary" style={{ flex:1 }}>로그인 / 가입</Link>
|
|
}
|
|
</div>
|
|
</nav>
|
|
)}
|
|
</header>
|
|
</>
|
|
);
|
|
}
|