- 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>
387 lines
12 KiB
JavaScript
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');
|
|
}
|
|
};
|