Files
TK-FB-Project/web-ui/js/daily-work-report/api.js
Hyungi Ahn 170adcc149 refactor: 코드 관리 페이지 삭제 및 프론트엔드 모듈화
- codes.html, code-management.js 삭제 (tasks.html에서 동일 기능 제공)
- 사이드바에서 코드 관리 링크 제거
- daily-work-report, tbm, workplace-management JS 모듈 분리
- common/security.js 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:42:12 +09:00

387 lines
12 KiB
JavaScript

/**
* 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');
}
};