- 일일순회점검 시스템 신규 구현 - DB 테이블: patrol_checklist_items, daily_patrol_sessions, patrol_check_records, workplace_items, item_types - API: /api/patrol/* 엔드포인트 - 프론트엔드: 지도 기반 작업장 점검 UI - 설비 관리 기능 개선 - 구매 관련 필드 추가 (구매일, 가격, 공급업체 등) - 설비 코드 자동 생성 (TKP-XXX 형식) - 작업장 관리 개선 - 레이아웃 이미지 업로드 기능 - 마커 위치 저장 기능 - 부서 관리 기능 추가 - 사이드바 네비게이션 카테고리 재구성 - 이미지 401 오류 수정 (정적 파일 경로 처리) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
767 lines
24 KiB
JavaScript
767 lines
24 KiB
JavaScript
// 코드 관리 페이지 JavaScript
|
||
|
||
// 전역 변수
|
||
let workStatusTypes = [];
|
||
let errorTypes = [];
|
||
let workTypes = [];
|
||
let currentCodeType = 'work-status';
|
||
let currentEditingCode = null;
|
||
|
||
// 페이지 초기화
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
console.log('🏷️ 코드 관리 페이지 초기화 시작');
|
||
|
||
initializePage();
|
||
loadAllCodes();
|
||
});
|
||
|
||
// 페이지 초기화
|
||
function initializePage() {
|
||
// 시간 업데이트 시작
|
||
updateCurrentTime();
|
||
setInterval(updateCurrentTime, 1000);
|
||
|
||
// 사용자 정보 업데이트
|
||
updateUserInfo();
|
||
|
||
// 프로필 메뉴 토글
|
||
setupProfileMenu();
|
||
|
||
// 로그아웃 버튼
|
||
setupLogoutButton();
|
||
}
|
||
|
||
// 현재 시간 업데이트
|
||
function updateCurrentTime() {
|
||
const now = new Date();
|
||
const timeString = now.toLocaleTimeString('ko-KR', {
|
||
hour12: false,
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
});
|
||
|
||
const timeElement = document.getElementById('timeValue');
|
||
if (timeElement) {
|
||
timeElement.textContent = timeString;
|
||
}
|
||
}
|
||
|
||
// 사용자 정보 업데이트
|
||
// navbar/sidebar는 app-init.js에서 공통 처리
|
||
function updateUserInfo() {
|
||
// app-init.js가 navbar 사용자 정보를 처리
|
||
}
|
||
|
||
// 프로필 메뉴 설정
|
||
function setupProfileMenu() {
|
||
const userProfile = document.getElementById('userProfile');
|
||
const profileMenu = document.getElementById('profileMenu');
|
||
|
||
if (userProfile && profileMenu) {
|
||
userProfile.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
const isVisible = profileMenu.style.display === 'block';
|
||
profileMenu.style.display = isVisible ? 'none' : 'block';
|
||
});
|
||
|
||
// 외부 클릭 시 메뉴 닫기
|
||
document.addEventListener('click', function() {
|
||
profileMenu.style.display = 'none';
|
||
});
|
||
}
|
||
}
|
||
|
||
// 로그아웃 버튼 설정
|
||
function setupLogoutButton() {
|
||
const logoutBtn = document.getElementById('logoutBtn');
|
||
if (logoutBtn) {
|
||
logoutBtn.addEventListener('click', function() {
|
||
if (confirm('로그아웃 하시겠습니까?')) {
|
||
localStorage.removeItem('token');
|
||
localStorage.removeItem('user');
|
||
localStorage.removeItem('userInfo');
|
||
window.location.href = '/index.html';
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 모든 코드 데이터 로드
|
||
async function loadAllCodes() {
|
||
try {
|
||
console.log('📊 모든 코드 데이터 로딩 시작');
|
||
|
||
await Promise.all([
|
||
loadWorkStatusTypes(),
|
||
// loadErrorTypes(), // 오류 유형은 신고 시스템으로 대체됨
|
||
loadWorkTypes()
|
||
]);
|
||
|
||
// 현재 활성 탭 렌더링
|
||
renderCurrentTab();
|
||
|
||
} catch (error) {
|
||
console.error('코드 데이터 로딩 오류:', error);
|
||
showToast('코드 데이터를 불러오는데 실패했습니다.', 'error');
|
||
}
|
||
}
|
||
|
||
// 작업 상태 유형 로드
|
||
async function loadWorkStatusTypes() {
|
||
try {
|
||
console.log('📊 작업 상태 유형 로딩...');
|
||
|
||
const response = await apiCall('/daily-work-reports/work-status-types', 'GET');
|
||
|
||
let statusData = [];
|
||
if (response && response.success && Array.isArray(response.data)) {
|
||
statusData = response.data;
|
||
} else if (Array.isArray(response)) {
|
||
statusData = response;
|
||
}
|
||
|
||
workStatusTypes = statusData;
|
||
console.log(`✅ 작업 상태 유형 ${workStatusTypes.length}개 로드 완료`);
|
||
|
||
} catch (error) {
|
||
console.error('작업 상태 유형 로딩 오류:', error);
|
||
workStatusTypes = [];
|
||
}
|
||
}
|
||
|
||
// 오류 유형 로드
|
||
async function loadErrorTypes() {
|
||
try {
|
||
console.log('⚠️ 오류 유형 로딩...');
|
||
|
||
const response = await apiCall('/daily-work-reports/error-types', 'GET');
|
||
|
||
let errorData = [];
|
||
if (response && response.success && Array.isArray(response.data)) {
|
||
errorData = response.data;
|
||
} else if (Array.isArray(response)) {
|
||
errorData = response;
|
||
}
|
||
|
||
errorTypes = errorData;
|
||
console.log(`✅ 오류 유형 ${errorTypes.length}개 로드 완료`);
|
||
|
||
} catch (error) {
|
||
console.error('오류 유형 로딩 오류:', error);
|
||
errorTypes = [];
|
||
}
|
||
}
|
||
|
||
// 작업 유형 로드
|
||
async function loadWorkTypes() {
|
||
try {
|
||
console.log('🔧 작업 유형 로딩...');
|
||
|
||
const response = await apiCall('/daily-work-reports/work-types', 'GET');
|
||
|
||
let typeData = [];
|
||
if (response && response.success && Array.isArray(response.data)) {
|
||
typeData = response.data;
|
||
} else if (Array.isArray(response)) {
|
||
typeData = response;
|
||
}
|
||
|
||
workTypes = typeData;
|
||
console.log(`✅ 작업 유형 ${workTypes.length}개 로드 완료`);
|
||
|
||
} catch (error) {
|
||
console.error('작업 유형 로딩 오류:', error);
|
||
workTypes = [];
|
||
}
|
||
}
|
||
|
||
// 코드 탭 전환
|
||
function switchCodeTab(tabName) {
|
||
// 탭 버튼 활성화 상태 변경
|
||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||
|
||
// 탭 콘텐츠 표시/숨김
|
||
document.querySelectorAll('.code-tab-content').forEach(content => {
|
||
content.classList.remove('active');
|
||
});
|
||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||
|
||
currentCodeType = tabName;
|
||
renderCurrentTab();
|
||
}
|
||
|
||
// 현재 탭 렌더링
|
||
function renderCurrentTab() {
|
||
switch (currentCodeType) {
|
||
case 'work-status':
|
||
renderWorkStatusTypes();
|
||
break;
|
||
// case 'error-types': // 오류 유형은 신고 시스템으로 대체됨
|
||
// renderErrorTypes();
|
||
// break;
|
||
case 'work-types':
|
||
renderWorkTypes();
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 작업 상태 유형 렌더링
|
||
function renderWorkStatusTypes() {
|
||
const grid = document.getElementById('workStatusGrid');
|
||
if (!grid) return;
|
||
|
||
if (workStatusTypes.length === 0) {
|
||
grid.innerHTML = `
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📊</div>
|
||
<h3>등록된 작업 상태 유형이 없습니다.</h3>
|
||
<p>"새 상태 추가" 버튼을 눌러 작업 상태를 등록해보세요.</p>
|
||
<button class="btn btn-primary" onclick="openCodeModal('work-status')">
|
||
➕ 첫 상태 추가하기
|
||
</button>
|
||
</div>
|
||
`;
|
||
updateWorkStatusStats();
|
||
return;
|
||
}
|
||
|
||
let gridHtml = '';
|
||
|
||
workStatusTypes.forEach(status => {
|
||
const isError = status.is_error === 1 || status.is_error === true;
|
||
const statusClass = isError ? 'error-status' : 'normal-status';
|
||
const statusIcon = isError ? '❌' : '✅';
|
||
const statusLabel = isError ? '오류' : '정상';
|
||
|
||
gridHtml += `
|
||
<div class="code-card ${statusClass}" onclick="editCode('work-status', ${status.id})">
|
||
<div class="code-header">
|
||
<div class="code-icon">${statusIcon}</div>
|
||
<div class="code-info">
|
||
<h3 class="code-name">${status.name}</h3>
|
||
<span class="code-label">${statusLabel}</span>
|
||
</div>
|
||
<div class="code-actions">
|
||
<button class="btn-small btn-edit" onclick="event.stopPropagation(); editCode('work-status', ${status.id})" title="수정">
|
||
✏️
|
||
</button>
|
||
<button class="btn-small btn-delete" onclick="event.stopPropagation(); confirmDeleteCode('work-status', ${status.id})" title="삭제">
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
</div>
|
||
${status.description ? `<p class="code-description">${status.description}</p>` : ''}
|
||
<div class="code-meta">
|
||
<span class="code-date">등록: ${formatDate(status.created_at)}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
grid.innerHTML = gridHtml;
|
||
updateWorkStatusStats();
|
||
}
|
||
|
||
// 오류 유형 렌더링
|
||
function renderErrorTypes() {
|
||
const grid = document.getElementById('errorTypesGrid');
|
||
if (!grid) return;
|
||
|
||
if (errorTypes.length === 0) {
|
||
grid.innerHTML = `
|
||
<div class="empty-state">
|
||
<div class="empty-icon">⚠️</div>
|
||
<h3>등록된 오류 유형이 없습니다.</h3>
|
||
<p>"새 오류 유형 추가" 버튼을 눌러 오류 유형을 등록해보세요.</p>
|
||
<button class="btn btn-primary" onclick="openCodeModal('error-types')">
|
||
➕ 첫 오류 유형 추가하기
|
||
</button>
|
||
</div>
|
||
`;
|
||
updateErrorTypesStats();
|
||
return;
|
||
}
|
||
|
||
let gridHtml = '';
|
||
|
||
errorTypes.forEach(error => {
|
||
const severityMap = {
|
||
'low': { icon: '🟢', label: '낮음', class: 'severity-low' },
|
||
'medium': { icon: '🟡', label: '보통', class: 'severity-medium' },
|
||
'high': { icon: '🟠', label: '높음', class: 'severity-high' },
|
||
'critical': { icon: '🔴', label: '심각', class: 'severity-critical' }
|
||
};
|
||
|
||
const severity = severityMap[error.severity] || severityMap.medium;
|
||
|
||
gridHtml += `
|
||
<div class="code-card error-type-card ${severity.class}" onclick="editCode('error-types', ${error.id})">
|
||
<div class="code-header">
|
||
<div class="code-icon">⚠️</div>
|
||
<div class="code-info">
|
||
<h3 class="code-name">${error.name}</h3>
|
||
<span class="code-label">${severity.icon} ${severity.label}</span>
|
||
</div>
|
||
<div class="code-actions">
|
||
<button class="btn-small btn-edit" onclick="event.stopPropagation(); editCode('error-types', ${error.id})" title="수정">
|
||
✏️
|
||
</button>
|
||
<button class="btn-small btn-delete" onclick="event.stopPropagation(); confirmDeleteCode('error-types', ${error.id})" title="삭제">
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
</div>
|
||
${error.description ? `<p class="code-description">${error.description}</p>` : ''}
|
||
${error.solution_guide ? `<div class="solution-guide"><strong>해결 가이드:</strong><br>${error.solution_guide}</div>` : ''}
|
||
<div class="code-meta">
|
||
<span class="code-date">등록: ${formatDate(error.created_at)}</span>
|
||
${error.updated_at !== error.created_at ? `<span class="code-date">수정: ${formatDate(error.updated_at)}</span>` : ''}
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
grid.innerHTML = gridHtml;
|
||
updateErrorTypesStats();
|
||
}
|
||
|
||
// 작업 유형 렌더링
|
||
function renderWorkTypes() {
|
||
const grid = document.getElementById('workTypesGrid');
|
||
if (!grid) return;
|
||
|
||
if (workTypes.length === 0) {
|
||
grid.innerHTML = `
|
||
<div class="empty-state">
|
||
<div class="empty-icon">🔧</div>
|
||
<h3>등록된 작업 유형이 없습니다.</h3>
|
||
<p>"새 작업 유형 추가" 버튼을 눌러 작업 유형을 등록해보세요.</p>
|
||
<button class="btn btn-primary" onclick="openCodeModal('work-types')">
|
||
➕ 첫 작업 유형 추가하기
|
||
</button>
|
||
</div>
|
||
`;
|
||
updateWorkTypesStats();
|
||
return;
|
||
}
|
||
|
||
let gridHtml = '';
|
||
|
||
workTypes.forEach(type => {
|
||
gridHtml += `
|
||
<div class="code-card work-type-card" onclick="editCode('work-types', ${type.id})">
|
||
<div class="code-header">
|
||
<div class="code-icon">🔧</div>
|
||
<div class="code-info">
|
||
<h3 class="code-name">${type.name}</h3>
|
||
${type.category ? `<span class="code-label">📁 ${type.category}</span>` : ''}
|
||
</div>
|
||
<div class="code-actions">
|
||
<button class="btn-small btn-edit" onclick="event.stopPropagation(); editCode('work-types', ${type.id})" title="수정">
|
||
✏️
|
||
</button>
|
||
<button class="btn-small btn-delete" onclick="event.stopPropagation(); confirmDeleteCode('work-types', ${type.id})" title="삭제">
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
</div>
|
||
${type.description ? `<p class="code-description">${type.description}</p>` : ''}
|
||
<div class="code-meta">
|
||
<span class="code-date">등록: ${formatDate(type.created_at)}</span>
|
||
${type.updated_at !== type.created_at ? `<span class="code-date">수정: ${formatDate(type.updated_at)}</span>` : ''}
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
grid.innerHTML = gridHtml;
|
||
updateWorkTypesStats();
|
||
}
|
||
|
||
// 작업 상태 통계 업데이트
|
||
function updateWorkStatusStats() {
|
||
const total = workStatusTypes.length;
|
||
const normal = workStatusTypes.filter(s => !s.is_error).length;
|
||
const error = workStatusTypes.filter(s => s.is_error).length;
|
||
|
||
document.getElementById('workStatusCount').textContent = total;
|
||
document.getElementById('normalStatusCount').textContent = normal;
|
||
document.getElementById('errorStatusCount').textContent = error;
|
||
}
|
||
|
||
// 오류 유형 통계 업데이트
|
||
function updateErrorTypesStats() {
|
||
const total = errorTypes.length;
|
||
const critical = errorTypes.filter(e => e.severity === 'critical').length;
|
||
const high = errorTypes.filter(e => e.severity === 'high').length;
|
||
const medium = errorTypes.filter(e => e.severity === 'medium').length;
|
||
const low = errorTypes.filter(e => e.severity === 'low').length;
|
||
|
||
document.getElementById('errorTypesCount').textContent = total;
|
||
document.getElementById('criticalErrorsCount').textContent = critical;
|
||
document.getElementById('highErrorsCount').textContent = high;
|
||
document.getElementById('mediumErrorsCount').textContent = medium;
|
||
document.getElementById('lowErrorsCount').textContent = low;
|
||
}
|
||
|
||
// 작업 유형 통계 업데이트
|
||
function updateWorkTypesStats() {
|
||
const total = workTypes.length;
|
||
const categories = new Set(workTypes.map(t => t.category).filter(Boolean)).size;
|
||
|
||
document.getElementById('workTypesCount').textContent = total;
|
||
document.getElementById('workCategoriesCount').textContent = categories;
|
||
}
|
||
|
||
// 코드 모달 열기
|
||
function openCodeModal(codeType, codeData = null) {
|
||
const modal = document.getElementById('codeModal');
|
||
const modalTitle = document.getElementById('modalTitle');
|
||
const deleteBtn = document.getElementById('deleteCodeBtn');
|
||
|
||
if (!modal) return;
|
||
|
||
currentEditingCode = codeData;
|
||
|
||
// 모든 전용 필드 숨기기
|
||
document.getElementById('isErrorGroup').style.display = 'none';
|
||
document.getElementById('severityGroup').style.display = 'none';
|
||
document.getElementById('solutionGuideGroup').style.display = 'none';
|
||
document.getElementById('categoryGroup').style.display = 'none';
|
||
|
||
// 코드 유형별 설정
|
||
switch (codeType) {
|
||
case 'work-status':
|
||
modalTitle.textContent = codeData ? '작업 상태 수정' : '새 작업 상태 추가';
|
||
document.getElementById('isErrorGroup').style.display = 'block';
|
||
break;
|
||
case 'error-types':
|
||
modalTitle.textContent = codeData ? '오류 유형 수정' : '새 오류 유형 추가';
|
||
document.getElementById('severityGroup').style.display = 'block';
|
||
document.getElementById('solutionGuideGroup').style.display = 'block';
|
||
break;
|
||
case 'work-types':
|
||
modalTitle.textContent = codeData ? '작업 유형 수정' : '새 작업 유형 추가';
|
||
document.getElementById('categoryGroup').style.display = 'block';
|
||
updateCategoryList();
|
||
break;
|
||
}
|
||
|
||
document.getElementById('codeType').value = codeType;
|
||
|
||
if (codeData) {
|
||
// 수정 모드
|
||
deleteBtn.style.display = 'inline-flex';
|
||
|
||
// 폼에 데이터 채우기
|
||
document.getElementById('codeId').value = codeData.id;
|
||
document.getElementById('codeName').value = codeData.name || '';
|
||
document.getElementById('codeDescription').value = codeData.description || '';
|
||
|
||
// 코드 유형별 필드 채우기
|
||
if (codeType === 'work-status') {
|
||
document.getElementById('isError').checked = codeData.is_error === 1 || codeData.is_error === true;
|
||
} else if (codeType === 'error-types') {
|
||
document.getElementById('severity').value = codeData.severity || 'medium';
|
||
document.getElementById('solutionGuide').value = codeData.solution_guide || '';
|
||
} else if (codeType === 'work-types') {
|
||
document.getElementById('category').value = codeData.category || '';
|
||
}
|
||
} else {
|
||
// 신규 등록 모드
|
||
deleteBtn.style.display = 'none';
|
||
|
||
// 폼 초기화
|
||
document.getElementById('codeForm').reset();
|
||
document.getElementById('codeId').value = '';
|
||
document.getElementById('codeType').value = codeType;
|
||
}
|
||
|
||
modal.style.display = 'flex';
|
||
document.body.style.overflow = 'hidden';
|
||
|
||
// 첫 번째 입력 필드에 포커스
|
||
setTimeout(() => {
|
||
document.getElementById('codeName').focus();
|
||
}, 100);
|
||
}
|
||
|
||
// 카테고리 목록 업데이트
|
||
function updateCategoryList() {
|
||
const categoryList = document.getElementById('categoryList');
|
||
if (categoryList) {
|
||
const categories = [...new Set(workTypes.map(t => t.category).filter(Boolean))].sort();
|
||
categoryList.innerHTML = categories.map(cat => `<option value="${cat}">`).join('');
|
||
}
|
||
}
|
||
|
||
// 코드 모달 닫기
|
||
function closeCodeModal() {
|
||
const modal = document.getElementById('codeModal');
|
||
if (modal) {
|
||
modal.style.display = 'none';
|
||
document.body.style.overflow = '';
|
||
currentEditingCode = null;
|
||
}
|
||
}
|
||
|
||
// 코드 편집
|
||
function editCode(codeType, codeId) {
|
||
let codeData = null;
|
||
|
||
switch (codeType) {
|
||
case 'work-status':
|
||
codeData = workStatusTypes.find(s => s.id === codeId);
|
||
break;
|
||
case 'error-types':
|
||
codeData = errorTypes.find(e => e.id === codeId);
|
||
break;
|
||
case 'work-types':
|
||
codeData = workTypes.find(t => t.id === codeId);
|
||
break;
|
||
}
|
||
|
||
if (codeData) {
|
||
openCodeModal(codeType, codeData);
|
||
} else {
|
||
showToast('코드를 찾을 수 없습니다.', 'error');
|
||
}
|
||
}
|
||
|
||
// 코드 저장
|
||
async function saveCode() {
|
||
try {
|
||
const codeType = document.getElementById('codeType').value;
|
||
const codeId = document.getElementById('codeId').value;
|
||
|
||
const codeData = {
|
||
name: document.getElementById('codeName').value.trim(),
|
||
description: document.getElementById('codeDescription').value.trim() || null
|
||
};
|
||
|
||
// 필수 필드 검증
|
||
if (!codeData.name) {
|
||
showToast('이름은 필수 입력 항목입니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
// 코드 유형별 추가 필드
|
||
if (codeType === 'work-status') {
|
||
codeData.is_error = document.getElementById('isError').checked ? 1 : 0;
|
||
} else if (codeType === 'error-types') {
|
||
codeData.severity = document.getElementById('severity').value;
|
||
codeData.solution_guide = document.getElementById('solutionGuide').value.trim() || null;
|
||
} else if (codeType === 'work-types') {
|
||
codeData.category = document.getElementById('category').value.trim() || null;
|
||
}
|
||
|
||
console.log('💾 저장할 코드 데이터:', codeData);
|
||
|
||
let endpoint = '';
|
||
switch (codeType) {
|
||
case 'work-status':
|
||
endpoint = '/daily-work-reports/work-status-types';
|
||
break;
|
||
case 'error-types':
|
||
endpoint = '/daily-work-reports/error-types';
|
||
break;
|
||
case 'work-types':
|
||
endpoint = '/daily-work-reports/work-types';
|
||
break;
|
||
}
|
||
|
||
let response;
|
||
if (codeId) {
|
||
// 수정
|
||
response = await apiCall(`${endpoint}/${codeId}`, 'PUT', codeData);
|
||
} else {
|
||
// 신규 등록
|
||
response = await apiCall(endpoint, 'POST', codeData);
|
||
}
|
||
|
||
if (response && (response.success || response.id)) {
|
||
const action = codeId ? '수정' : '등록';
|
||
showToast(`코드가 성공적으로 ${action}되었습니다.`, 'success');
|
||
|
||
closeCodeModal();
|
||
await loadAllCodes();
|
||
} else {
|
||
throw new Error(response?.message || '저장에 실패했습니다.');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('코드 저장 오류:', error);
|
||
showToast(error.message || '코드 저장 중 오류가 발생했습니다.', 'error');
|
||
}
|
||
}
|
||
|
||
// 코드 삭제 확인
|
||
function confirmDeleteCode(codeType, codeId) {
|
||
let codeData = null;
|
||
let typeName = '';
|
||
|
||
switch (codeType) {
|
||
case 'work-status':
|
||
codeData = workStatusTypes.find(s => s.id === codeId);
|
||
typeName = '작업 상태';
|
||
break;
|
||
case 'error-types':
|
||
codeData = errorTypes.find(e => e.id === codeId);
|
||
typeName = '오류 유형';
|
||
break;
|
||
case 'work-types':
|
||
codeData = workTypes.find(t => t.id === codeId);
|
||
typeName = '작업 유형';
|
||
break;
|
||
}
|
||
|
||
if (!codeData) {
|
||
showToast('코드를 찾을 수 없습니다.', 'error');
|
||
return;
|
||
}
|
||
|
||
if (confirm(`"${codeData.name}" ${typeName}을 정말 삭제하시겠습니까?\n\n⚠️ 삭제된 코드는 복구할 수 없습니다.`)) {
|
||
deleteCodeById(codeType, codeId);
|
||
}
|
||
}
|
||
|
||
// 코드 삭제 (수정 모드에서)
|
||
function deleteCode() {
|
||
if (currentEditingCode) {
|
||
const codeType = document.getElementById('codeType').value;
|
||
confirmDeleteCode(codeType, currentEditingCode.id);
|
||
}
|
||
}
|
||
|
||
// 코드 삭제 실행
|
||
async function deleteCodeById(codeType, codeId) {
|
||
try {
|
||
let endpoint = '';
|
||
switch (codeType) {
|
||
case 'work-status':
|
||
endpoint = '/daily-work-reports/work-status-types';
|
||
break;
|
||
case 'error-types':
|
||
endpoint = '/daily-work-reports/error-types';
|
||
break;
|
||
case 'work-types':
|
||
endpoint = '/daily-work-reports/work-types';
|
||
break;
|
||
}
|
||
|
||
const response = await apiCall(`${endpoint}/${codeId}`, 'DELETE');
|
||
|
||
if (response && response.success) {
|
||
showToast('코드가 성공적으로 삭제되었습니다.', 'success');
|
||
|
||
closeCodeModal();
|
||
await loadAllCodes();
|
||
} else {
|
||
throw new Error(response?.message || '삭제에 실패했습니다.');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('코드 삭제 오류:', error);
|
||
showToast(error.message || '코드 삭제 중 오류가 발생했습니다.', 'error');
|
||
}
|
||
}
|
||
|
||
// 전체 새로고침
|
||
async function refreshAllCodes() {
|
||
const refreshBtn = document.querySelector('.btn-secondary');
|
||
if (refreshBtn) {
|
||
const originalText = refreshBtn.innerHTML;
|
||
refreshBtn.innerHTML = '<span class="btn-icon">⏳</span>새로고침 중...';
|
||
refreshBtn.disabled = true;
|
||
|
||
await loadAllCodes();
|
||
|
||
refreshBtn.innerHTML = originalText;
|
||
refreshBtn.disabled = false;
|
||
} else {
|
||
await loadAllCodes();
|
||
}
|
||
|
||
showToast('모든 코드 데이터가 새로고침되었습니다.', 'success');
|
||
}
|
||
|
||
// 날짜 포맷팅
|
||
function formatDate(dateString) {
|
||
if (!dateString) return '';
|
||
|
||
const date = new Date(dateString);
|
||
return date.toLocaleDateString('ko-KR', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit'
|
||
});
|
||
}
|
||
|
||
// 토스트 메시지 표시
|
||
function showToast(message, type = 'info') {
|
||
// 기존 토스트 제거
|
||
const existingToast = document.querySelector('.toast');
|
||
if (existingToast) {
|
||
existingToast.remove();
|
||
}
|
||
|
||
// 새 토스트 생성
|
||
const toast = document.createElement('div');
|
||
toast.className = `toast toast-${type}`;
|
||
toast.textContent = message;
|
||
|
||
// 스타일 적용
|
||
Object.assign(toast.style, {
|
||
position: 'fixed',
|
||
top: '20px',
|
||
right: '20px',
|
||
padding: '12px 24px',
|
||
borderRadius: '8px',
|
||
color: 'white',
|
||
fontWeight: '500',
|
||
zIndex: '1000',
|
||
transform: 'translateX(100%)',
|
||
transition: 'transform 0.3s ease'
|
||
});
|
||
|
||
// 타입별 배경색
|
||
const colors = {
|
||
success: '#10b981',
|
||
error: '#ef4444',
|
||
warning: '#f59e0b',
|
||
info: '#3b82f6'
|
||
};
|
||
toast.style.backgroundColor = colors[type] || colors.info;
|
||
|
||
document.body.appendChild(toast);
|
||
|
||
// 애니메이션
|
||
setTimeout(() => {
|
||
toast.style.transform = 'translateX(0)';
|
||
}, 100);
|
||
|
||
// 자동 제거
|
||
setTimeout(() => {
|
||
toast.style.transform = 'translateX(100%)';
|
||
setTimeout(() => {
|
||
if (toast.parentNode) {
|
||
toast.remove();
|
||
}
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// 전역 함수로 노출
|
||
window.switchCodeTab = switchCodeTab;
|
||
window.openCodeModal = openCodeModal;
|
||
window.closeCodeModal = closeCodeModal;
|
||
window.editCode = editCode;
|
||
window.saveCode = saveCode;
|
||
window.deleteCode = deleteCode;
|
||
window.confirmDeleteCode = confirmDeleteCode;
|
||
window.refreshAllCodes = refreshAllCodes;
|