776 lines
25 KiB
JavaScript
776 lines
25 KiB
JavaScript
// work-review.js - 통합 API 설정 적용 버전
|
||
|
||
// =================================================================
|
||
// 🌐 통합 API 설정 import
|
||
// =================================================================
|
||
import { API, getAuthHeaders, apiCall } from '/js/api-config.js';
|
||
|
||
// 전역 변수
|
||
let currentDate = new Date();
|
||
let selectedDate = null;
|
||
let selectedDateData = null;
|
||
let basicData = {
|
||
workTypes: [],
|
||
workStatusTypes: [],
|
||
errorTypes: [],
|
||
projects: []
|
||
};
|
||
|
||
// 현재 사용자 정보 가져오기
|
||
function 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('토큰에서 사용자 정보 추출 실패:', error);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 메시지 표시
|
||
function showMessage(message, type = 'info') {
|
||
const container = document.getElementById('message-container');
|
||
container.innerHTML = `<div class="message ${type}">${message}</div>`;
|
||
|
||
if (type !== 'loading') {
|
||
setTimeout(() => {
|
||
container.innerHTML = '';
|
||
}, 5000);
|
||
}
|
||
}
|
||
|
||
// 날짜 포맷팅
|
||
function formatDate(date) {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
return `${year}-${month}-${day}`;
|
||
}
|
||
|
||
// 월 표시 업데이트
|
||
function updateMonthDisplay() {
|
||
const monthElement = document.getElementById('currentMonth');
|
||
const year = currentDate.getFullYear();
|
||
const month = currentDate.getMonth() + 1;
|
||
monthElement.textContent = `${year}년 ${month}월`;
|
||
}
|
||
|
||
// 근무 유형 분류
|
||
function classifyWorkType(totalHours) {
|
||
if (totalHours === 0) return { type: 'vacation', label: '휴무' };
|
||
if (totalHours === 2) return { type: 'vacation', label: '조퇴' };
|
||
if (totalHours === 4) return { type: 'vacation', label: '반차' };
|
||
if (totalHours === 6) return { type: 'vacation', label: '반반차' };
|
||
if (totalHours === 8) return { type: 'normal-work', label: '정시근무' };
|
||
if (totalHours > 8) return { type: 'overtime', label: '잔업' };
|
||
return { type: 'vacation', label: '기타' };
|
||
}
|
||
|
||
// 캘린더 렌더링 (데이터 로드 없이)
|
||
function renderCalendar() {
|
||
const calendar = document.getElementById('calendar');
|
||
|
||
// 기존 날짜 셀들 제거 (헤더는 유지)
|
||
const dayHeaders = calendar.querySelectorAll('.day-header');
|
||
calendar.innerHTML = '';
|
||
dayHeaders.forEach(header => calendar.appendChild(header));
|
||
|
||
const year = currentDate.getFullYear();
|
||
const month = currentDate.getMonth();
|
||
|
||
// 해당 월의 첫째 날과 마지막 날
|
||
const firstDay = new Date(year, month, 1);
|
||
const lastDay = new Date(year, month + 1, 0);
|
||
|
||
// 첫째 주의 시작 (일요일부터 시작)
|
||
const startDate = new Date(firstDay);
|
||
startDate.setDate(startDate.getDate() - firstDay.getDay());
|
||
|
||
// 마지막 주의 끝
|
||
const endDate = new Date(lastDay);
|
||
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()));
|
||
|
||
// 오늘 날짜
|
||
const today = new Date();
|
||
const todayStr = formatDate(today);
|
||
|
||
// 날짜 셀 생성
|
||
let currentCalendarDate = new Date(startDate);
|
||
|
||
while (currentCalendarDate <= endDate) {
|
||
const dateStr = formatDate(currentCalendarDate);
|
||
const isCurrentMonth = currentCalendarDate.getMonth() === month;
|
||
const isToday = dateStr === todayStr;
|
||
const isSelected = selectedDate === dateStr;
|
||
|
||
const dayCell = document.createElement('div');
|
||
dayCell.className = 'day-cell';
|
||
|
||
if (!isCurrentMonth) {
|
||
dayCell.classList.add('other-month');
|
||
}
|
||
|
||
if (isToday) {
|
||
dayCell.classList.add('today');
|
||
}
|
||
|
||
if (isSelected) {
|
||
dayCell.classList.add('selected');
|
||
}
|
||
|
||
// 날짜 번호
|
||
const dayNumber = document.createElement('div');
|
||
dayNumber.className = 'day-number';
|
||
dayNumber.textContent = currentCalendarDate.getDate();
|
||
dayCell.appendChild(dayNumber);
|
||
|
||
// 클릭 이벤트 - 현재 월의 날짜만 클릭 가능
|
||
if (isCurrentMonth) {
|
||
dayCell.style.cursor = 'pointer';
|
||
dayCell.addEventListener('click', () => {
|
||
selectedDate = dateStr;
|
||
loadDayData(dateStr);
|
||
renderCalendar(); // 선택 상태 업데이트를 위해 재렌더링
|
||
});
|
||
}
|
||
|
||
calendar.appendChild(dayCell);
|
||
currentCalendarDate.setDate(currentCalendarDate.getDate() + 1);
|
||
}
|
||
}
|
||
|
||
// 특정 날짜 데이터 로드 (통합 API 사용)
|
||
async function loadDayData(dateStr) {
|
||
try {
|
||
showMessage(`${dateStr} 데이터를 불러오는 중... (통합 API)`, 'loading');
|
||
|
||
const data = await apiCall(`${API}/daily-work-reports?date=${dateStr}`);
|
||
const dataArray = Array.isArray(data) ? data : (data.data || []);
|
||
|
||
// 데이터 처리
|
||
processDayData(dateStr, dataArray);
|
||
renderDayInfo();
|
||
|
||
document.getElementById('message-container').innerHTML = '';
|
||
|
||
} catch (error) {
|
||
console.error('날짜 데이터 로드 실패:', error);
|
||
showMessage('데이터를 불러올 수 없습니다: ' + error.message, 'error');
|
||
selectedDateData = null;
|
||
renderDayInfo();
|
||
}
|
||
}
|
||
|
||
// 일별 데이터 처리
|
||
function processDayData(dateStr, works) {
|
||
const dayData = {
|
||
date: dateStr,
|
||
totalHours: 0,
|
||
workers: new Set(),
|
||
reviewed: Math.random() > 0.3, // 임시: 70% 확률로 검토 완료
|
||
details: works
|
||
};
|
||
|
||
works.forEach(work => {
|
||
dayData.totalHours += parseFloat(work.work_hours || 0);
|
||
dayData.workers.add(work.worker_name || work.worker_id);
|
||
});
|
||
|
||
const workType = classifyWorkType(dayData.totalHours);
|
||
dayData.workType = workType.type;
|
||
dayData.workLabel = workType.label;
|
||
|
||
selectedDateData = dayData;
|
||
}
|
||
|
||
// 선택된 날짜 정보 렌더링
|
||
function renderDayInfo() {
|
||
const dayInfoContainer = document.getElementById('day-info-container');
|
||
|
||
if (!selectedDate) {
|
||
dayInfoContainer.innerHTML = `
|
||
<div class="day-info-placeholder">
|
||
<h3>📅 날짜를 선택하세요</h3>
|
||
<p>캘린더에서 날짜를 클릭하면 해당 날짜의 작업 정보를 확인할 수 있습니다.</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
if (!selectedDateData) {
|
||
dayInfoContainer.innerHTML = `
|
||
<div class="day-info-placeholder">
|
||
<h3>📅 ${selectedDate}</h3>
|
||
<p>해당 날짜에 등록된 작업이 없습니다.</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const data = selectedDateData;
|
||
|
||
// 작업자별 상세 정보 생성
|
||
const workerDetailsHtml = Array.from(data.workers).map(worker => {
|
||
const workerWorks = data.details.filter(w => (w.worker_name || w.worker_id) === worker);
|
||
const workerHours = workerWorks.reduce((sum, w) => sum + parseFloat(w.work_hours || 0), 0);
|
||
|
||
const workerWorkItemsHtml = workerWorks.map(work => `
|
||
<div class="work-item-detail">
|
||
<div class="work-item-info">
|
||
<strong>${work.project_name || '프로젝트'}</strong> - ${work.work_hours}시간<br>
|
||
<small>작업: ${work.work_type_name || '미지정'} | 상태: ${work.work_status_name || '미지정'}</small>
|
||
${work.error_type_name ? `<br><small style="color: #dc3545;">에러: ${work.error_type_name}</small>` : ''}
|
||
</div>
|
||
<div class="work-item-actions">
|
||
<button class="edit-work-btn" onclick="editWorkItem('${work.id}')">✏️ 수정</button>
|
||
<button class="delete-work-btn" onclick="deleteWorkItem('${work.id}')">🗑️ 삭제</button>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
return `
|
||
<div class="worker-detail-section">
|
||
<div class="worker-header-detail">
|
||
<strong>👤 ${worker}</strong> - 총 ${workerHours}시간
|
||
<button class="delete-worker-btn" onclick="deleteWorkerAllWorks('${selectedDate}', '${worker}')">
|
||
🗑️ 전체삭제
|
||
</button>
|
||
</div>
|
||
<div class="worker-work-items">
|
||
${workerWorkItemsHtml}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
dayInfoContainer.innerHTML = `
|
||
<div class="day-info-content">
|
||
<div class="day-info-header">
|
||
<h3>📅 ${selectedDate} 작업 정보</h3>
|
||
<div class="day-info-actions">
|
||
<button class="review-toggle ${data.reviewed ? 'reviewed' : ''}" onclick="toggleReview()">
|
||
${data.reviewed ? '✅ 검토완료' : '⏳ 검토하기'}
|
||
</button>
|
||
<button class="refresh-day-btn" onclick="refreshCurrentDay()">
|
||
🔄 새로고침
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="day-summary">
|
||
<div class="summary-item">
|
||
<span class="summary-label">총 작업시간:</span>
|
||
<span class="summary-value">${data.totalHours}시간</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">근무 유형:</span>
|
||
<span class="summary-value ${data.workType}">${data.workLabel}</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">작업자 수:</span>
|
||
<span class="summary-value">${data.workers.size}명</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">검토 상태:</span>
|
||
<span class="summary-value ${data.reviewed ? 'reviewed' : 'unreviewed'}">
|
||
${data.reviewed ? '✅ 검토완료' : '⏳ 미검토'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="workers-detail-container">
|
||
<h4>👥 작업자별 상세</h4>
|
||
${workerDetailsHtml}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// 검토 상태 토글
|
||
function toggleReview() {
|
||
if (selectedDateData) {
|
||
selectedDateData.reviewed = !selectedDateData.reviewed;
|
||
renderDayInfo();
|
||
|
||
// TODO: 실제로는 여기서 API 호출해서 DB에 저장해야 함
|
||
console.log(`검토 상태 변경: ${selectedDate} - ${selectedDateData.reviewed ? '검토완료' : '미검토'}`);
|
||
|
||
showMessage(`검토 상태가 ${selectedDateData.reviewed ? '완료' : '미완료'}로 변경되었습니다.`, 'success');
|
||
}
|
||
}
|
||
|
||
// 현재 날짜 새로고침
|
||
function refreshCurrentDay() {
|
||
if (selectedDate) {
|
||
loadDayData(selectedDate);
|
||
}
|
||
}
|
||
|
||
// 🛠️ 작업 항목 수정 함수 (통합 API 사용)
|
||
async function editWorkItem(workId) {
|
||
try {
|
||
console.log('수정할 작업 ID:', workId);
|
||
|
||
if (!selectedDateData) {
|
||
showMessage('작업 데이터를 찾을 수 없습니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
const workData = selectedDateData.details.find(work => work.id == workId);
|
||
if (!workData) {
|
||
showMessage('수정할 작업 데이터를 찾을 수 없습니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
// 기본 데이터가 없으면 로드
|
||
if (basicData.workTypes.length === 0) {
|
||
showMessage('기본 데이터를 불러오는 중... (통합 API)', 'loading');
|
||
await loadBasicData();
|
||
}
|
||
|
||
showEditModal(workData);
|
||
document.getElementById('message-container').innerHTML = '';
|
||
|
||
} catch (error) {
|
||
console.error('작업 정보 조회 오류:', error);
|
||
showMessage('작업 정보를 불러올 수 없습니다: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 🛠️ 수정 모달 표시 (개선된 버전)
|
||
function showEditModal(workData) {
|
||
const modalHtml = `
|
||
<div class="edit-modal" id="editModal">
|
||
<div class="edit-modal-content">
|
||
<div class="edit-modal-header">
|
||
<h3>✏️ 작업 수정</h3>
|
||
<button class="close-modal-btn" onclick="closeEditModal()">×</button>
|
||
</div>
|
||
<div class="edit-modal-body">
|
||
<div class="edit-form-group">
|
||
<label>🏗️ 프로젝트</label>
|
||
<select class="edit-select" id="editProject" required>
|
||
<option value="">프로젝트 선택</option>
|
||
${basicData.projects.map(p => `
|
||
<option value="${p.project_id}" ${p.project_id == workData.project_id ? 'selected' : ''}>
|
||
${p.project_name}
|
||
</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="edit-form-group">
|
||
<label>⚙️ 작업 유형</label>
|
||
<select class="edit-select" id="editWorkType" required>
|
||
<option value="">작업 유형 선택</option>
|
||
${basicData.workTypes.map(wt => `
|
||
<option value="${wt.id}" ${wt.id == workData.work_type_id ? 'selected' : ''}>
|
||
${wt.name}
|
||
</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="edit-form-group">
|
||
<label>📊 업무 상태</label>
|
||
<select class="edit-select" id="editWorkStatus" required>
|
||
<option value="">업무 상태 선택</option>
|
||
${basicData.workStatusTypes.map(ws => `
|
||
<option value="${ws.id}" ${ws.id == workData.work_status_id ? 'selected' : ''}>
|
||
${ws.name}
|
||
</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="edit-form-group" id="editErrorTypeGroup" style="${workData.work_status_id == 2 ? '' : 'display: none;'}">
|
||
<label>❌ 에러 유형</label>
|
||
<select class="edit-select" id="editErrorType">
|
||
<option value="">에러 유형 선택</option>
|
||
${basicData.errorTypes.map(et => `
|
||
<option value="${et.id}" ${et.id == workData.error_type_id ? 'selected' : ''}>
|
||
${et.name}
|
||
</option>
|
||
`).join('')}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="edit-form-group">
|
||
<label>⏰ 작업 시간</label>
|
||
<input type="number" class="edit-input" id="editWorkHours"
|
||
value="${workData.work_hours}"
|
||
min="0" max="24" step="0.5" required>
|
||
</div>
|
||
</div>
|
||
<div class="edit-modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeEditModal()">취소</button>
|
||
<button class="btn btn-success" onclick="saveEditedWork('${workData.id}')">💾 저장</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||
|
||
// 업무 상태 변경 이벤트
|
||
document.getElementById('editWorkStatus').addEventListener('change', (e) => {
|
||
const errorTypeGroup = document.getElementById('editErrorTypeGroup');
|
||
if (e.target.value === '2') {
|
||
errorTypeGroup.style.display = 'block';
|
||
} else {
|
||
errorTypeGroup.style.display = 'none';
|
||
}
|
||
});
|
||
}
|
||
|
||
// 🛠️ 수정 모달 닫기
|
||
function closeEditModal() {
|
||
const modal = document.getElementById('editModal');
|
||
if (modal) {
|
||
modal.remove();
|
||
}
|
||
}
|
||
|
||
// 🛠️ 수정된 작업 저장 (통합 API 사용)
|
||
async function saveEditedWork(workId) {
|
||
try {
|
||
// 입력값 검증
|
||
const projectId = document.getElementById('editProject').value;
|
||
const workTypeId = document.getElementById('editWorkType').value;
|
||
const workStatusId = document.getElementById('editWorkStatus').value;
|
||
const errorTypeId = document.getElementById('editErrorType').value;
|
||
const workHours = document.getElementById('editWorkHours').value;
|
||
|
||
// 필수값 체크
|
||
if (!projectId) {
|
||
showMessage('프로젝트를 선택해주세요.', 'error');
|
||
document.getElementById('editProject').focus();
|
||
return;
|
||
}
|
||
|
||
if (!workTypeId) {
|
||
showMessage('작업 유형을 선택해주세요.', 'error');
|
||
document.getElementById('editWorkType').focus();
|
||
return;
|
||
}
|
||
|
||
if (!workStatusId) {
|
||
showMessage('업무 상태를 선택해주세요.', 'error');
|
||
document.getElementById('editWorkStatus').focus();
|
||
return;
|
||
}
|
||
|
||
if (!workHours || workHours <= 0) {
|
||
showMessage('작업 시간을 올바르게 입력해주세요.', 'error');
|
||
document.getElementById('editWorkHours').focus();
|
||
return;
|
||
}
|
||
|
||
if (workStatusId === '2' && !errorTypeId) {
|
||
showMessage('에러 상태인 경우 에러 유형을 선택해주세요.', 'error');
|
||
document.getElementById('editErrorType').focus();
|
||
return;
|
||
}
|
||
|
||
const updateData = {
|
||
project_id: parseInt(projectId),
|
||
work_type_id: parseInt(workTypeId),
|
||
work_status_id: parseInt(workStatusId),
|
||
error_type_id: errorTypeId ? parseInt(errorTypeId) : null,
|
||
work_hours: parseFloat(workHours)
|
||
};
|
||
|
||
showMessage('작업을 수정하는 중... (통합 API)', 'loading');
|
||
|
||
// 저장 버튼 비활성화
|
||
const saveBtn = document.querySelector('.btn-success');
|
||
const originalText = saveBtn.textContent;
|
||
saveBtn.textContent = '저장 중...';
|
||
saveBtn.disabled = true;
|
||
|
||
const result = await apiCall(`${API}/daily-work-reports/my-entry/${workId}`, {
|
||
method: 'PUT',
|
||
body: JSON.stringify(updateData)
|
||
});
|
||
|
||
console.log('✅ 수정 성공 (통합 API):', result);
|
||
showMessage('✅ 작업이 성공적으로 수정되었습니다!', 'success');
|
||
|
||
closeEditModal();
|
||
refreshCurrentDay(); // 현재 날짜 데이터 새로고침
|
||
|
||
} catch (error) {
|
||
console.error('❌ 수정 실패:', error);
|
||
showMessage('수정 중 오류가 발생했습니다: ' + error.message, 'error');
|
||
|
||
// 버튼 복원
|
||
const saveBtn = document.querySelector('.btn-success');
|
||
if (saveBtn) {
|
||
saveBtn.textContent = '💾 저장';
|
||
saveBtn.disabled = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 🗑️ 작업 항목 삭제 (통합 API 사용)
|
||
async function deleteWorkItem(workId) {
|
||
// 확인 대화상자
|
||
const confirmDelete = await showConfirmDialog(
|
||
'작업 삭제 확인',
|
||
'정말로 이 작업을 삭제하시겠습니까?',
|
||
'삭제된 작업은 복구할 수 없습니다.'
|
||
);
|
||
|
||
if (!confirmDelete) return;
|
||
|
||
try {
|
||
console.log('삭제할 작업 ID:', workId);
|
||
|
||
showMessage('작업을 삭제하는 중... (통합 API)', 'loading');
|
||
|
||
const result = await apiCall(`${API}/daily-work-reports/my-entry/${workId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
console.log('✅ 삭제 성공 (통합 API):', result);
|
||
showMessage('✅ 작업이 성공적으로 삭제되었습니다!', 'success');
|
||
|
||
refreshCurrentDay(); // 현재 날짜 데이터 새로고침
|
||
|
||
} catch (error) {
|
||
console.error('❌ 삭제 실패:', error);
|
||
showMessage('삭제 중 오류가 발생했습니다: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 🗑️ 작업자의 모든 작업 삭제 (통합 API 사용)
|
||
async function deleteWorkerAllWorks(date, workerName) {
|
||
// 확인 대화상자
|
||
const confirmDelete = await showConfirmDialog(
|
||
'전체 작업 삭제 확인',
|
||
`정말로 ${workerName}님의 ${date} 모든 작업을 삭제하시겠습니까?`,
|
||
'삭제된 작업들은 복구할 수 없습니다.'
|
||
);
|
||
|
||
if (!confirmDelete) return;
|
||
|
||
try {
|
||
if (!selectedDateData) return;
|
||
|
||
const workerWorks = selectedDateData.details.filter(w => (w.worker_name || w.worker_id) === workerName);
|
||
|
||
if (workerWorks.length === 0) {
|
||
showMessage('삭제할 작업이 없습니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
showMessage(`${workerName}님의 작업들을 삭제하는 중... (통합 API)`, 'loading');
|
||
|
||
// 순차적으로 삭제 (병렬 처리하면 서버 부하 발생 가능)
|
||
let successCount = 0;
|
||
let failCount = 0;
|
||
|
||
for (const work of workerWorks) {
|
||
try {
|
||
await apiCall(`${API}/daily-work-reports/my-entry/${work.id}`, {
|
||
method: 'DELETE'
|
||
});
|
||
successCount++;
|
||
} catch (error) {
|
||
console.error(`작업 ${work.id} 삭제 실패:`, error);
|
||
failCount++;
|
||
}
|
||
}
|
||
|
||
if (failCount === 0) {
|
||
showMessage(`✅ ${workerName}님의 모든 작업(${successCount}개)이 삭제되었습니다!`, 'success');
|
||
} else {
|
||
showMessage(`⚠️ ${successCount}개 삭제 완료, ${failCount}개 삭제 실패`, 'warning');
|
||
}
|
||
|
||
refreshCurrentDay(); // 현재 날짜 데이터 새로고침
|
||
|
||
} catch (error) {
|
||
console.error('❌ 전체 삭제 실패:', error);
|
||
showMessage('작업 삭제 중 오류가 발생했습니다: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 확인 대화상자 표시
|
||
function showConfirmDialog(title, message, warning) {
|
||
return new Promise((resolve) => {
|
||
const modalHtml = `
|
||
<div class="confirm-modal" id="confirmModal">
|
||
<div class="confirm-modal-content">
|
||
<div class="confirm-modal-header">
|
||
<h3>⚠️ ${title}</h3>
|
||
</div>
|
||
<div class="confirm-modal-body">
|
||
<p><strong>${message}</strong></p>
|
||
<p style="color: #dc3545; font-size: 0.9rem;">${warning}</p>
|
||
</div>
|
||
<div class="confirm-modal-footer">
|
||
<button class="btn btn-secondary" onclick="resolveConfirm(false)">취소</button>
|
||
<button class="btn btn-danger" onclick="resolveConfirm(true)">삭제</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||
|
||
// 전역 함수로 resolve 함수 노출
|
||
window.resolveConfirm = (result) => {
|
||
const modal = document.getElementById('confirmModal');
|
||
if (modal) modal.remove();
|
||
delete window.resolveConfirm;
|
||
resolve(result);
|
||
};
|
||
});
|
||
}
|
||
|
||
// 기본 데이터 로드 (통합 API 사용)
|
||
async function loadBasicData() {
|
||
try {
|
||
console.log('🔗 통합 API 설정을 사용한 기본 데이터 로딩...');
|
||
|
||
const promises = [
|
||
// 프로젝트 로드
|
||
apiCall(`${API}/projects`)
|
||
.then(data => Array.isArray(data) ? data : (data.projects || []))
|
||
.catch(() => []),
|
||
|
||
// 작업 유형 로드
|
||
apiCall(`${API}/daily-work-reports/work-types`)
|
||
.then(data => Array.isArray(data) ? data : [
|
||
{id: 1, name: 'Base'},
|
||
{id: 2, name: 'Vessel'},
|
||
{id: 3, name: 'Piping'}
|
||
])
|
||
.catch(() => [
|
||
{id: 1, name: 'Base'},
|
||
{id: 2, name: 'Vessel'},
|
||
{id: 3, name: 'Piping'}
|
||
]),
|
||
|
||
// 업무 상태 유형 로드
|
||
apiCall(`${API}/daily-work-reports/work-status-types`)
|
||
.then(data => Array.isArray(data) ? data : [
|
||
{id: 1, name: '정규'},
|
||
{id: 2, name: '에러'}
|
||
])
|
||
.catch(() => [
|
||
{id: 1, name: '정규'},
|
||
{id: 2, name: '에러'}
|
||
]),
|
||
|
||
// 에러 유형 로드
|
||
apiCall(`${API}/daily-work-reports/error-types`)
|
||
.then(data => Array.isArray(data) ? data : [
|
||
{id: 1, name: '설계미스'},
|
||
{id: 2, name: '외주작업 불량'},
|
||
{id: 3, name: '입고지연'},
|
||
{id: 4, name: '작업 불량'}
|
||
])
|
||
.catch(() => [
|
||
{id: 1, name: '설계미스'},
|
||
{id: 2, name: '외주작업 불량'},
|
||
{id: 3, name: '입고지연'},
|
||
{id: 4, name: '작업 불량'}
|
||
])
|
||
];
|
||
|
||
const [projects, workTypes, workStatusTypes, errorTypes] = await Promise.all(promises);
|
||
|
||
basicData = {
|
||
projects,
|
||
workTypes,
|
||
workStatusTypes,
|
||
errorTypes
|
||
};
|
||
|
||
console.log('✅ 기본 데이터 로드 완료 (통합 API):', basicData);
|
||
} catch (error) {
|
||
console.error('기본 데이터 로드 실패:', error);
|
||
}
|
||
}
|
||
|
||
// 이벤트 리스너 설정
|
||
function setupEventListeners() {
|
||
document.getElementById('prevMonth').addEventListener('click', () => {
|
||
currentDate.setMonth(currentDate.getMonth() - 1);
|
||
updateMonthDisplay();
|
||
selectedDate = null;
|
||
selectedDateData = null;
|
||
renderCalendar();
|
||
renderDayInfo();
|
||
});
|
||
|
||
document.getElementById('nextMonth').addEventListener('click', () => {
|
||
currentDate.setMonth(currentDate.getMonth() + 1);
|
||
updateMonthDisplay();
|
||
selectedDate = null;
|
||
selectedDateData = null;
|
||
renderCalendar();
|
||
renderDayInfo();
|
||
});
|
||
|
||
// 오늘 날짜로 이동 버튼 추가
|
||
document.getElementById('goToday')?.addEventListener('click', () => {
|
||
const today = new Date();
|
||
currentDate = new Date(today);
|
||
updateMonthDisplay();
|
||
renderCalendar();
|
||
|
||
// 오늘 날짜 자동 선택
|
||
const todayStr = formatDate(today);
|
||
selectedDate = todayStr;
|
||
loadDayData(todayStr);
|
||
});
|
||
}
|
||
|
||
// 전역 함수로 노출
|
||
window.toggleReview = toggleReview;
|
||
window.refreshCurrentDay = refreshCurrentDay;
|
||
window.editWorkItem = editWorkItem;
|
||
window.deleteWorkItem = deleteWorkItem;
|
||
window.deleteWorkerAllWorks = deleteWorkerAllWorks;
|
||
window.closeEditModal = closeEditModal;
|
||
window.saveEditedWork = saveEditedWork;
|
||
|
||
// 초기화
|
||
async function init() {
|
||
try {
|
||
const token = localStorage.getItem('token');
|
||
if (!token || token === 'undefined') {
|
||
showMessage('로그인이 필요합니다.', 'error');
|
||
setTimeout(() => {
|
||
window.location.href = '/';
|
||
}, 2000);
|
||
return;
|
||
}
|
||
|
||
updateMonthDisplay();
|
||
setupEventListeners();
|
||
renderCalendar();
|
||
renderDayInfo();
|
||
|
||
// 기본 데이터 미리 로드
|
||
await loadBasicData();
|
||
|
||
console.log('✅ 검토 페이지 초기화 완료 (통합 API 설정 적용)');
|
||
|
||
} catch (error) {
|
||
console.error('초기화 오류:', error);
|
||
showMessage('초기화 중 오류가 발생했습니다.', 'error');
|
||
}
|
||
}
|
||
|
||
// 페이지 로드 시 초기화
|
||
document.addEventListener('DOMContentLoaded', init); |