- 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>
490 lines
15 KiB
JavaScript
490 lines
15 KiB
JavaScript
/**
|
|
* TBM - API Client
|
|
* TBM 관련 모든 API 호출을 관리
|
|
*/
|
|
|
|
class TbmAPI {
|
|
constructor() {
|
|
this.state = window.TbmState;
|
|
this.utils = window.TbmUtils;
|
|
console.log('[TbmAPI] 초기화 완료');
|
|
}
|
|
|
|
/**
|
|
* 초기 데이터 로드 (작업자, 프로젝트, 안전 체크리스트, 공정, 작업, 작업장)
|
|
*/
|
|
async loadInitialData() {
|
|
try {
|
|
// 현재 로그인한 사용자 정보 가져오기
|
|
const userInfo = JSON.parse(localStorage.getItem('user') || '{}');
|
|
this.state.currentUser = userInfo;
|
|
console.log('👤 로그인 사용자:', this.state.currentUser, 'worker_id:', this.state.currentUser?.worker_id);
|
|
|
|
// 병렬로 데이터 로드
|
|
await Promise.all([
|
|
this.loadWorkers(),
|
|
this.loadProjects(),
|
|
this.loadSafetyChecks(),
|
|
this.loadWorkTypes(),
|
|
this.loadTasks(),
|
|
this.loadWorkplaces(),
|
|
this.loadWorkplaceCategories()
|
|
]);
|
|
|
|
console.log('✅ 초기 데이터 로드 완료');
|
|
} catch (error) {
|
|
console.error('❌ 초기 데이터 로드 오류:', error);
|
|
window.showToast?.('데이터를 불러오는 중 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업자 목록 로드 (생산팀 소속만)
|
|
*/
|
|
async loadWorkers() {
|
|
try {
|
|
const response = await window.apiCall('/workers?limit=1000&department_id=1');
|
|
if (response) {
|
|
let workers = Array.isArray(response) ? response : (response.data || []);
|
|
// 활성 상태인 작업자만 필터링
|
|
workers = workers.filter(w => w.status === 'active' && w.employment_status === 'employed');
|
|
this.state.allWorkers = workers;
|
|
console.log('✅ 작업자 목록 로드:', workers.length + '명');
|
|
return workers;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 작업자 로딩 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 프로젝트 목록 로드 (활성 프로젝트만)
|
|
*/
|
|
async loadProjects() {
|
|
try {
|
|
const response = await window.apiCall('/projects?is_active=1');
|
|
if (response) {
|
|
const projects = Array.isArray(response) ? response : (response.data || []);
|
|
this.state.allProjects = projects.filter(p =>
|
|
p.is_active === 1 || p.is_active === true || p.is_active === '1'
|
|
);
|
|
console.log('✅ 프로젝트 목록 로드:', this.state.allProjects.length + '개 (활성)');
|
|
return this.state.allProjects;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 프로젝트 로딩 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 안전 체크리스트 로드
|
|
*/
|
|
async loadSafetyChecks() {
|
|
try {
|
|
const response = await window.apiCall('/tbm/safety-checks');
|
|
if (response && response.success) {
|
|
this.state.allSafetyChecks = response.data;
|
|
console.log('✅ 안전 체크리스트 로드:', this.state.allSafetyChecks.length + '개');
|
|
return this.state.allSafetyChecks;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 안전 체크리스트 로딩 오류:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 공정(Work Types) 목록 로드
|
|
*/
|
|
async loadWorkTypes() {
|
|
try {
|
|
const response = await window.apiCall('/daily-work-reports/work-types');
|
|
if (response && response.success) {
|
|
this.state.allWorkTypes = response.data || [];
|
|
console.log('✅ 공정 목록 로드:', this.state.allWorkTypes.length + '개');
|
|
return this.state.allWorkTypes;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 공정 로딩 오류:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업(Tasks) 목록 로드
|
|
*/
|
|
async loadTasks() {
|
|
try {
|
|
const response = await window.apiCall('/tasks/active/list');
|
|
if (response && response.success) {
|
|
this.state.allTasks = response.data || [];
|
|
console.log('✅ 작업 목록 로드:', this.state.allTasks.length + '개');
|
|
return this.state.allTasks;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 작업 로딩 오류:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업장 목록 로드
|
|
*/
|
|
async loadWorkplaces() {
|
|
try {
|
|
const response = await window.apiCall('/workplaces?is_active=true');
|
|
if (response && response.success) {
|
|
this.state.allWorkplaces = response.data || [];
|
|
console.log('✅ 작업장 목록 로드:', this.state.allWorkplaces.length + '개');
|
|
return this.state.allWorkplaces;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 작업장 로딩 오류:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업장 카테고리 로드
|
|
*/
|
|
async loadWorkplaceCategories() {
|
|
try {
|
|
const response = await window.apiCall('/workplaces/categories/active/list');
|
|
if (response && response.success) {
|
|
this.state.allWorkplaceCategories = response.data || [];
|
|
console.log('✅ 작업장 카테고리 로드:', this.state.allWorkplaceCategories.length + '개');
|
|
return this.state.allWorkplaceCategories;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 작업장 카테고리 로딩 오류:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 오늘의 TBM만 로드 (TBM 입력 탭용)
|
|
*/
|
|
async loadTodayOnlyTbm() {
|
|
const today = this.utils.getTodayKST();
|
|
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/date/${today}`);
|
|
|
|
if (response && response.success) {
|
|
this.state.todaySessions = response.data || [];
|
|
} else {
|
|
this.state.todaySessions = [];
|
|
}
|
|
console.log('✅ 오늘 TBM 로드:', this.state.todaySessions.length + '건');
|
|
return this.state.todaySessions;
|
|
} catch (error) {
|
|
console.error('❌ 오늘 TBM 조회 오류:', error);
|
|
window.showToast?.('오늘 TBM을 불러오는 중 오류가 발생했습니다.', 'error');
|
|
this.state.todaySessions = [];
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 최근 TBM을 날짜별로 그룹화하여 로드
|
|
*/
|
|
async loadRecentTbmGroupedByDate() {
|
|
try {
|
|
const today = new Date();
|
|
const dates = [];
|
|
|
|
// 최근 N일의 날짜 생성
|
|
for (let i = 0; i < this.state.loadedDaysCount; i++) {
|
|
const date = new Date(today);
|
|
date.setDate(date.getDate() - i);
|
|
const dateStr = date.toISOString().split('T')[0];
|
|
dates.push(dateStr);
|
|
}
|
|
|
|
// 각 날짜의 TBM 로드
|
|
this.state.dateGroupedSessions = {};
|
|
this.state.allLoadedSessions = [];
|
|
|
|
const promises = dates.map(date => window.apiCall(`/tbm/sessions/date/${date}`));
|
|
const results = await Promise.all(promises);
|
|
|
|
results.forEach((response, index) => {
|
|
const date = dates[index];
|
|
if (response && response.success && response.data && response.data.length > 0) {
|
|
let sessions = response.data;
|
|
|
|
// admin이 아니면 본인이 작성한 TBM만 필터링
|
|
if (!this.state.isAdminUser()) {
|
|
const userId = this.state.currentUser?.user_id;
|
|
const workerId = this.state.currentUser?.worker_id;
|
|
sessions = sessions.filter(s => {
|
|
return s.created_by === userId ||
|
|
s.leader_id === workerId ||
|
|
s.created_by_name === this.state.currentUser?.name;
|
|
});
|
|
}
|
|
|
|
if (sessions.length > 0) {
|
|
this.state.dateGroupedSessions[date] = sessions;
|
|
this.state.allLoadedSessions = this.state.allLoadedSessions.concat(sessions);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('✅ 날짜별 TBM 로드 완료:', this.state.allLoadedSessions.length + '건');
|
|
return this.state.dateGroupedSessions;
|
|
|
|
} catch (error) {
|
|
console.error('❌ TBM 날짜별 로드 오류:', error);
|
|
window.showToast?.('TBM을 불러오는 중 오류가 발생했습니다.', 'error');
|
|
this.state.dateGroupedSessions = {};
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 특정 날짜의 TBM 세션 목록 로드
|
|
*/
|
|
async loadTbmSessionsByDate(date) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/date/${date}`);
|
|
|
|
if (response && response.success) {
|
|
this.state.allSessions = response.data || [];
|
|
} else {
|
|
this.state.allSessions = [];
|
|
}
|
|
return this.state.allSessions;
|
|
} catch (error) {
|
|
console.error('❌ TBM 세션 조회 오류:', error);
|
|
window.showToast?.('TBM 세션을 불러오는 중 오류가 발생했습니다.', 'error');
|
|
this.state.allSessions = [];
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 세션 생성
|
|
*/
|
|
async createTbmSession(sessionData) {
|
|
try {
|
|
const response = await window.apiCall('/tbm/sessions', 'POST', sessionData);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '세션 생성 실패');
|
|
}
|
|
console.log('✅ TBM 세션 생성 완료:', response.data?.session_id);
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ TBM 세션 생성 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 세션 정보 조회
|
|
*/
|
|
async getSession(sessionId) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/${sessionId}`);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '세션 조회 실패');
|
|
}
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('❌ TBM 세션 조회 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 팀원 조회
|
|
*/
|
|
async getTeamMembers(sessionId) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/${sessionId}/team`);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '팀원 조회 실패');
|
|
}
|
|
return response.data || [];
|
|
} catch (error) {
|
|
console.error('❌ TBM 팀원 조회 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 팀원 일괄 추가
|
|
*/
|
|
async addTeamMembers(sessionId, members) {
|
|
try {
|
|
const response = await window.apiCall(
|
|
`/tbm/sessions/${sessionId}/team/batch`,
|
|
'POST',
|
|
{ members }
|
|
);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '팀원 추가 실패');
|
|
}
|
|
console.log('✅ TBM 팀원 추가 완료:', members.length + '명');
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ TBM 팀원 추가 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 팀원 전체 삭제
|
|
*/
|
|
async clearTeamMembers(sessionId) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/${sessionId}/team/clear`, 'DELETE');
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ TBM 팀원 삭제 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 안전 체크 조회
|
|
*/
|
|
async getSafetyChecks(sessionId) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/${sessionId}/safety`);
|
|
return response?.data || [];
|
|
} catch (error) {
|
|
console.error('❌ 안전 체크 조회 오류:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 안전 체크 (필터링된) 조회
|
|
*/
|
|
async getFilteredSafetyChecks(sessionId) {
|
|
try {
|
|
const response = await window.apiCall(`/tbm/sessions/${sessionId}/safety-checks/filtered`);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '체크리스트를 불러올 수 없습니다.');
|
|
}
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('❌ 필터링된 안전 체크 조회 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 안전 체크 저장
|
|
*/
|
|
async saveSafetyChecks(sessionId, records) {
|
|
try {
|
|
const response = await window.apiCall(
|
|
`/tbm/sessions/${sessionId}/safety`,
|
|
'POST',
|
|
{ records }
|
|
);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '저장 실패');
|
|
}
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ 안전 체크 저장 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TBM 세션 완료 처리
|
|
*/
|
|
async completeTbmSession(sessionId, endTime) {
|
|
try {
|
|
const response = await window.apiCall(
|
|
`/tbm/sessions/${sessionId}/complete`,
|
|
'POST',
|
|
{ end_time: endTime }
|
|
);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '완료 처리 실패');
|
|
}
|
|
console.log('✅ TBM 완료 처리:', sessionId);
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ TBM 완료 처리 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업 인계 저장
|
|
*/
|
|
async saveHandover(handoverData) {
|
|
try {
|
|
const response = await window.apiCall('/tbm/handovers', 'POST', handoverData);
|
|
if (!response || !response.success) {
|
|
throw new Error(response?.message || '인계 요청 실패');
|
|
}
|
|
return response;
|
|
} catch (error) {
|
|
console.error('❌ 작업 인계 저장 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 카테고리별 작업장 로드
|
|
*/
|
|
async loadWorkplacesByCategory(categoryId) {
|
|
try {
|
|
const response = await window.apiCall(`/workplaces?category_id=${categoryId}`);
|
|
if (!response || !response.success || !response.data) {
|
|
return [];
|
|
}
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('❌ 작업장 로드 오류:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 작업장 지도 영역 로드
|
|
*/
|
|
async loadMapRegions(categoryId) {
|
|
try {
|
|
const response = await window.apiCall(`/workplaces/categories/${categoryId}/map-regions`);
|
|
if (response && response.success) {
|
|
this.state.mapRegions = response.data || [];
|
|
return this.state.mapRegions;
|
|
}
|
|
return [];
|
|
} catch (error) {
|
|
console.error('❌ 지도 영역 로드 오류:', error);
|
|
return [];
|
|
}
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.TbmAPI = new TbmAPI();
|
|
|
|
// 하위 호환성: 기존 함수들
|
|
window.loadInitialData = () => window.TbmAPI.loadInitialData();
|
|
window.loadTodayOnlyTbm = () => window.TbmAPI.loadTodayOnlyTbm();
|
|
window.loadTodayTbm = () => window.TbmAPI.loadRecentTbmGroupedByDate();
|
|
window.loadAllTbm = () => {
|
|
window.TbmState.loadedDaysCount = 30;
|
|
return window.TbmAPI.loadRecentTbmGroupedByDate();
|
|
};
|
|
window.loadRecentTbmGroupedByDate = () => window.TbmAPI.loadRecentTbmGroupedByDate();
|
|
window.loadTbmSessionsByDate = (date) => window.TbmAPI.loadTbmSessionsByDate(date);
|
|
window.loadWorkplaceCategories = () => window.TbmAPI.loadWorkplaceCategories();
|
|
window.loadWorkplacesByCategory = (categoryId) => window.TbmAPI.loadWorkplacesByCategory(categoryId);
|
|
|
|
// 더 많은 날짜 로드
|
|
window.loadMoreTbmDays = async function() {
|
|
window.TbmState.loadedDaysCount += 7;
|
|
await window.TbmAPI.loadRecentTbmGroupedByDate();
|
|
window.showToast?.(`최근 ${window.TbmState.loadedDaysCount}일의 TBM을 로드했습니다.`, 'success');
|
|
};
|
|
|
|
console.log('[Module] tbm/api.js 로드 완료');
|