// 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('프로젝트 관리 페이지 초기화'); // 드롭다운 외부 클릭 이벤트 설정 setupDropdownOutsideClick(); // 애니메이션 효과 animateElements('.project-selector-header'); animateElements('.project-dropdown-container'); animateElements('.project-stats-simple'); } // 드롭다운 외부 클릭 설정 function setupDropdownOutsideClick() { document.addEventListener('click', function(e) { const dropdown = document.querySelector('.project-dropdown'); const dropdownMenu = document.getElementById('project-dropdown-menu'); const dropdownDisplay = document.querySelector('.dropdown-display'); if (!dropdown.contains(e.target)) { dropdownMenu.classList.remove('active'); dropdownDisplay.classList.remove('active'); } }); } // 프로젝트 드롭다운 토글 function toggleProjectDropdown() { const dropdownMenu = document.getElementById('project-dropdown-menu'); const dropdownDisplay = document.querySelector('.dropdown-display'); dropdownMenu.classList.toggle('active'); dropdownDisplay.classList.toggle('active'); // 검색 입력창에 포커스 if (dropdownMenu.classList.contains('active')) { setTimeout(() => { const searchInput = document.querySelector('.dropdown-search-input'); if (searchInput) { searchInput.focus(); } }, 100); } } // 드롭다운에서 프로젝트 선택 function selectProjectFromDropdown(jobNo) { const projectData = getProjectData(jobNo); if (!projectData) return; // 드롭다운 닫기 const dropdownMenu = document.getElementById('project-dropdown-menu'); const dropdownDisplay = document.querySelector('.dropdown-display'); dropdownMenu.classList.remove('active'); dropdownDisplay.classList.remove('active'); // 선택된 프로젝트 텍스트 업데이트 const selectedText = document.getElementById('selected-project-text'); selectedText.textContent = `${projectData.name} (${jobNo})`; // 프로젝트 정보 카드 표시 showProjectInfo(projectData); // 전역 변수 업데이트 selectedProject = jobNo; // 세션 스토리지에 저장 sessionStorage.setItem('selectedProject', JSON.stringify(projectData)); showNotification(`${projectData.name} 프로젝트가 선택되었습니다.`, 'success'); } // 프로젝트 데이터 가져오기 function getProjectData(jobNo) { const projectsData = { 'TK-2024-015': { jobNo: 'TK-2024-015', name: 'ABC 공장 배관공사', customer: 'ABC 케미칼', deadline: '2024-03-30', delivery: '현장납품', pm: '이PM', status: '제작중', statusClass: 'status-production', progress: '75%' }, 'TK-2024-016': { jobNo: 'TK-2024-016', name: 'DEF 플랜트 배관 설치', customer: 'DEF 화학', deadline: '2024-04-15', delivery: '공장인도', pm: '박PM', status: '계획', statusClass: 'status-planning', progress: '25%' }, 'TK-2024-017': { jobNo: 'TK-2024-017', name: 'GHI 정유 공장 개보수', customer: 'GHI 정유 / 한국엔지니어링', deadline: '2024-05-20', delivery: '부분납품', pm: '최PM', status: '진행중', statusClass: 'status-in-progress', progress: '60%' }, 'TK-2024-012': { jobNo: 'TK-2024-012', name: 'JKL 화학 공장 신설', customer: 'JKL 화학', deadline: '2024-02-28', delivery: '현장납품', pm: '김PM', status: '완료', statusClass: 'status-completed', progress: '100%' } }; return projectsData[jobNo]; } // 프로젝트 정보 표시 function showProjectInfo(projectData) { const infoContainer = document.getElementById('selected-project-info'); // 정보 업데이트 document.getElementById('info-project-title').textContent = projectData.name; document.getElementById('info-job-no').textContent = projectData.jobNo; document.getElementById('info-customer').textContent = projectData.customer; document.getElementById('info-deadline').textContent = projectData.deadline; document.getElementById('info-delivery').textContent = projectData.delivery; document.getElementById('info-pm').textContent = projectData.pm; document.getElementById('info-progress').textContent = projectData.progress; // 상태 배지 업데이트 const statusBadge = document.getElementById('info-status'); statusBadge.textContent = projectData.status; statusBadge.className = `status-badge ${projectData.statusClass}`; // 버튼 상태 업데이트 updateActionButtons(projectData); // 정보 카드 표시 infoContainer.style.display = 'block'; } // 액션 버튼 상태 업데이트 function updateActionButtons(projectData) { const buttons = { meeting: document.getElementById('btn-production-meeting'), inspection: document.getElementById('btn-incoming-inspection'), work: document.getElementById('btn-production-work') }; // 모든 버튼 활성화 Object.values(buttons).forEach(btn => { btn.classList.remove('disabled'); btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }); // 상태에 따른 버튼 비활성화 switch (projectData.statusClass) { case 'status-planning': buttons.meeting.classList.add('disabled'); buttons.meeting.style.opacity = '0.5'; buttons.meeting.style.pointerEvents = 'none'; break; case 'status-completed': buttons.meeting.textContent = '🏭 생산회의록 (완료)'; buttons.inspection.textContent = '📦 입고 검수 (완료)'; buttons.work.textContent = '🔧 생산팀 작업 (완료)'; break; } } // 드롭다운 프로젝트 필터링 function filterProjects(query) { const options = document.querySelectorAll('.dropdown-option'); const searchQuery = query.toLowerCase().trim(); options.forEach(option => { const title = option.querySelector('.option-title').textContent.toLowerCase(); const jobNo = option.querySelector('.option-job-no').textContent.toLowerCase(); const customer = option.querySelector('.option-customer').textContent.toLowerCase(); if (!searchQuery || title.includes(searchQuery) || jobNo.includes(searchQuery) || customer.includes(searchQuery)) { option.style.display = 'block'; } else { option.style.display = 'none'; } }); } // 프로젝트 세부 관리로 이동 function goToProjectDetail() { if (!selectedProject) { showNotification('프로젝트를 먼저 선택해주세요.', 'warning'); return; } showNotification('프로젝트 세부 관리 페이지는 추후 구현 예정입니다.', 'info'); } // 프로젝트 등록 페이지 초기화 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; window.toggleProjectDropdown = toggleProjectDropdown; window.selectProjectFromDropdown = selectProjectFromDropdown; window.filterProjects = filterProjects; window.goToProjectDetail = goToProjectDetail; console.log('TK Project Demo JavaScript 로드 완료');