// TK Project Demo - Main JavaScript // 전역 변수 let currentPage = 'project-management'; let selectedProject = null; let currentUser = { name: '김그룹장', role: '생산팀', avatar: '김' }; // 하드코딩된 데이터 const demoData = { projects: [ { jobNo: 'TK-2024-015', name: 'ABC 공장 배관공사', client: 'ABC 케미칼', contractAmount: '150,000,000', orderDate: '2024-01-15', deliveryDate: '2024-03-30', deliveryMethod: '현장납품', productionType: '자체제작', status: '승인완료' } ], processChart: { design: { progress: 100, status: '완료', dueDate: '2024-02-15', responsible: '박설계' }, procurement: { progress: 85, status: '진행중', dueDate: '2024-02-28', responsible: '김구매' }, production: { progress: 60, status: '진행중', dueDate: '2024-03-20', responsible: '이생산' }, inspection: { progress: 0, status: '대기', dueDate: '2024-03-25', responsible: '최품질' }, delivery: { progress: 0, status: '대기', dueDate: '2024-03-30', responsible: '박PM' } }, schedules: [ { date: '09/20', type: '외주출고', title: '도장 작업 출고', memo: 'A구역 파이프 20본, B구역 밸브 5개\n업체: 대한도장\n연락처: 010-1234-5678', responsible: '김구매', urgency: 'urgent' }, { date: '09/22', type: '검사일정', title: '압력시험', memo: '시험압력: 15bar, 30분간 유지\n검사자: 최품질, 이생산', responsible: '최품질', urgency: 'normal' } ], deliveries: [ { item: '파이프 4인치 x 50EA', date: '입고완료 - 09/10', status: 'completed' }, { item: '밸브 2인치 x 10EA', date: '입고예정 - 09/16', status: 'warning' }, { item: '엘보 4인치 x 20EA', date: '지연 - 09/12 → 09/18', status: 'delayed' } ], followUps: [ { priority: '긴급', title: '밸브 A 납기 지연 대응', description: '주 공급업체 생산 지연으로 대체 업체 검토 필요', responsible: '김구매', registeredDate: '09/10', expectedResolution: '09/17', status: '진행중', level: 'high' }, { priority: '높음', title: '용접 검사 일정 조정', description: '고객사 일정 변경으로 검사일 재조정 필요', responsible: '최품질', registeredDate: '09/12', expectedResolution: '09/20', status: '검토중', level: 'medium' } ], purchaseOrders: [ { poNumber: 'PO-2024-0156', project: 'TK-2024-015', item: '스테인리스 파이프 4인치 SCH40', qty: 50, unit: 'EA', supplier: '대한파이프', orderDate: '2024-09-01', expectedDate: '2024-09-15', buyer: '김구매', status: 'pending' }, { poNumber: 'PO-2024-0157', project: 'TK-2024-015', item: '게이트밸브 2인치 150LB', qty: 10, unit: 'EA', supplier: '코리아밸브', orderDate: '2024-09-05', expectedDate: '2024-09-16', buyer: '김구매', status: 'inspecting' } ], materials: [ { item: '파이프 4인치 SCH40', required: 50, status: 'available', location: 'A-3-상단', availableQty: 45, lastUpdate: '2024-09-14 09:00' }, { item: '엘보 4인치 150LB', required: 20, status: 'ordered', currentStage: '구매 진행 중', expectedDate: '2024-09-18', availableQty: 0 }, { item: '플랜지 4인치 150LB', required: 15, status: 'not-requested', currentStage: '설계팀 구매 요청 대기', availableQty: 0, note: '설계 변경으로 사양 확정 대기' }, { item: '밸브 2인치 150LB', required: 5, status: 'ready', location: 'B-2-중단', availableQty: 5, note: '인수 대기 중' } ], issues: [ { time: '14:30', type: '자재부족', description: '엘보 4인치 10EA 부족으로 작업 중단', urgency: '높음', solution: '구매팀에 긴급 요청, 대체재 검토', responsible: '김그룹장', photos: ['issue_001.jpg'] }, { time: '10:15', type: '품질이슈', description: '용접부 기공 발견, 재작업 필요', urgency: '보통', solution: '해당 부위 그라인딩 후 재용접', responsible: '이용접사', photos: ['quality_001.jpg'] } ] }; // DOM이 로드되면 초기화 document.addEventListener('DOMContentLoaded', function() { initializeApp(); setupEventListeners(); showPage(currentPage); }); // 앱 초기화 function initializeApp() { console.log('TK Project Demo 초기화 중...'); // 사용자 정보 업데이트 updateUserInfo(); // 데이터 로드 loadDemoData(); console.log('TK Project Demo 초기화 완료'); } // 이벤트 리스너 설정 function setupEventListeners() { // 네비게이션 클릭 이벤트 document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', function(e) { e.preventDefault(); const pageId = this.getAttribute('onclick').match(/'([^']+)'/)[1]; showPage(pageId); }); }); // 진행률 슬라이더 이벤트 const progressSlider = document.querySelector('.progress-slider'); if (progressSlider) { progressSlider.addEventListener('input', function() { const value = this.value; const display = document.querySelector('.progress-value'); if (display) { display.textContent = value + '%'; } }); } // 버튼 클릭 이벤트 setupButtonEvents(); // 폼 이벤트 setupFormEvents(); } // 버튼 이벤트 설정 function setupButtonEvents() { // 프로젝트 승인 버튼 const approveBtn = document.querySelector('.btn-primary'); if (approveBtn && approveBtn.textContent.includes('프로젝트 승인')) { approveBtn.addEventListener('click', function() { showNotification('프로젝트가 성공적으로 승인되었습니다!', 'success'); }); } // 검수 시작 버튼 document.querySelectorAll('.btn').forEach(btn => { if (btn.textContent.includes('검수 시작')) { btn.addEventListener('click', function() { showNotification('검수 프로세스를 시작합니다.', 'info'); // 버튼 상태 변경 this.textContent = '🔄 검수 진행중'; this.classList.remove('btn-primary'); this.classList.add('btn-warning'); }); } if (btn.textContent.includes('인수 처리')) { btn.addEventListener('click', function() { showNotification('자재 인수가 완료되었습니다.', 'success'); this.textContent = '✅ 인수완료'; this.classList.remove('btn-primary'); this.classList.add('btn-success'); this.disabled = true; }); } if (btn.textContent.includes('데일리 체크에 기록')) { btn.addEventListener('click', function() { showNotification('데일리 체크에 이슈가 등록되었습니다.', 'warning'); }); } }); // 선반 클릭 이벤트 document.querySelectorAll('.shelf.available').forEach(shelf => { shelf.addEventListener('click', function() { // 기존 선택 해제 document.querySelectorAll('.shelf').forEach(s => s.classList.remove('selected')); // 현재 선택 this.classList.add('selected'); // 선택된 위치 업데이트 const locationDisplay = document.querySelector('.selected-location .location'); if (locationDisplay) { locationDisplay.textContent = this.textContent + '-중단'; } showNotification(`${this.textContent} 선반이 선택되었습니다.`, 'info'); }); }); } // 폼 이벤트 설정 function setupFormEvents() { // BOM 조회 버튼 const bomSearchBtn = document.querySelector('.search-input .btn-primary'); if (bomSearchBtn && bomSearchBtn.textContent.includes('BOM 조회')) { bomSearchBtn.addEventListener('click', function() { const input = document.querySelector('.search-input .form-input'); const jobNo = input.value || 'TK-2024-015'; showNotification(`${jobNo} 프로젝트의 BOM 정보를 조회했습니다.`, 'success'); // 자재 리스트 업데이트 (이미 하드코딩되어 있음) animateElements('.material-item'); }); } } // 페이지 표시 function showPage(pageId) { // 모든 페이지 숨기기 document.querySelectorAll('.page').forEach(page => { page.classList.remove('active'); }); // 모든 네비게이션 아이템 비활성화 document.querySelectorAll('.nav-item').forEach(item => { item.classList.remove('active'); }); // 선택된 페이지 표시 const targetPage = document.getElementById(pageId); if (targetPage) { targetPage.classList.add('active'); targetPage.classList.add('fade-in'); // 네비게이션 아이템 활성화 const navItem = document.querySelector(`[onclick*="${pageId}"]`); if (navItem) { navItem.classList.add('active'); } currentPage = pageId; // 페이지별 초기화 initializePage(pageId); } } // 페이지별 초기화 function initializePage(pageId) { switch (pageId) { case 'project-registration': initializeProjectRegistration(); break; case 'production-meeting': initializeProductionMeeting(); break; case 'incoming-inspection': initializeIncomingInspection(); break; case 'production-work': initializeProductionWork(); break; case 'project-management': initializeProjectManagement(); break; } } // 프로젝트 관리 페이지 초기화 function initializeProjectManagement() { console.log('프로젝트 관리 페이지 초기화'); // 프로젝트 카드 애니메이션 animateElements('.project-card'); // 필터 버튼 이벤트 설정 setupProjectFilters(); // 검색 기능 설정 setupProjectSearch(); } // 프로젝트 필터 설정 function setupProjectFilters() { const filterButtons = document.querySelectorAll('.filter-btn'); filterButtons.forEach(btn => { btn.addEventListener('click', function() { // 활성 버튼 변경 filterButtons.forEach(b => b.classList.remove('active')); this.classList.add('active'); // 필터 적용 const filter = this.getAttribute('data-filter'); filterProjects(filter); }); }); } // 프로젝트 검색 설정 function setupProjectSearch() { const searchInput = document.querySelector('.search-input'); const searchBtn = document.querySelector('.search-btn'); if (searchInput && searchBtn) { searchBtn.addEventListener('click', performSearch); searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { performSearch(); } }); } } // 검색 실행 function performSearch() { const searchInput = document.querySelector('.search-input'); const query = searchInput.value.toLowerCase().trim(); if (!query) { showAllProjects(); return; } const projectCards = document.querySelectorAll('.project-card'); let visibleCount = 0; projectCards.forEach(card => { const title = card.querySelector('h3').textContent.toLowerCase(); const jobNo = card.querySelector('.job-no').textContent.toLowerCase(); const customer = card.querySelector('.info-value').textContent.toLowerCase(); if (title.includes(query) || jobNo.includes(query) || customer.includes(query)) { card.style.display = 'block'; visibleCount++; } else { card.style.display = 'none'; } }); showNotification(`${visibleCount}개의 프로젝트를 찾았습니다.`, 'info'); } // 프로젝트 필터링 function filterProjects(filter) { const projectCards = document.querySelectorAll('.project-card'); let visibleCount = 0; projectCards.forEach(card => { if (filter === 'all') { card.style.display = 'block'; visibleCount++; } else { const hasStatus = card.classList.contains(`status-${filter}`); if (hasStatus) { card.style.display = 'block'; visibleCount++; } else { card.style.display = 'none'; } } }); // 통계 업데이트 updateProjectStats(filter, visibleCount); } // 모든 프로젝트 표시 function showAllProjects() { const projectCards = document.querySelectorAll('.project-card'); projectCards.forEach(card => { card.style.display = 'block'; }); } // 프로젝트 통계 업데이트 function updateProjectStats(filter, count) { const filterName = { 'all': '전체', 'planning': '계획', 'in-progress': '진행중', 'production': '제작중', 'completed': '완료' }; showNotification(`${filterName[filter]} 프로젝트: ${count}개`, 'info'); } // 프로젝트 선택 function selectProject(jobNo) { selectedProject = jobNo; // 프로젝트 정보 저장 const projectCard = document.querySelector(`[data-job-no="${jobNo}"]`); if (projectCard) { const projectName = projectCard.querySelector('h3').textContent; const customer = projectCard.querySelector('.info-value').textContent; // 세션 스토리지에 저장 sessionStorage.setItem('selectedProject', JSON.stringify({ jobNo: jobNo, name: projectName, customer: customer })); showNotification(`${projectName} (${jobNo}) 프로젝트가 선택되었습니다.`, 'success'); // 프로젝트 세부 관리 페이지로 이동 (현재는 알림만) setTimeout(() => { showNotification('프로젝트 세부 관리 기능은 추후 구현 예정입니다.', 'info'); }, 1000); } } // 프로젝트 등록 페이지 초기화 function initializeProjectRegistration() { console.log('프로젝트 등록 페이지 초기화'); // 폼 이벤트 리스너 설정 setupProjectForm(); // Job No. 미리보기 업데이트 updateJobNoPreview(); // 요청 리스트 애니메이션 animateElements('.request-item'); } // 프로젝트 폼 설정 function setupProjectForm() { const form = document.querySelector('.project-form'); if (!form) return; // 폼 제출 이벤트 form.addEventListener('submit', function(e) { e.preventDefault(); handleProjectSubmission(); }); // 입력 필드 변경시 Job No. 미리보기 업데이트 const inputs = form.querySelectorAll('input, select'); inputs.forEach(input => { input.addEventListener('input', updateJobNoPreview); }); } // 프로젝트 제출 처리 function handleProjectSubmission() { const form = document.querySelector('.project-form'); const formData = new FormData(form); // 입력값 검증 const projectName = document.getElementById('project-name').value; const endUser = document.getElementById('end-user').value; const deliveryDate = document.getElementById('delivery-date').value; const deliveryMethod = document.getElementById('delivery-method').value; if (!projectName || !endUser || !deliveryDate || !deliveryMethod) { showNotification('필수 항목을 모두 입력해주세요.', 'warning'); return; } // 제출 버튼 상태 변경 const submitBtn = document.getElementById('submit-btn'); const originalText = submitBtn.textContent; submitBtn.textContent = '📤 요청 중...'; submitBtn.disabled = true; // 가상 제출 처리 (2초 후 완료) setTimeout(() => { // 새로운 Job No. 생성 const newJobNo = generateJobNo(); // 성공 메시지 showNotification(`프로젝트 등록 요청이 완료되었습니다! (예상 Job No: ${newJobNo})`, 'success'); // 요청 리스트에 새 항목 추가 addNewRequestToList(projectName, endUser, deliveryDate, deliveryMethod); // 폼 초기화 clearForm(); // 버튼 복원 submitBtn.textContent = originalText; submitBtn.disabled = false; }, 2000); } // Job No. 생성 function generateJobNo() { const year = new Date().getFullYear(); const randomNum = Math.floor(Math.random() * 900) + 100; // 100-999 return `TK-${year}-${randomNum}`; } // Job No. 미리보기 업데이트 function updateJobNoPreview() { const projectName = document.getElementById('project-name')?.value; const previewElement = document.getElementById('preview-job-no'); if (!previewElement) return; if (projectName && projectName.length > 0) { const year = new Date().getFullYear(); const nextNum = String(Math.floor(Math.random() * 900) + 100); previewElement.textContent = `TK-${year}-${nextNum}`; previewElement.style.color = 'var(--dt-primary)'; } else { previewElement.textContent = 'TK-2024-XXX'; previewElement.style.color = 'var(--dt-gray-500)'; } } // 새 요청을 리스트에 추가 function addNewRequestToList(projectName, endUser, deliveryDate, deliveryMethod) { const requestList = document.querySelector('.request-list'); if (!requestList) return; const epcCustomer = document.getElementById('epc-customer')?.value; const customerText = epcCustomer ? `${endUser} / ${epcCustomer}` : endUser; const newItem = document.createElement('div'); newItem.className = 'request-item status-pending'; newItem.innerHTML = `
${projectName}
${customerText} ${deliveryMethod} 납기: ${deliveryDate}
검토중 대기중
`; // 리스트 맨 위에 추가 requestList.insertBefore(newItem, requestList.firstChild); // 애니메이션 newItem.style.opacity = '0'; newItem.style.transform = 'translateY(-20px)'; setTimeout(() => { newItem.style.transition = 'all 0.3s ease'; newItem.style.opacity = '1'; newItem.style.transform = 'translateY(0)'; }, 100); } // 폼 초기화 function clearForm() { const form = document.querySelector('.project-form'); if (form) { form.reset(); updateJobNoPreview(); } } // 생산회의록 페이지 초기화 function initializeProductionMeeting() { console.log('생산회의록 페이지 초기화'); // 공정표 애니메이션 animateElements('.process-item'); // 일정 및 배송 아이템 애니메이션 setTimeout(() => { animateElements('.schedule-item'); animateElements('.delivery-item'); }, 300); // Follow-up 아이템 애니메이션 setTimeout(() => { animateElements('.followup-item'); }, 600); } // 입고 검수 페이지 초기화 function initializeIncomingInspection() { console.log('입고 검수 페이지 초기화'); // 구매 아이템 애니메이션 animateElements('.purchase-item'); // 검수 단계 애니메이션 setTimeout(() => { animateElements('.step'); }, 300); // 창고 구역 애니메이션 setTimeout(() => { animateElements('.zone-item'); }, 600); } // 생산팀 작업 페이지 초기화 function initializeProductionWork() { console.log('생산팀 작업 페이지 초기화'); // 이슈 아이템 애니메이션 animateElements('.issue-item'); // 자재 아이템 애니메이션 setTimeout(() => { animateElements('.material-item'); }, 300); } // 사용자 정보 업데이트 function updateUserInfo() { const userAvatar = document.querySelector('.user-avatar'); const userName = document.querySelector('.user-name'); const userRole = document.querySelector('.user-role'); if (userAvatar) userAvatar.textContent = currentUser.avatar; if (userName) userName.textContent = currentUser.name; if (userRole) userRole.textContent = currentUser.role; } // 데모 데이터 로드 function loadDemoData() { console.log('데모 데이터 로드 중...'); // 여기서 필요한 경우 동적으로 데이터를 DOM에 삽입할 수 있습니다. // 현재는 HTML에 하드코딩되어 있으므로 추가 작업 불필요 console.log('데모 데이터 로드 완료'); } // 요소 애니메이션 function animateElements(selector) { const elements = document.querySelectorAll(selector); elements.forEach((element, index) => { setTimeout(() => { element.classList.add('slide-in'); }, index * 100); }); } // 알림 표시 function showNotification(message, type = 'info') { // 기존 알림 제거 const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // 새 알림 생성 const notification = document.createElement('div'); notification.className = `notification alert alert-${type}`; notification.textContent = message; // 스타일 설정 notification.style.position = 'fixed'; notification.style.top = '20px'; notification.style.right = '20px'; notification.style.zIndex = '9999'; notification.style.minWidth = '300px'; notification.style.maxWidth = '500px'; notification.style.boxShadow = 'var(--dt-shadow-lg)'; notification.style.transform = 'translateX(100%)'; notification.style.transition = 'transform 0.3s ease'; // DOM에 추가 document.body.appendChild(notification); // 애니메이션 setTimeout(() => { notification.style.transform = 'translateX(0)'; }, 100); // 자동 제거 setTimeout(() => { notification.style.transform = 'translateX(100%)'; setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); }, 3000); } // 모달 관련 함수들 function showModal(title, content) { let modal = document.querySelector('.modal-overlay'); if (!modal) { modal = createModal(); document.body.appendChild(modal); } const modalTitle = modal.querySelector('.modal-title'); const modalBody = modal.querySelector('.modal-body'); modalTitle.textContent = title; modalBody.innerHTML = content; modal.classList.add('active'); } function hideModal() { const modal = document.querySelector('.modal-overlay'); if (modal) { modal.classList.remove('active'); } } function createModal() { const modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.innerHTML = ` `; // 오버레이 클릭시 모달 닫기 modal.addEventListener('click', function(e) { if (e.target === modal) { hideModal(); } }); return modal; } // 유틸리티 함수들 function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('ko-KR'); } function formatNumber(number) { return number.toLocaleString('ko-KR'); } function formatCurrency(amount) { return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(amount); } // 검색 기능 function searchItems(query, items, searchFields) { if (!query) return items; const lowercaseQuery = query.toLowerCase(); return items.filter(item => { return searchFields.some(field => { const value = item[field]; return value && value.toString().toLowerCase().includes(lowercaseQuery); }); }); } // 정렬 기능 function sortItems(items, field, direction = 'asc') { return items.sort((a, b) => { const aValue = a[field]; const bValue = b[field]; if (direction === 'asc') { return aValue > bValue ? 1 : -1; } else { return aValue < bValue ? 1 : -1; } }); } // 필터 기능 function filterItems(items, filters) { return items.filter(item => { return Object.entries(filters).every(([key, value]) => { if (!value) return true; return item[key] === value; }); }); } // 로컬 스토리지 관련 function saveToLocalStorage(key, data) { try { localStorage.setItem(key, JSON.stringify(data)); } catch (error) { console.error('로컬 스토리지 저장 실패:', error); } } function loadFromLocalStorage(key, defaultValue = null) { try { const data = localStorage.getItem(key); return data ? JSON.parse(data) : defaultValue; } catch (error) { console.error('로컬 스토리지 로드 실패:', error); return defaultValue; } } // 키보드 단축키 document.addEventListener('keydown', function(e) { // Ctrl + 숫자키로 페이지 전환 if (e.ctrlKey && e.key >= '1' && e.key <= '4') { e.preventDefault(); const pages = ['project-registration', 'production-meeting', 'incoming-inspection', 'production-work']; const pageIndex = parseInt(e.key) - 1; if (pages[pageIndex]) { showPage(pages[pageIndex]); } } // ESC로 모달 닫기 if (e.key === 'Escape') { hideModal(); } }); // 전역 함수로 노출 (HTML onclick에서 사용) window.showPage = showPage; window.showModal = showModal; window.hideModal = hideModal; window.showNotification = showNotification; window.clearForm = clearForm; window.selectProject = selectProject; console.log('TK Project Demo JavaScript 로드 완료');