Files
Groupware/Project/Web/wwwroot/react/CommonNavigation.jsx
ChiKyun Kim 6bd4f84192 feat(service): Console_SendMail을 Windows 서비스로 변환
- MailService.cs 추가: ServiceBase 상속받는 Windows 서비스 클래스
- Program.cs 수정: 서비스/콘솔 모드 지원, 설치/제거 기능 추가
- 프로젝트 설정: System.ServiceProcess 참조 추가
- 배치 파일 추가: 서비스 설치/제거/콘솔실행 스크립트

주요 기능:
- Windows 서비스로 백그라운드 실행
- 명령행 인수로 모드 선택 (-install, -uninstall, -console)
- EventLog를 통한 서비스 로깅
- 안전한 서비스 시작/중지 처리

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 09:08:40 +09:00

166 lines
7.8 KiB
JavaScript

// CommonNavigation.jsx - React Navigation Component for GroupWare
const CommonNavigation = ({ currentPage = 'dashboard' }) => {
const [menuItems, setMenuItems] = useState([]);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [currentUser, setCurrentUser] = useState('사용자');
// 기본 메뉴 아이템 - React 경로 사용
const defaultMenuItems = [
{
key: 'dashboard',
title: '대시보드',
url: '/react/dashboard',
icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z',
isVisible: true,
sortOrder: 1
},
{
key: 'common',
title: '공용코드',
url: '/react/common',
icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z',
isVisible: true,
sortOrder: 2
},
{
key: 'jobreport',
title: '업무일지',
url: '/react/jobreport',
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2',
isVisible: true,
sortOrder: 3
},
{
key: 'kuntae',
title: '근태관리',
url: '/react/kuntae',
icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
isVisible: true,
sortOrder: 4
},
{
key: 'todo',
title: '할일관리',
url: '/react/todo',
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4',
isVisible: true,
sortOrder: 5
},
{
key: 'project',
title: '프로젝트',
url: '/react/project',
icon: 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10',
isVisible: true,
sortOrder: 6
},
{
key: 'purchase',
title: '구매관리',
url: '/Purchase/',
icon: 'M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z',
isVisible: true,
sortOrder: 7
},
{
key: 'customer',
title: '고객관리',
url: '/Customer/',
icon: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z',
isVisible: true,
sortOrder: 8
}
];
// 메뉴 아이템 로드 - defaultMenuItems 사용 (React 경로)
useEffect(() => {
setMenuItems(defaultMenuItems);
}, []);
// 보이는 메뉴 아이템만 정렬해서 반환
const visibleItems = menuItems
.filter(item => item.isVisible)
.sort((a, b) => a.sortOrder - b.sortOrder);
return (
<nav className="glass-effect border-b border-white/10 relative z-40">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
{/* 로고 및 브랜드 */}
<div className="flex items-center space-x-8">
<div className="flex items-center space-x-2">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z"></path>
</svg>
<span className="text-xl font-bold text-white">GroupWare</span>
</div>
{/* 데스크톱 메뉴 */}
<nav className="hidden md:flex space-x-1">
{visibleItems.map(item => (
<a
key={item.key}
href={item.url}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
currentPage === item.key
? 'bg-white/20 text-white'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}
>
<svg className="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={item.icon}></path>
</svg>
{item.title}
</a>
))}
</nav>
</div>
{/* 우측 메뉴 */}
<div className="flex items-center space-x-4">
<div className="text-sm text-white/60">
<span>{currentUser}</span>
</div>
{/* 모바일 메뉴 버튼 */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden text-white/60 hover:text-white focus:outline-none focus:text-white"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{mobileMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path>
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16"></path>
)}
</svg>
</button>
</div>
</div>
{/* 모바일 메뉴 */}
{mobileMenuOpen && (
<div className="md:hidden border-t border-white/10 py-2">
{visibleItems.map(item => (
<a
key={item.key}
href={item.url}
className={`block px-3 py-2 text-sm font-medium transition-colors ${
currentPage === item.key
? 'bg-white/20 text-white'
: 'text-white/60 hover:text-white hover:bg-white/10'
}`}
onClick={() => setMobileMenuOpen(false)}
>
<svg className="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={item.icon}></path>
</svg>
{item.title}
</a>
))}
</div>
)}
</div>
</nav>
);
};