feat: 작업 관리 페이지에 공정 관리 기능 추가
## 추가 기능 - 공정 추가/수정/삭제 기능 구현 - 공정 관리 모달 UI 추가 - 공정 탭에 편집 버튼 추가 (✏️) ## UI 변경 - 상단에 "공정 추가" 버튼 추가 - 공정 모달: 공정명, 카테고리, 설명 입력 필드 - 각 공정 탭에 편집 아이콘 표시 ## JavaScript 함수 - openWorkTypeModal(): 공정 추가 모달 열기 - editWorkType(workTypeId): 공정 수정 모달 열기 - saveWorkType(): 공정 저장 (POST/PUT) - deleteWorkType(): 공정 삭제 (연결된 작업 확인) - closeWorkTypeModal(): 모달 닫기 ## 검증 로직 - 연결된 작업이 있는 공정은 삭제 불가 - 필수 필드(공정명) 검증 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -103,9 +103,15 @@ function renderWorkTypeTabs() {
|
|||||||
tabsHtml += `
|
tabsHtml += `
|
||||||
<button class="tab-btn ${isActive ? 'active' : ''}"
|
<button class="tab-btn ${isActive ? 'active' : ''}"
|
||||||
data-work-type="${workType.id}"
|
data-work-type="${workType.id}"
|
||||||
onclick="switchWorkType(${workType.id})">
|
onclick="switchWorkType(${workType.id})"
|
||||||
|
style="position: relative; padding-right: 3rem;">
|
||||||
<span class="tab-icon">🔧</span>
|
<span class="tab-icon">🔧</span>
|
||||||
${workType.name} (${count})
|
${workType.name} (${count})
|
||||||
|
<span onclick="event.stopPropagation(); editWorkType(${workType.id});"
|
||||||
|
style="position: absolute; right: 0.5rem; padding: 0.25rem 0.5rem; opacity: 0.7; cursor: pointer; font-size: 0.75rem;"
|
||||||
|
title="공정 수정">
|
||||||
|
✏️
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
@@ -375,3 +381,126 @@ function showToast(message, type = 'info') {
|
|||||||
setTimeout(() => toast.remove(), 300);
|
setTimeout(() => toast.remove(), 300);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 공정 관리 ====================
|
||||||
|
|
||||||
|
let currentEditingWorkType = null;
|
||||||
|
|
||||||
|
// 공정 모달 열기 (신규)
|
||||||
|
function openWorkTypeModal() {
|
||||||
|
currentEditingWorkType = null;
|
||||||
|
document.getElementById('workTypeModalTitle').textContent = '공정 추가';
|
||||||
|
document.getElementById('workTypeId').value = '';
|
||||||
|
document.getElementById('workTypeName').value = '';
|
||||||
|
document.getElementById('workTypeCategory').value = '';
|
||||||
|
document.getElementById('workTypeDescription').value = '';
|
||||||
|
document.getElementById('deleteWorkTypeBtn').style.display = 'none';
|
||||||
|
|
||||||
|
document.getElementById('workTypeModal').style.display = 'flex';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
window.openWorkTypeModal = openWorkTypeModal;
|
||||||
|
|
||||||
|
// 공정 수정 모달 열기
|
||||||
|
async function editWorkType(workTypeId) {
|
||||||
|
try {
|
||||||
|
const workType = workTypes.find(wt => wt.id === workTypeId);
|
||||||
|
if (!workType) {
|
||||||
|
showToast('공정 정보를 찾을 수 없습니다.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEditingWorkType = workType;
|
||||||
|
document.getElementById('workTypeModalTitle').textContent = '공정 수정';
|
||||||
|
document.getElementById('workTypeId').value = workType.id;
|
||||||
|
document.getElementById('workTypeName').value = workType.name || '';
|
||||||
|
document.getElementById('workTypeCategory').value = workType.category || '';
|
||||||
|
document.getElementById('workTypeDescription').value = workType.description || '';
|
||||||
|
document.getElementById('deleteWorkTypeBtn').style.display = 'block';
|
||||||
|
|
||||||
|
document.getElementById('workTypeModal').style.display = 'flex';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 공정 조회 오류:', error);
|
||||||
|
showToast('공정 정보를 불러올 수 없습니다.', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.editWorkType = editWorkType;
|
||||||
|
|
||||||
|
// 공정 모달 닫기
|
||||||
|
function closeWorkTypeModal() {
|
||||||
|
document.getElementById('workTypeModal').style.display = 'none';
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
currentEditingWorkType = null;
|
||||||
|
}
|
||||||
|
window.closeWorkTypeModal = closeWorkTypeModal;
|
||||||
|
|
||||||
|
// 공정 저장
|
||||||
|
async function saveWorkType() {
|
||||||
|
const workTypeId = document.getElementById('workTypeId').value;
|
||||||
|
const workTypeData = {
|
||||||
|
name: document.getElementById('workTypeName').value.trim(),
|
||||||
|
category: document.getElementById('workTypeCategory').value.trim() || null,
|
||||||
|
description: document.getElementById('workTypeDescription').value.trim() || null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!workTypeData.name) {
|
||||||
|
showToast('공정명을 입력해주세요.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response;
|
||||||
|
if (workTypeId) {
|
||||||
|
// 수정
|
||||||
|
response = await window.apiCall(`/daily-work-reports/work-types/${workTypeId}`, 'PUT', workTypeData);
|
||||||
|
} else {
|
||||||
|
// 신규
|
||||||
|
response = await window.apiCall('/daily-work-reports/work-types', 'POST', workTypeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response && response.success) {
|
||||||
|
showToast(workTypeId ? '공정이 수정되었습니다.' : '공정이 추가되었습니다.', 'success');
|
||||||
|
closeWorkTypeModal();
|
||||||
|
await loadAllData();
|
||||||
|
} else {
|
||||||
|
throw new Error(response.message || '저장에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 공정 저장 오류:', error);
|
||||||
|
showToast('공정 저장 중 오류가 발생했습니다.', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.saveWorkType = saveWorkType;
|
||||||
|
|
||||||
|
// 공정 삭제
|
||||||
|
async function deleteWorkType() {
|
||||||
|
if (!currentEditingWorkType) return;
|
||||||
|
|
||||||
|
// 이 공정에 속한 작업이 있는지 확인
|
||||||
|
const relatedTasks = tasks.filter(t => t.work_type_id === currentEditingWorkType.id);
|
||||||
|
if (relatedTasks.length > 0) {
|
||||||
|
showToast(`이 공정에 ${relatedTasks.length}개의 작업이 연결되어 있어 삭제할 수 없습니다.`, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm(`"${currentEditingWorkType.name}" 공정을 삭제하시겠습니까?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await window.apiCall(`/daily-work-reports/work-types/${currentEditingWorkType.id}`, 'DELETE');
|
||||||
|
|
||||||
|
if (response && response.success) {
|
||||||
|
showToast('공정이 삭제되었습니다.', 'success');
|
||||||
|
closeWorkTypeModal();
|
||||||
|
await loadAllData();
|
||||||
|
} else {
|
||||||
|
throw new Error(response.message || '삭제에 실패했습니다.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 공정 삭제 오류:', error);
|
||||||
|
showToast('공정 삭제 중 오류가 발생했습니다.', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.deleteWorkType = deleteWorkType;
|
||||||
|
|||||||
@@ -77,6 +77,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-actions">
|
<div class="page-actions">
|
||||||
|
<button class="btn btn-primary" onclick="openWorkTypeModal()">
|
||||||
|
<span class="btn-icon">🔧</span>
|
||||||
|
공정 추가
|
||||||
|
</button>
|
||||||
<button class="btn btn-primary" onclick="openTaskModal()">
|
<button class="btn btn-primary" onclick="openTaskModal()">
|
||||||
<span class="btn-icon">➕</span>
|
<span class="btn-icon">➕</span>
|
||||||
작업 추가
|
작업 추가
|
||||||
@@ -176,6 +180,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 공정 추가/수정 모달 -->
|
||||||
|
<div id="workTypeModal" class="modal-overlay" style="display: none;">
|
||||||
|
<div class="modal-container">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 id="workTypeModalTitle">공정 추가</h2>
|
||||||
|
<button class="modal-close-btn" onclick="closeWorkTypeModal()">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="workTypeForm" onsubmit="event.preventDefault(); saveWorkType();">
|
||||||
|
<input type="hidden" id="workTypeId">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">공정명 *</label>
|
||||||
|
<input type="text" id="workTypeName" class="form-control" placeholder="예: Base(구조물), Vessel(용기)" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">카테고리</label>
|
||||||
|
<input type="text" id="workTypeCategory" class="form-control" placeholder="예: 제작, 조립">
|
||||||
|
<small class="form-help">공정을 그룹화할 카테고리 (선택사항)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">설명</label>
|
||||||
|
<textarea id="workTypeDescription" class="form-control" rows="3" placeholder="공정에 대한 설명"></textarea>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="closeWorkTypeModal()">취소</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="deleteWorkTypeBtn" onclick="deleteWorkType()" style="display: none;">
|
||||||
|
🗑️ 삭제
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="saveWorkType()">
|
||||||
|
💾 저장
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user