- 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>
166 lines
7.8 KiB
JavaScript
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>
|
|
);
|
|
}; |