TK-FB(공장관리+신고)와 M-Project(부적합관리)를 3개 독립 시스템으로 분리하기 위한 전체 코드 구조 작성. - SSO 인증 서비스 (bcrypt + pbkdf2 이중 해시 지원) - System 1: 공장관리 (TK-FB 기반, 신고 코드 제거) - System 2: 신고 (TK-FB에서 workIssue 코드 추출) - System 3: 부적합관리 (M-Project 기반) - Gateway 포털 (path-based 라우팅) - 통합 docker-compose.yml 및 배포 스크립트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
319 lines
9.8 KiB
JavaScript
319 lines
9.8 KiB
JavaScript
/**
|
|
* Daily Work Report - Module Loader
|
|
* 작업보고서 모듈을 초기화하고 연결하는 메인 진입점
|
|
*
|
|
* 로드 순서:
|
|
* 1. state.js - 전역 상태 관리
|
|
* 2. utils.js - 유틸리티 함수
|
|
* 3. api.js - API 클라이언트
|
|
* 4. index.js - 이 파일 (메인 컨트롤러)
|
|
*/
|
|
|
|
class DailyWorkReportController {
|
|
constructor() {
|
|
this.state = window.DailyWorkReportState;
|
|
this.api = window.DailyWorkReportAPI;
|
|
this.utils = window.DailyWorkReportUtils;
|
|
this.initialized = false;
|
|
|
|
console.log('[Controller] DailyWorkReportController 생성');
|
|
}
|
|
|
|
/**
|
|
* 초기화
|
|
*/
|
|
async init() {
|
|
if (this.initialized) {
|
|
console.log('[Controller] 이미 초기화됨');
|
|
return;
|
|
}
|
|
|
|
console.log('[Controller] 초기화 시작...');
|
|
|
|
try {
|
|
// 이벤트 리스너 설정
|
|
this.setupEventListeners();
|
|
|
|
// 기본 데이터 로드
|
|
await this.api.loadAllData();
|
|
|
|
// TBM 탭이 기본
|
|
await this.switchTab('tbm');
|
|
|
|
this.initialized = true;
|
|
console.log('[Controller] 초기화 완료');
|
|
|
|
} catch (error) {
|
|
console.error('[Controller] 초기화 실패:', error);
|
|
window.showMessage?.('초기화 중 오류가 발생했습니다: ' + error.message, 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 이벤트 리스너 설정
|
|
*/
|
|
setupEventListeners() {
|
|
// 탭 버튼
|
|
const tbmBtn = document.getElementById('tbmReportTab');
|
|
const completedBtn = document.getElementById('completedReportTab');
|
|
|
|
if (tbmBtn) {
|
|
tbmBtn.addEventListener('click', () => this.switchTab('tbm'));
|
|
}
|
|
if (completedBtn) {
|
|
completedBtn.addEventListener('click', () => this.switchTab('completed'));
|
|
}
|
|
|
|
// 완료 보고서 날짜 변경
|
|
const completedDateInput = document.getElementById('completedReportDate');
|
|
if (completedDateInput) {
|
|
completedDateInput.addEventListener('change', () => this.loadCompletedReports());
|
|
}
|
|
|
|
console.log('[Controller] 이벤트 리스너 설정 완료');
|
|
}
|
|
|
|
/**
|
|
* 탭 전환
|
|
*/
|
|
async switchTab(tab) {
|
|
this.state.setCurrentTab(tab);
|
|
|
|
const tbmBtn = document.getElementById('tbmReportTab');
|
|
const completedBtn = document.getElementById('completedReportTab');
|
|
const tbmSection = document.getElementById('tbmReportSection');
|
|
const completedSection = document.getElementById('completedReportSection');
|
|
|
|
// 모든 탭 버튼 비활성화
|
|
tbmBtn?.classList.remove('active');
|
|
completedBtn?.classList.remove('active');
|
|
|
|
// 모든 섹션 숨기기
|
|
if (tbmSection) tbmSection.style.display = 'none';
|
|
if (completedSection) completedSection.style.display = 'none';
|
|
|
|
// 선택된 탭 활성화
|
|
if (tab === 'tbm') {
|
|
tbmBtn?.classList.add('active');
|
|
if (tbmSection) tbmSection.style.display = 'block';
|
|
await this.loadTbmData();
|
|
} else if (tab === 'completed') {
|
|
completedBtn?.classList.add('active');
|
|
if (completedSection) completedSection.style.display = 'block';
|
|
|
|
// 오늘 날짜로 초기화
|
|
const dateInput = document.getElementById('completedReportDate');
|
|
if (dateInput) {
|
|
dateInput.value = this.utils.getKoreaToday();
|
|
}
|
|
await this.loadCompletedReports();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 데이터 로드
|
|
*/
|
|
async loadTbmData() {
|
|
try {
|
|
await this.api.loadIncompleteTbms();
|
|
await this.api.loadDailyIssuesForTbms();
|
|
|
|
// 렌더링은 기존 함수 사용 (점진적 마이그레이션)
|
|
if (typeof window.renderTbmWorkList === 'function') {
|
|
window.renderTbmWorkList();
|
|
}
|
|
} catch (error) {
|
|
console.error('[Controller] TBM 데이터 로드 오류:', error);
|
|
window.showMessage?.('TBM 데이터를 불러오는 중 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 완료 보고서 로드
|
|
*/
|
|
async loadCompletedReports() {
|
|
try {
|
|
const dateInput = document.getElementById('completedReportDate');
|
|
const date = dateInput?.value || this.utils.getKoreaToday();
|
|
|
|
const reports = await this.api.loadCompletedReports(date);
|
|
|
|
// 렌더링은 기존 함수 사용
|
|
if (typeof window.renderCompletedReports === 'function') {
|
|
window.renderCompletedReports(reports);
|
|
}
|
|
} catch (error) {
|
|
console.error('[Controller] 완료 보고서 로드 오류:', error);
|
|
window.showMessage?.('완료 보고서를 불러오는 중 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 작업보고서 제출
|
|
*/
|
|
async submitTbmWorkReport(index) {
|
|
try {
|
|
const tbm = this.state.incompleteTbms[index];
|
|
if (!tbm) {
|
|
throw new Error('TBM 데이터를 찾을 수 없습니다.');
|
|
}
|
|
|
|
// 유효성 검사
|
|
const totalHoursInput = document.getElementById(`totalHours_${index}`);
|
|
const totalHours = parseFloat(totalHoursInput?.value);
|
|
|
|
if (!totalHours || totalHours <= 0) {
|
|
window.showMessage?.('작업시간을 입력해주세요.', 'warning');
|
|
return;
|
|
}
|
|
|
|
// 부적합 시간 계산
|
|
const defects = this.state.tempDefects[index] || [];
|
|
const errorHours = defects.reduce((sum, d) => sum + (parseFloat(d.defect_hours) || 0), 0);
|
|
const regularHours = totalHours - errorHours;
|
|
|
|
if (regularHours < 0) {
|
|
window.showMessage?.('부적합 시간이 총 작업시간을 초과할 수 없습니다.', 'warning');
|
|
return;
|
|
}
|
|
|
|
// API 데이터 구성
|
|
const user = this.state.getCurrentUser();
|
|
const reportData = {
|
|
tbm_session_id: tbm.session_id,
|
|
tbm_assignment_id: tbm.assignment_id,
|
|
worker_id: tbm.worker_id,
|
|
project_id: tbm.project_id,
|
|
work_type_id: tbm.work_type_id,
|
|
report_date: this.utils.formatDateForApi(tbm.session_date),
|
|
total_hours: totalHours,
|
|
regular_hours: regularHours,
|
|
error_hours: errorHours,
|
|
work_status_id: errorHours > 0 ? 2 : 1,
|
|
created_by: user?.user_id || user?.id,
|
|
defects: defects.map(d => ({
|
|
category_id: d.category_id,
|
|
item_id: d.item_id,
|
|
issue_report_id: d.issue_report_id,
|
|
defect_hours: d.defect_hours,
|
|
note: d.note
|
|
}))
|
|
};
|
|
|
|
const result = await this.api.submitTbmWorkReport(reportData);
|
|
|
|
window.showSaveResultModal?.(
|
|
'success',
|
|
'제출 완료',
|
|
`${tbm.worker_name}의 작업보고서가 제출되었습니다.`
|
|
);
|
|
|
|
// 목록 새로고침
|
|
await this.loadTbmData();
|
|
|
|
} catch (error) {
|
|
console.error('[Controller] 제출 오류:', error);
|
|
window.showSaveResultModal?.(
|
|
'error',
|
|
'제출 실패',
|
|
error.message || '작업보고서 제출 중 오류가 발생했습니다.'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 세션 일괄 제출
|
|
*/
|
|
async batchSubmitSession(sessionKey) {
|
|
const rows = document.querySelectorAll(`tr[data-session-key="${sessionKey}"][data-type="tbm"]`);
|
|
const indices = [];
|
|
|
|
rows.forEach(row => {
|
|
const index = parseInt(row.dataset.index);
|
|
const totalHoursInput = document.getElementById(`totalHours_${index}`);
|
|
if (totalHoursInput?.value && parseFloat(totalHoursInput.value) > 0) {
|
|
indices.push(index);
|
|
}
|
|
});
|
|
|
|
if (indices.length === 0) {
|
|
window.showMessage?.('제출할 항목이 없습니다. 작업시간을 입력해주세요.', 'warning');
|
|
return;
|
|
}
|
|
|
|
const confirmed = confirm(`${indices.length}건의 작업보고서를 일괄 제출하시겠습니까?`);
|
|
if (!confirmed) return;
|
|
|
|
let successCount = 0;
|
|
let failCount = 0;
|
|
|
|
for (const index of indices) {
|
|
try {
|
|
await this.submitTbmWorkReport(index);
|
|
successCount++;
|
|
} catch (error) {
|
|
failCount++;
|
|
console.error(`[Controller] 일괄 제출 오류 (index: ${index}):`, error);
|
|
}
|
|
}
|
|
|
|
if (failCount === 0) {
|
|
window.showSaveResultModal?.('success', '일괄 제출 완료', `${successCount}건이 성공적으로 제출되었습니다.`);
|
|
} else {
|
|
window.showSaveResultModal?.('warning', '일괄 제출 부분 완료', `성공: ${successCount}건, 실패: ${failCount}건`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 상태 디버그
|
|
*/
|
|
debug() {
|
|
console.log('[Controller] 상태 디버그:');
|
|
this.state.debug();
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.DailyWorkReportController = new DailyWorkReportController();
|
|
|
|
// 하위 호환성: 기존 전역 함수들
|
|
window.switchTab = (tab) => window.DailyWorkReportController.switchTab(tab);
|
|
window.submitTbmWorkReport = (index) => window.DailyWorkReportController.submitTbmWorkReport(index);
|
|
window.batchSubmitTbmSession = (sessionKey) => window.DailyWorkReportController.batchSubmitSession(sessionKey);
|
|
|
|
// 사용자 정보 함수
|
|
window.getUser = () => window.DailyWorkReportState.getUser();
|
|
window.getCurrentUser = () => window.DailyWorkReportState.getCurrentUser();
|
|
|
|
// 날짜 그룹 토글 (UI 함수)
|
|
window.toggleDateGroup = function(dateStr) {
|
|
const group = document.querySelector(`.date-group[data-date="${dateStr}"]`);
|
|
if (!group) return;
|
|
|
|
const isExpanded = group.classList.contains('expanded');
|
|
const content = group.querySelector('.date-group-content');
|
|
const icon = group.querySelector('.date-toggle-icon');
|
|
|
|
if (isExpanded) {
|
|
group.classList.remove('expanded');
|
|
group.classList.add('collapsed');
|
|
if (content) content.style.display = 'none';
|
|
if (icon) icon.textContent = '▶';
|
|
} else {
|
|
group.classList.remove('collapsed');
|
|
group.classList.add('expanded');
|
|
if (content) content.style.display = 'block';
|
|
if (icon) icon.textContent = '▼';
|
|
}
|
|
};
|
|
|
|
// DOMContentLoaded 이벤트에서 초기화
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// 약간의 지연 후 초기화 (다른 스크립트 로드 대기)
|
|
setTimeout(() => {
|
|
window.DailyWorkReportController.init();
|
|
}, 100);
|
|
});
|
|
|
|
console.log('[Module] daily-work-report/index.js 로드 완료');
|