Files
Groupware/Project/Web/wwwroot/react/react-dashboard.html
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

297 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="version" content="v2.0-20250905-react">
<title>근태현황 대시보드 (React)</title>
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
<script src="/lib/js/tailwind-config.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
}
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
}
}
}
}
}
</script>
<style>
.glass-effect {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* 스크롤바 스타일링 */
.custom-scrollbar::-webkit-scrollbar {
width: 16px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 8px;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* React 컴포넌트 로딩 스타일 */
.react-dashboard-container {
min-height: 100vh;
}
.loading-skeleton {
background: linear-gradient(90deg, rgba(255,255,255,0.1) 25%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.1) 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>
</head>
<body>
<div id="react-dashboard-app" class="react-dashboard-container">
<!-- 로딩 화면 -->
<div class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
<div class="container mx-auto px-4 py-8">
<!-- 로딩 헤더 -->
<div class="text-center mb-8">
<div class="loading-skeleton h-10 w-80 mx-auto rounded-lg mb-4"></div>
<div class="loading-skeleton h-6 w-60 mx-auto rounded-lg mb-2"></div>
<div class="loading-skeleton h-4 w-40 mx-auto rounded-lg"></div>
</div>
<!-- 로딩 통계 카드들 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-8">
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-success-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-warning-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-16 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-primary-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-20 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-danger-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<div class="flex items-center justify-between">
<div>
<div class="loading-skeleton h-4 w-20 rounded mb-2"></div>
<div class="loading-skeleton h-8 w-12 rounded"></div>
</div>
<div class="w-12 h-12 bg-purple-500/20 rounded-full flex items-center justify-center">
<div class="loading-skeleton w-6 h-6 rounded"></div>
</div>
</div>
</div>
</div>
<!-- 로딩 추가 정보 -->
<div class="glass-effect rounded-2xl p-6">
<div class="loading-skeleton h-8 w-32 rounded mb-4"></div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="text-center">
<div class="w-16 h-16 bg-success-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-warning-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
<div class="text-center">
<div class="w-16 h-16 bg-primary-500/20 rounded-full mx-auto mb-2">
<div class="loading-skeleton w-full h-full rounded-full"></div>
</div>
<div class="loading-skeleton h-4 w-16 rounded mx-auto mb-1"></div>
<div class="loading-skeleton h-3 w-24 rounded mx-auto"></div>
</div>
</div>
</div>
<!-- 로딩 텍스트 -->
<div class="text-center mt-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-white mx-auto mb-4"></div>
<p class="text-white/70">React Dashboard 컴포넌트를 로딩 중입니다...</p>
</div>
</div>
</div>
</div>
<!-- React Local -->
<script crossorigin src="/lib/js/react.development.js"></script>
<script crossorigin src="/lib/js/react-dom.development.js"></script>
<script src="/lib/js/babel.min.js"></script>
<!-- 공통 컴포넌트 로드 -->
<script type="text/babel" src="/react/component/CommonNavigation"></script>
<script type="text/babel" src="/react/component/DevWarning"></script>
<!-- Dashboard 컴포넌트 -->
<script type="text/babel" src="/react/component/DashboardApp"></script>
<!-- 앱 초기화 -->
<script type="text/babel">
const { useState, useEffect } = React;
function App() {
console.log('✅ App 컴포넌트 렌더링 시작');
console.log('📊 CommonNavigation 사용 가능:', typeof CommonNavigation);
console.log('📊 DashboardApp 사용 가능:', typeof DashboardApp);
console.log('📊 DevWarning 사용 가능:', typeof DevWarning);
return (
<div>
<CommonNavigation currentPage="dashboard" />
<DashboardApp />
<DevWarning show={false} />
</div>
);
}
// 루트 렌더링
const root = ReactDOM.createRoot(document.getElementById('react-dashboard-app'));
root.render(<App />);
console.log('✅ React Dashboard 앱이 마운트되었습니다.');
</script>
</body>
</html>