- 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>
343 lines
8.6 KiB
JavaScript
343 lines
8.6 KiB
JavaScript
/**
|
|
* Daily Work Report - State Manager
|
|
* 작업보고서 페이지의 전역 상태 관리
|
|
*/
|
|
|
|
class DailyWorkReportState {
|
|
constructor() {
|
|
// 마스터 데이터
|
|
this.workTypes = [];
|
|
this.workStatusTypes = [];
|
|
this.errorTypes = []; // 레거시 호환용
|
|
this.issueCategories = []; // 신고 카테고리 (nonconformity)
|
|
this.issueItems = []; // 신고 아이템
|
|
this.workers = [];
|
|
this.projects = [];
|
|
|
|
// UI 상태
|
|
this.selectedWorkers = new Set();
|
|
this.workEntryCounter = 0;
|
|
this.currentStep = 1;
|
|
this.editingWorkId = null;
|
|
this.currentTab = 'tbm';
|
|
|
|
// TBM 관련
|
|
this.incompleteTbms = [];
|
|
|
|
// 부적합 원인 관리
|
|
this.currentDefectIndex = null;
|
|
this.tempDefects = {}; // { index: [{ error_type_id, defect_hours, note }] }
|
|
|
|
// 작업장소 지도 관련
|
|
this.mapCanvas = null;
|
|
this.mapCtx = null;
|
|
this.mapImage = null;
|
|
this.mapRegions = [];
|
|
this.selectedWorkplace = null;
|
|
this.selectedWorkplaceName = null;
|
|
this.selectedWorkplaceCategory = null;
|
|
this.selectedWorkplaceCategoryName = null;
|
|
|
|
// 시간 선택 관련
|
|
this.currentEditingField = null; // { index, type: 'total' | 'error' }
|
|
this.currentTimeValue = 0;
|
|
|
|
// 캐시
|
|
this.dailyIssuesCache = {}; // { 'YYYY-MM-DD': [issues] }
|
|
|
|
// 리스너
|
|
this.listeners = new Map();
|
|
|
|
console.log('[State] DailyWorkReportState 초기화 완료');
|
|
}
|
|
|
|
/**
|
|
* 상태 업데이트
|
|
*/
|
|
update(key, value) {
|
|
const prevValue = this[key];
|
|
this[key] = value;
|
|
this.notifyListeners(key, value, prevValue);
|
|
}
|
|
|
|
/**
|
|
* 리스너 등록
|
|
*/
|
|
subscribe(key, callback) {
|
|
if (!this.listeners.has(key)) {
|
|
this.listeners.set(key, []);
|
|
}
|
|
this.listeners.get(key).push(callback);
|
|
}
|
|
|
|
/**
|
|
* 리스너에게 알림
|
|
*/
|
|
notifyListeners(key, newValue, prevValue) {
|
|
const keyListeners = this.listeners.get(key) || [];
|
|
keyListeners.forEach(callback => {
|
|
try {
|
|
callback(newValue, prevValue);
|
|
} catch (error) {
|
|
console.error(`[State] 리스너 오류 (${key}):`, error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 현재 사용자 정보 가져오기
|
|
*/
|
|
getUser() {
|
|
const user = localStorage.getItem('user');
|
|
return user ? JSON.parse(user) : null;
|
|
}
|
|
|
|
/**
|
|
* 토큰에서 사용자 정보 추출
|
|
*/
|
|
getCurrentUser() {
|
|
try {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) return null;
|
|
|
|
const payloadBase64 = token.split('.')[1];
|
|
if (payloadBase64) {
|
|
const payload = JSON.parse(atob(payloadBase64));
|
|
return payload;
|
|
}
|
|
} catch (error) {
|
|
console.log('[State] 토큰에서 사용자 정보 추출 실패:', error);
|
|
}
|
|
|
|
try {
|
|
const userInfo = localStorage.getItem('user') || localStorage.getItem('userInfo') || localStorage.getItem('currentUser');
|
|
if (userInfo) {
|
|
return JSON.parse(userInfo);
|
|
}
|
|
} catch (error) {
|
|
console.log('[State] localStorage에서 사용자 정보 가져오기 실패:', error);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 선택된 작업자 토글
|
|
*/
|
|
toggleWorkerSelection(workerId) {
|
|
if (this.selectedWorkers.has(workerId)) {
|
|
this.selectedWorkers.delete(workerId);
|
|
} else {
|
|
this.selectedWorkers.add(workerId);
|
|
}
|
|
this.notifyListeners('selectedWorkers', this.selectedWorkers, null);
|
|
}
|
|
|
|
/**
|
|
* 작업자 전체 선택/해제
|
|
*/
|
|
selectAllWorkers(select = true) {
|
|
if (select) {
|
|
this.workers.forEach(w => this.selectedWorkers.add(w.worker_id));
|
|
} else {
|
|
this.selectedWorkers.clear();
|
|
}
|
|
this.notifyListeners('selectedWorkers', this.selectedWorkers, null);
|
|
}
|
|
|
|
/**
|
|
* 작업 항목 카운터 증가
|
|
*/
|
|
incrementWorkEntryCounter() {
|
|
this.workEntryCounter++;
|
|
return this.workEntryCounter;
|
|
}
|
|
|
|
/**
|
|
* 탭 변경
|
|
*/
|
|
setCurrentTab(tab) {
|
|
const prevTab = this.currentTab;
|
|
this.currentTab = tab;
|
|
this.notifyListeners('currentTab', tab, prevTab);
|
|
}
|
|
|
|
/**
|
|
* 부적합 임시 저장소 초기화
|
|
*/
|
|
initTempDefects(index) {
|
|
if (!this.tempDefects[index]) {
|
|
this.tempDefects[index] = [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 부적합 추가
|
|
*/
|
|
addTempDefect(index, defect) {
|
|
this.initTempDefects(index);
|
|
this.tempDefects[index].push(defect);
|
|
this.notifyListeners('tempDefects', this.tempDefects, null);
|
|
}
|
|
|
|
/**
|
|
* 부적합 업데이트
|
|
*/
|
|
updateTempDefect(index, defectIndex, field, value) {
|
|
if (this.tempDefects[index] && this.tempDefects[index][defectIndex]) {
|
|
this.tempDefects[index][defectIndex][field] = value;
|
|
this.notifyListeners('tempDefects', this.tempDefects, null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 부적합 삭제
|
|
*/
|
|
removeTempDefect(index, defectIndex) {
|
|
if (this.tempDefects[index]) {
|
|
this.tempDefects[index].splice(defectIndex, 1);
|
|
this.notifyListeners('tempDefects', this.tempDefects, null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 일일 이슈 캐시 설정
|
|
*/
|
|
setDailyIssuesCache(dateStr, issues) {
|
|
this.dailyIssuesCache[dateStr] = issues;
|
|
}
|
|
|
|
/**
|
|
* 일일 이슈 캐시 조회
|
|
*/
|
|
getDailyIssuesCache(dateStr) {
|
|
return this.dailyIssuesCache[dateStr] || [];
|
|
}
|
|
|
|
/**
|
|
* 상태 초기화
|
|
*/
|
|
reset() {
|
|
this.selectedWorkers.clear();
|
|
this.workEntryCounter = 0;
|
|
this.currentStep = 1;
|
|
this.editingWorkId = null;
|
|
this.tempDefects = {};
|
|
this.currentDefectIndex = null;
|
|
this.dailyIssuesCache = {};
|
|
}
|
|
|
|
/**
|
|
* 디버그 출력
|
|
*/
|
|
debug() {
|
|
console.log('[State] 현재 상태:', {
|
|
workTypes: this.workTypes.length,
|
|
workers: this.workers.length,
|
|
projects: this.projects.length,
|
|
selectedWorkers: this.selectedWorkers.size,
|
|
currentTab: this.currentTab,
|
|
incompleteTbms: this.incompleteTbms.length,
|
|
tempDefects: Object.keys(this.tempDefects).length
|
|
});
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.DailyWorkReportState = new DailyWorkReportState();
|
|
|
|
// 하위 호환성을 위한 전역 변수 프록시
|
|
const stateProxy = window.DailyWorkReportState;
|
|
|
|
// 기존 전역 변수들과 호환
|
|
Object.defineProperties(window, {
|
|
workTypes: {
|
|
get: () => stateProxy.workTypes,
|
|
set: (v) => { stateProxy.workTypes = v; }
|
|
},
|
|
workStatusTypes: {
|
|
get: () => stateProxy.workStatusTypes,
|
|
set: (v) => { stateProxy.workStatusTypes = v; }
|
|
},
|
|
errorTypes: {
|
|
get: () => stateProxy.errorTypes,
|
|
set: (v) => { stateProxy.errorTypes = v; }
|
|
},
|
|
issueCategories: {
|
|
get: () => stateProxy.issueCategories,
|
|
set: (v) => { stateProxy.issueCategories = v; }
|
|
},
|
|
issueItems: {
|
|
get: () => stateProxy.issueItems,
|
|
set: (v) => { stateProxy.issueItems = v; }
|
|
},
|
|
workers: {
|
|
get: () => stateProxy.workers,
|
|
set: (v) => { stateProxy.workers = v; }
|
|
},
|
|
projects: {
|
|
get: () => stateProxy.projects,
|
|
set: (v) => { stateProxy.projects = v; }
|
|
},
|
|
selectedWorkers: {
|
|
get: () => stateProxy.selectedWorkers,
|
|
set: (v) => { stateProxy.selectedWorkers = v; }
|
|
},
|
|
incompleteTbms: {
|
|
get: () => stateProxy.incompleteTbms,
|
|
set: (v) => { stateProxy.incompleteTbms = v; }
|
|
},
|
|
tempDefects: {
|
|
get: () => stateProxy.tempDefects,
|
|
set: (v) => { stateProxy.tempDefects = v; }
|
|
},
|
|
dailyIssuesCache: {
|
|
get: () => stateProxy.dailyIssuesCache,
|
|
set: (v) => { stateProxy.dailyIssuesCache = v; }
|
|
},
|
|
currentTab: {
|
|
get: () => stateProxy.currentTab,
|
|
set: (v) => { stateProxy.currentTab = v; }
|
|
},
|
|
currentStep: {
|
|
get: () => stateProxy.currentStep,
|
|
set: (v) => { stateProxy.currentStep = v; }
|
|
},
|
|
editingWorkId: {
|
|
get: () => stateProxy.editingWorkId,
|
|
set: (v) => { stateProxy.editingWorkId = v; }
|
|
},
|
|
workEntryCounter: {
|
|
get: () => stateProxy.workEntryCounter,
|
|
set: (v) => { stateProxy.workEntryCounter = v; }
|
|
},
|
|
currentDefectIndex: {
|
|
get: () => stateProxy.currentDefectIndex,
|
|
set: (v) => { stateProxy.currentDefectIndex = v; }
|
|
},
|
|
currentEditingField: {
|
|
get: () => stateProxy.currentEditingField,
|
|
set: (v) => { stateProxy.currentEditingField = v; }
|
|
},
|
|
currentTimeValue: {
|
|
get: () => stateProxy.currentTimeValue,
|
|
set: (v) => { stateProxy.currentTimeValue = v; }
|
|
},
|
|
selectedWorkplace: {
|
|
get: () => stateProxy.selectedWorkplace,
|
|
set: (v) => { stateProxy.selectedWorkplace = v; }
|
|
},
|
|
selectedWorkplaceName: {
|
|
get: () => stateProxy.selectedWorkplaceName,
|
|
set: (v) => { stateProxy.selectedWorkplaceName = v; }
|
|
},
|
|
selectedWorkplaceCategory: {
|
|
get: () => stateProxy.selectedWorkplaceCategory,
|
|
set: (v) => { stateProxy.selectedWorkplaceCategory = v; }
|
|
},
|
|
selectedWorkplaceCategoryName: {
|
|
get: () => stateProxy.selectedWorkplaceCategoryName,
|
|
set: (v) => { stateProxy.selectedWorkplaceCategoryName = v; }
|
|
}
|
|
});
|