Files
tk-factory-services/system1-factory/web/js/work-management.js
Hyungi Ahn 6495b8af32 feat: SSO 쿠키 인증 통합 + 서브도메인 라우팅 아키텍처
- Path-based 라우팅을 서브도메인 기반으로 전환
  (tkfb/tkreport/tkqc.technicalkorea.net)
- 3개 시스템 프론트엔드에 SSO 쿠키 인증 통합
  (domain=.technicalkorea.net, localStorage 폴백)
- Gateway: 포털+로그인+System1 프록시, 쿠키 SSO 설정
- System 1: 토큰키 통일, nginx.conf 생성, 신고페이지 리다이렉트
- System 2: api-base.js/app-init.js 생성, getSSOToken() 통합
- System 3: TokenManager 쿠키 지원, 중앙 로그인 리다이렉트
- docker-compose.yml에 cloudflared 서비스 추가
- DEPLOY-GUIDE.md 배포 가이드 작성

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 18:41:44 +09:00

231 lines
6.1 KiB
JavaScript

// 작업 관리 페이지 JavaScript
// 전역 변수
let statsData = {
projects: 0,
workers: 0,
tasks: 0,
codeTypes: 0
};
// 페이지 초기화
document.addEventListener('DOMContentLoaded', function() {
console.log('🔧 작업 관리 페이지 초기화 시작');
initializePage();
loadStatistics();
});
// 페이지 초기화
function initializePage() {
// 시간 업데이트 시작
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 사용자 정보 업데이트
updateUserInfo();
// 프로필 메뉴 토글
setupProfileMenu();
// 로그아웃 버튼
setupLogoutButton();
}
// 현재 시간 업데이트 (시 분 초 형식으로 고정)
function updateCurrentTime() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const timeString = `${hours}${minutes}${seconds}`;
const timeElement = document.getElementById('timeValue');
if (timeElement) {
timeElement.textContent = timeString;
}
}
// navbar/sidebar는 app-init.js에서 공통 처리
function updateUserInfo() {
// app-init.js가 navbar 사용자 정보를 처리
}
// 프로필 메뉴 설정
function setupProfileMenu() {
const userProfile = document.getElementById('userProfile');
const profileMenu = document.getElementById('profileMenu');
if (userProfile && profileMenu) {
userProfile.addEventListener('click', function(e) {
e.stopPropagation();
const isVisible = profileMenu.style.display === 'block';
profileMenu.style.display = isVisible ? 'none' : 'block';
});
// 외부 클릭 시 메뉴 닫기
document.addEventListener('click', function() {
profileMenu.style.display = 'none';
});
}
}
// 로그아웃 버튼 설정
function setupLogoutButton() {
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
if (confirm('로그아웃 하시겠습니까?')) {
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
localStorage.removeItem('userInfo');
window.location.href = '/login';
}
});
}
}
// 통계 데이터 로드
async function loadStatistics() {
try {
console.log('📊 통계 데이터 로딩 시작');
// 프로젝트 수 조회
try {
const projectsResponse = await apiCall('/projects', 'GET');
if (projectsResponse && Array.isArray(projectsResponse)) {
statsData.projects = projectsResponse.length;
updateStatDisplay('projectCount', statsData.projects);
}
} catch (error) {
console.warn('프로젝트 통계 로드 실패:', error);
updateStatDisplay('projectCount', '오류');
}
// 작업자 수 조회
try {
const workersResponse = await apiCall('/workers', 'GET');
if (workersResponse && Array.isArray(workersResponse)) {
const activeWorkers = workersResponse.filter(w => w.status === 'active');
statsData.workers = activeWorkers.length;
updateStatDisplay('workerCount', statsData.workers);
}
} catch (error) {
console.warn('작업자 통계 로드 실패:', error);
updateStatDisplay('workerCount', '오류');
}
// 작업 유형 수 조회
try {
const tasksResponse = await apiCall('/tasks', 'GET');
if (tasksResponse && Array.isArray(tasksResponse)) {
const activeTasks = tasksResponse.filter(t => t.is_active);
statsData.tasks = activeTasks.length;
updateStatDisplay('taskCount', statsData.tasks);
}
} catch (error) {
console.warn('작업 유형 통계 로드 실패:', error);
updateStatDisplay('taskCount', '오류');
}
// 코드 타입 수 조회 (임시로 고정값)
statsData.codeTypes = 3; // ISSUE_TYPE, ERROR_TYPE, WORK_STATUS
updateStatDisplay('codeTypeCount', statsData.codeTypes);
console.log('✅ 통계 데이터 로딩 완료:', statsData);
} catch (error) {
console.error('통계 데이터 로딩 오류:', error);
}
}
// 통계 표시 업데이트
function updateStatDisplay(elementId, value) {
const element = document.getElementById(elementId);
if (element) {
element.textContent = value;
// 애니메이션 효과
element.style.transform = 'scale(1.1)';
setTimeout(() => {
element.style.transform = 'scale(1)';
}, 200);
}
}
// 최근 활동 관련 함수들 제거됨
// 페이지 네비게이션
function navigateToPage(url) {
console.log(`🔗 페이지 이동: ${url}`);
// 로딩 효과
const card = event.currentTarget;
const originalContent = card.innerHTML;
card.style.opacity = '0.7';
card.style.pointerEvents = 'none';
// 잠시 후 페이지 이동
setTimeout(() => {
window.location.href = url;
}, 300);
}
// 토스트 메시지 표시
function showToast(message, type = 'info') {
// 기존 토스트 제거
const existingToast = document.querySelector('.toast');
if (existingToast) {
existingToast.remove();
}
// 새 토스트 생성
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
// 스타일 적용
Object.assign(toast.style, {
position: 'fixed',
top: '20px',
right: '20px',
padding: '12px 24px',
borderRadius: '8px',
color: 'white',
fontWeight: '500',
zIndex: '1000',
transform: 'translateX(100%)',
transition: 'transform 0.3s ease'
});
// 타입별 배경색
const colors = {
success: '#10b981',
error: '#ef4444',
warning: '#f59e0b',
info: '#3b82f6'
};
toast.style.backgroundColor = colors[type] || colors.info;
document.body.appendChild(toast);
// 애니메이션
setTimeout(() => {
toast.style.transform = 'translateX(0)';
}, 100);
// 자동 제거
setTimeout(() => {
toast.style.transform = 'translateX(100%)';
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
}, 300);
}, 3000);
}
// 전역 함수로 노출
window.navigateToPage = navigateToPage;