/** * Daily Work Report - API Client * 작업보고서 관련 모든 API 호출을 관리 */ class DailyWorkReportAPI { constructor() { this.state = window.DailyWorkReportState; console.log('[API] DailyWorkReportAPI 초기화'); } /** * 작업자 로드 (생산팀 소속) */ async loadWorkers() { try { console.log('[API] Workers 로딩 중...'); const data = await window.apiCall('/workers?limit=1000&department_id=1'); const allWorkers = Array.isArray(data) ? data : (data.data || data.workers || []); // 퇴사자만 제외 const filtered = allWorkers.filter(worker => worker.employment_status !== 'resigned'); this.state.workers = filtered; console.log(`[API] Workers 로드 완료: ${filtered.length}명`); return filtered; } catch (error) { console.error('[API] 작업자 로딩 오류:', error); throw error; } } /** * 프로젝트 로드 (활성 프로젝트만) */ async loadProjects() { try { console.log('[API] Projects 로딩 중...'); const data = await window.apiCall('/projects/active/list'); const projects = Array.isArray(data) ? data : (data.data || data.projects || []); this.state.projects = projects; console.log(`[API] Projects 로드 완료: ${projects.length}개`); return projects; } catch (error) { console.error('[API] 프로젝트 로딩 오류:', error); throw error; } } /** * 작업 유형 로드 */ async loadWorkTypes() { try { const data = await window.apiCall('/daily-work-reports/work-types'); if (Array.isArray(data) && data.length > 0) { this.state.workTypes = data; console.log('[API] 작업 유형 로드 완료:', data.length); return data; } throw new Error('API 실패'); } catch (error) { console.log('[API] 작업 유형 API 사용 불가, 기본값 사용'); this.state.workTypes = [ { id: 1, name: 'Base' }, { id: 2, name: 'Vessel' }, { id: 3, name: 'Piping' } ]; return this.state.workTypes; } } /** * 업무 상태 유형 로드 */ async loadWorkStatusTypes() { try { const data = await window.apiCall('/daily-work-reports/work-status-types'); if (Array.isArray(data) && data.length > 0) { this.state.workStatusTypes = data; console.log('[API] 업무 상태 유형 로드 완료:', data.length); return data; } throw new Error('API 실패'); } catch (error) { console.log('[API] 업무 상태 유형 API 사용 불가, 기본값 사용'); this.state.workStatusTypes = [ { id: 1, name: '정상', is_error: false }, { id: 2, name: '부적합', is_error: true } ]; return this.state.workStatusTypes; } } /** * 오류 유형 로드 (신고 카테고리/아이템) */ async loadErrorTypes() { try { // 1. 신고 카테고리 (nonconformity만) const categoriesResponse = await window.apiCall('/work-issues/categories'); if (categoriesResponse.success && categoriesResponse.data) { this.state.issueCategories = categoriesResponse.data.filter( c => c.category_type === 'nonconformity' ); console.log('[API] 신고 카테고리 로드:', this.state.issueCategories.length); } // 2. 신고 아이템 전체 const itemsResponse = await window.apiCall('/work-issues/items'); if (itemsResponse.success && itemsResponse.data) { // nonconformity 카테고리의 아이템만 필터링 const nonconfCatIds = this.state.issueCategories.map(c => c.category_id); this.state.issueItems = itemsResponse.data.filter( item => nonconfCatIds.includes(item.category_id) ); console.log('[API] 신고 아이템 로드:', this.state.issueItems.length); } // 레거시 호환: errorTypes에 카테고리 매핑 this.state.errorTypes = this.state.issueCategories.map(cat => ({ id: cat.category_id, name: cat.category_name })); } catch (error) { console.error('[API] 오류 유형 로딩 오류:', error); // 기본값 설정 this.state.errorTypes = [ { id: 1, name: '자재 부적합' }, { id: 2, name: '도면 오류' }, { id: 3, name: '장비 고장' } ]; } } /** * 미완료 TBM 세션 로드 */ async loadIncompleteTbms() { try { const response = await window.apiCall('/tbm/sessions/incomplete-reports'); if (!response.success) { throw new Error(response.message || '미완료 TBM 조회 실패'); } let data = response.data || []; // 사용자 권한 확인 및 필터링 const user = this.state.getUser(); if (user && user.role !== 'Admin' && user.access_level !== 'system') { const userId = user.user_id; data = data.filter(tbm => tbm.created_by === userId); } this.state.incompleteTbms = data; console.log('[API] 미완료 TBM 로드 완료:', data.length); return data; } catch (error) { console.error('[API] 미완료 TBM 로드 오류:', error); throw error; } } /** * TBM 세션별 당일 신고 로드 */ async loadDailyIssuesForTbms() { const tbms = this.state.incompleteTbms; if (!tbms || tbms.length === 0) { console.log('[API] 미완료 TBM 없음, 신고 조회 건너뜀'); return; } // 고유한 날짜 수집 const uniqueDates = [...new Set(tbms.map(tbm => { return window.DailyWorkReportUtils?.formatDateForApi(tbm.session_date) || this.formatDateForApi(tbm.session_date); }).filter(Boolean))]; console.log('[API] 조회할 날짜들:', uniqueDates); for (const dateStr of uniqueDates) { if (this.state.dailyIssuesCache[dateStr]) { console.log(`[API] 캐시 사용 (${dateStr})`); continue; } try { const response = await window.apiCall(`/work-issues?start_date=${dateStr}&end_date=${dateStr}`); if (response.success) { this.state.setDailyIssuesCache(dateStr, response.data || []); console.log(`[API] 신고 로드 완료 (${dateStr}):`, this.state.dailyIssuesCache[dateStr].length); } else { this.state.setDailyIssuesCache(dateStr, []); } } catch (error) { console.error(`[API] 신고 조회 오류 (${dateStr}):`, error); this.state.setDailyIssuesCache(dateStr, []); } } } /** * 완료된 작업보고서 조회 */ async loadCompletedReports(date) { try { const response = await window.apiCall(`/daily-work-reports/v2/reports?date=${date}`); if (response.success) { console.log(`[API] 완료 보고서 로드 (${date}):`, response.data?.length || 0); return response.data || []; } throw new Error(response.message || '조회 실패'); } catch (error) { console.error('[API] 완료 보고서 로드 오류:', error); throw error; } } /** * TBM 작업보고서 제출 */ async submitTbmWorkReport(reportData) { try { const response = await window.apiCall('/daily-work-reports/from-tbm', 'POST', reportData); if (!response.success) { throw new Error(response.message || '제출 실패'); } console.log('[API] TBM 작업보고서 제출 완료:', response); return response; } catch (error) { console.error('[API] TBM 작업보고서 제출 오류:', error); throw error; } } /** * 수동 작업보고서 제출 */ async submitManualWorkReport(reportData) { try { const response = await window.apiCall('/daily-work-reports/v2/reports', 'POST', reportData); if (!response.success) { throw new Error(response.message || '제출 실패'); } console.log('[API] 수동 작업보고서 제출 완료:', response); return response; } catch (error) { console.error('[API] 수동 작업보고서 제출 오류:', error); throw error; } } /** * 작업보고서 삭제 */ async deleteWorkReport(reportId) { try { const response = await window.apiCall(`/daily-work-reports/v2/reports/${reportId}`, 'DELETE'); if (!response.success) { throw new Error(response.message || '삭제 실패'); } console.log('[API] 작업보고서 삭제 완료:', reportId); return response; } catch (error) { console.error('[API] 작업보고서 삭제 오류:', error); throw error; } } /** * 작업보고서 수정 */ async updateWorkReport(reportId, updateData) { try { const response = await window.apiCall(`/daily-work-reports/v2/reports/${reportId}`, 'PUT', updateData); if (!response.success) { throw new Error(response.message || '수정 실패'); } console.log('[API] 작업보고서 수정 완료:', reportId); return response; } catch (error) { console.error('[API] 작업보고서 수정 오류:', error); throw error; } } /** * 신고 카테고리 추가 */ async addIssueCategory(categoryData) { try { const response = await window.apiCall('/work-issues/categories', 'POST', categoryData); if (response.success) { await this.loadErrorTypes(); // 목록 새로고침 } return response; } catch (error) { console.error('[API] 카테고리 추가 오류:', error); throw error; } } /** * 신고 아이템 추가 */ async addIssueItem(itemData) { try { const response = await window.apiCall('/work-issues/items', 'POST', itemData); if (response.success) { await this.loadErrorTypes(); // 목록 새로고침 } return response; } catch (error) { console.error('[API] 아이템 추가 오류:', error); throw error; } } /** * 모든 기본 데이터 로드 */ async loadAllData() { console.log('[API] 모든 기본 데이터 로딩 시작...'); await Promise.all([ this.loadWorkers(), this.loadProjects(), this.loadWorkTypes(), this.loadWorkStatusTypes(), this.loadErrorTypes() ]); console.log('[API] 모든 기본 데이터 로딩 완료'); } // 유틸리티: 날짜 형식 변환 (API 형식) formatDateForApi(date) { if (!date) return null; let dateObj; if (date instanceof Date) { dateObj = date; } else if (typeof date === 'string') { dateObj = new Date(date); } else { return null; } const year = dateObj.getFullYear(); const month = String(dateObj.getMonth() + 1).padStart(2, '0'); const day = String(dateObj.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } } // 전역 인스턴스 생성 window.DailyWorkReportAPI = new DailyWorkReportAPI(); // 하위 호환성: 기존 함수들 window.loadWorkers = () => window.DailyWorkReportAPI.loadWorkers(); window.loadProjects = () => window.DailyWorkReportAPI.loadProjects(); window.loadWorkTypes = () => window.DailyWorkReportAPI.loadWorkTypes(); window.loadWorkStatusTypes = () => window.DailyWorkReportAPI.loadWorkStatusTypes(); window.loadErrorTypes = () => window.DailyWorkReportAPI.loadErrorTypes(); window.loadIncompleteTbms = () => window.DailyWorkReportAPI.loadIncompleteTbms(); window.loadDailyIssuesForTbms = () => window.DailyWorkReportAPI.loadDailyIssuesForTbms(); window.loadCompletedReports = () => window.DailyWorkReportAPI.loadCompletedReports( document.getElementById('completedReportDate')?.value ); // 통합 데이터 로드 함수 window.loadData = async () => { try { window.showMessage?.('데이터를 불러오는 중...', 'loading'); await window.DailyWorkReportAPI.loadAllData(); window.hideMessage?.(); } catch (error) { console.error('[API] 데이터 로드 실패:', error); window.showMessage?.('데이터 로드 중 오류가 발생했습니다: ' + error.message, 'error'); } };