feat: 완료 사진 HEIC 지원 및 관리함 수정 기능 개선
✨ 새로운 기능: - iPhone HEIC 사진 업로드 지원 (pillow-heif 라이브러리 추가) - 완료 사진 업로드/교체 기능 - 완료 코멘트 수정 기능 - 통합 이슈 수정 모달 (진행 중/완료 대기 공통) 🔧 기술적 개선: - HEIC 파일 자동 감지 및 원본 저장 - Base64 이미지 처리 로직 강화 - 상세한 디버깅 로그 추가 - 프론트엔드 파일 정보 로깅 📝 문서화: - 배포 가이드 (DEPLOYMENT_GUIDE_20251026.md) 추가 - DB 변경사항 로그 업데이트 - 마이그레이션 스크립트 (020_add_management_completion_fields.sql) 🐛 버그 수정: - loadManagementData -> initializeManagement 함수명 통일 - 모달 저장 후 즉시 닫히는 문제 해결 - 422 Unprocessable Entity 오류 해결
This commit is contained in:
@@ -789,9 +789,6 @@
|
||||
<button onclick="rejectCompletion(${issue.id})" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
|
||||
<i class="fas fa-times mr-1"></i>반려
|
||||
</button>
|
||||
<button onclick="openIssueEditModal(${issue.id})" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||
<i class="fas fa-edit mr-1"></i>수정
|
||||
</button>
|
||||
<button onclick="confirmCompletion(${issue.id})" class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
|
||||
<i class="fas fa-check-circle mr-1"></i>최종확인
|
||||
</button>
|
||||
@@ -800,10 +797,7 @@
|
||||
<button onclick="saveIssueChanges(${issue.id})" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||
<i class="fas fa-save mr-1"></i>저장
|
||||
</button>
|
||||
<button onclick="openIssueEditModal(${issue.id})" class="px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors">
|
||||
<i class="fas fa-eye mr-1"></i>확인
|
||||
</button>
|
||||
<button onclick="completeIssue(${issue.id})" class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
|
||||
<button onclick="confirmCompletion(${issue.id})" class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
|
||||
<i class="fas fa-check mr-1"></i>완료처리
|
||||
</button>
|
||||
`}
|
||||
@@ -1748,8 +1742,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 완료 확인 모달 열기 (진행 중 -> 완료 처리용)
|
||||
function openCompletionConfirmModal(issueId) {
|
||||
openIssueEditModal(issueId, true); // 완료 처리 모드로 열기
|
||||
}
|
||||
|
||||
// 이슈 수정 모달 열기 (모든 진행 중 상태에서 사용)
|
||||
function openIssueEditModal(issueId) {
|
||||
function openIssueEditModal(issueId, isCompletionMode = false) {
|
||||
const issue = issues.find(i => i.id === issueId);
|
||||
if (!issue) return;
|
||||
|
||||
@@ -1793,7 +1792,14 @@
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">원인분류</label>
|
||||
<input type="text" value="${getCategoryText(issue.final_category || issue.category)}" class="w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-lg text-sm" readonly>
|
||||
<select id="edit-category-${issue.id}" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm">
|
||||
<option value="material_missing" ${(issue.final_category || issue.category) === 'material_missing' ? 'selected' : ''}>자재 누락</option>
|
||||
<option value="process_error" ${(issue.final_category || issue.category) === 'process_error' ? 'selected' : ''}>공정 오류</option>
|
||||
<option value="quality_defect" ${(issue.final_category || issue.category) === 'quality_defect' ? 'selected' : ''}>품질 결함</option>
|
||||
<option value="design_issue" ${(issue.final_category || issue.category) === 'design_issue' ? 'selected' : ''}>설계 문제</option>
|
||||
<option value="safety_violation" ${(issue.final_category || issue.category) === 'safety_violation' ? 'selected' : ''}>안전 위반</option>
|
||||
<option value="etc" ${(issue.final_category || issue.category) === 'etc' ? 'selected' : ''}>기타</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1834,32 +1840,66 @@
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">조치예상일</label>
|
||||
<input type="date" id="edit-date-${issue.id}" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 text-sm" value="${issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : ''}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">원인부서</label>
|
||||
<select id="edit-cause-department-${issue.id}" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 text-sm">
|
||||
${getDepartmentOptions().map(opt =>
|
||||
`<option value="${opt.value}" ${opt.value === (issue.cause_department || '') ? 'selected' : ''}>${opt.text}</option>`
|
||||
).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">관리 코멘트</label>
|
||||
<textarea id="edit-management-comment-${issue.id}" rows="2" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 text-sm resize-none" placeholder="관리 코멘트를 입력하세요...">${issue.management_comment || ''}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${isPendingCompletion ? `
|
||||
<div class="bg-purple-50 p-4 rounded-lg">
|
||||
<h4 class="font-semibold text-purple-800 mb-3">완료 신청 정보</h4>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">완료 사진</label>
|
||||
<!-- 완료 신청 정보 (진행 중, 완료 대기 둘 다 표시) -->
|
||||
<div class="bg-purple-50 p-4 rounded-lg">
|
||||
<h4 class="font-semibold text-purple-800 mb-3">완료 신청 정보</h4>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">완료 사진</label>
|
||||
<div class="space-y-3">
|
||||
${issue.completion_photo_path ? `
|
||||
<div class="mt-1">
|
||||
<img src="${issue.completion_photo_path}" class="w-32 h-32 object-cover rounded-lg cursor-pointer border-2 border-purple-200" onclick="openPhotoModal('${issue.completion_photo_path}')" alt="완료 사진">
|
||||
<div class="flex items-center space-x-3">
|
||||
<img src="${issue.completion_photo_path}" class="w-24 h-24 object-cover rounded-lg cursor-pointer border-2 border-purple-200 hover:border-purple-400 transition-colors" onclick="openPhotoModal('${issue.completion_photo_path}')" alt="현재 완료 사진">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-600 mb-1">현재 완료 사진</p>
|
||||
<p class="text-xs text-gray-500">클릭하면 크게 볼 수 있습니다</p>
|
||||
</div>
|
||||
</div>
|
||||
` : '<p class="text-sm text-gray-500 mt-1">완료 사진 없음</p>'}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">완료 코멘트</label>
|
||||
<p class="text-sm text-gray-700 p-2 bg-white rounded border">${issue.completion_comment || '코멘트 없음'}</p>
|
||||
` : `
|
||||
<div class="flex items-center justify-center w-24 h-24 bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-camera text-gray-400 text-lg mb-1"></i>
|
||||
<p class="text-xs text-gray-500">사진 없음</p>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="file" id="edit-completion-photo-${issue.id}" accept="image/*" class="hidden">
|
||||
<button type="button" onclick="document.getElementById('edit-completion-photo-${issue.id}').click()" class="flex items-center px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors text-sm">
|
||||
<i class="fas fa-upload mr-2"></i>
|
||||
${issue.completion_photo_path ? '사진 교체' : '사진 업로드'}
|
||||
</button>
|
||||
<span id="photo-filename-${issue.id}" class="text-sm text-gray-600"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">완료 코멘트</label>
|
||||
<textarea id="edit-completion-comment-${issue.id}" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm resize-none" placeholder="완료 코멘트를 입력하세요...">${issue.completion_comment || ''}</textarea>
|
||||
</div>
|
||||
${isPendingCompletion ? `
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">신청일시</label>
|
||||
<p class="text-sm text-gray-700">${new Date(issue.completion_requested_at).toLocaleString('ko-KR')}</p>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1871,11 +1911,9 @@
|
||||
<button onclick="saveIssueFromModal(${issue.id})" class="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||
<i class="fas fa-save mr-2"></i>저장
|
||||
</button>
|
||||
${isPendingCompletion ? `
|
||||
<button onclick="finalConfirmCompletion(${issue.id})" class="px-6 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
|
||||
<i class="fas fa-check-circle mr-2"></i>최종 확인
|
||||
</button>
|
||||
` : ''}
|
||||
<button onclick="saveAndCompleteIssue(${issue.id})" class="px-6 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
|
||||
<i class="fas fa-check-circle mr-2"></i>최종확인
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1884,6 +1922,22 @@
|
||||
|
||||
// 모달을 body에 추가
|
||||
document.body.insertAdjacentHTML('beforeend', modalContent);
|
||||
|
||||
// 파일 선택 이벤트 리스너 추가
|
||||
const fileInput = document.getElementById(`edit-completion-photo-${issue.id}`);
|
||||
const filenameSpan = document.getElementById(`photo-filename-${issue.id}`);
|
||||
|
||||
if (fileInput && filenameSpan) {
|
||||
fileInput.addEventListener('change', function(e) {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
filenameSpan.textContent = e.target.files[0].name;
|
||||
filenameSpan.className = 'text-sm text-green-600 font-medium';
|
||||
} else {
|
||||
filenameSpan.textContent = '';
|
||||
filenameSpan.className = 'text-sm text-gray-600';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 이슈 수정 모달 닫기
|
||||
@@ -1894,14 +1948,62 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 파일을 Base64로 변환하는 함수
|
||||
function fileToBase64(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
// 모달에서 이슈 저장
|
||||
async function saveIssueFromModal(issueId) {
|
||||
const title = document.getElementById(`edit-issue-title-${issueId}`).value.trim();
|
||||
const detail = document.getElementById(`edit-issue-detail-${issueId}`).value.trim();
|
||||
const category = document.getElementById(`edit-category-${issueId}`).value;
|
||||
const solution = document.getElementById(`edit-solution-${issueId}`).value.trim();
|
||||
const department = document.getElementById(`edit-department-${issueId}`).value;
|
||||
const person = document.getElementById(`edit-person-${issueId}`).value.trim();
|
||||
const date = document.getElementById(`edit-date-${issueId}`).value;
|
||||
const causeDepartment = document.getElementById(`edit-cause-department-${issueId}`).value;
|
||||
const managementComment = document.getElementById(`edit-management-comment-${issueId}`).value.trim();
|
||||
|
||||
// 완료 신청 정보 (완료 대기 상태일 때만)
|
||||
const completionCommentElement = document.getElementById(`edit-completion-comment-${issueId}`);
|
||||
const completionPhotoElement = document.getElementById(`edit-completion-photo-${issueId}`);
|
||||
|
||||
let completionComment = null;
|
||||
let completionPhoto = null;
|
||||
|
||||
if (completionCommentElement) {
|
||||
completionComment = completionCommentElement.value.trim();
|
||||
}
|
||||
|
||||
if (completionPhotoElement && completionPhotoElement.files[0]) {
|
||||
try {
|
||||
const file = completionPhotoElement.files[0];
|
||||
console.log('🔍 업로드할 파일 정보:', {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
lastModified: file.lastModified
|
||||
});
|
||||
|
||||
const base64 = await fileToBase64(file);
|
||||
console.log('🔍 Base64 변환 완료 - 전체 길이:', base64.length);
|
||||
console.log('🔍 Base64 헤더:', base64.substring(0, 50));
|
||||
|
||||
completionPhoto = base64.split(',')[1]; // Base64 데이터만 추출
|
||||
console.log('🔍 헤더 제거 후 길이:', completionPhoto.length);
|
||||
console.log('🔍 전송할 Base64 시작 부분:', completionPhoto.substring(0, 50));
|
||||
} catch (error) {
|
||||
console.error('파일 변환 오류:', error);
|
||||
alert('완료 사진 업로드 중 오류가 발생했습니다.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
alert('부적합명을 입력해주세요.');
|
||||
@@ -1909,6 +2011,25 @@
|
||||
}
|
||||
|
||||
const combinedDescription = title + (detail ? '\n' + detail : '');
|
||||
|
||||
const requestBody = {
|
||||
final_description: combinedDescription,
|
||||
final_category: category,
|
||||
solution: solution || null,
|
||||
responsible_department: department || null,
|
||||
responsible_person: person || null,
|
||||
expected_completion_date: date || null,
|
||||
cause_department: causeDepartment || null,
|
||||
management_comment: managementComment || null
|
||||
};
|
||||
|
||||
// 완료 신청 정보가 있으면 추가
|
||||
if (completionComment !== null) {
|
||||
requestBody.completion_comment = completionComment || null;
|
||||
}
|
||||
if (completionPhoto !== null) {
|
||||
requestBody.completion_photo = completionPhoto;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/management/${issueId}`, {
|
||||
@@ -1917,19 +2038,59 @@
|
||||
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
final_description: combinedDescription,
|
||||
solution: solution || null,
|
||||
responsible_department: department || null,
|
||||
responsible_person: person || null,
|
||||
expected_completion_date: date || null
|
||||
})
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('이슈가 성공적으로 저장되었습니다.');
|
||||
closeIssueEditModal();
|
||||
loadManagementData(); // 페이지 새로고침
|
||||
// 저장 성공 후 데이터 새로고침하고 모달은 유지
|
||||
await initializeManagement(); // 페이지 새로고침
|
||||
|
||||
// 저장된 이슈 정보 다시 로드하여 모달 업데이트
|
||||
const updatedIssue = issues.find(i => i.id === issueId);
|
||||
if (updatedIssue) {
|
||||
// 완료 사진이 저장되었는지 확인
|
||||
if (updatedIssue.completion_photo_path) {
|
||||
alert('✅ 완료 사진이 성공적으로 저장되었습니다!');
|
||||
} else {
|
||||
alert('⚠️ 저장은 완료되었지만 완료 사진 저장에 실패했습니다. 다시 시도해주세요.');
|
||||
}
|
||||
|
||||
// 모달 내용 업데이트 (완료 사진 표시 갱신)
|
||||
const photoContainer = document.querySelector(`#issueEditModal img[alt*="완료 사진"]`)?.parentElement;
|
||||
if (photoContainer && updatedIssue.completion_photo_path) {
|
||||
// HEIC 파일인지 확인
|
||||
const isHeic = updatedIssue.completion_photo_path.toLowerCase().endsWith('.heic');
|
||||
|
||||
if (isHeic) {
|
||||
// HEIC 파일은 다운로드 링크로 표시
|
||||
photoContainer.innerHTML = `
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-24 h-24 bg-purple-100 rounded-lg flex items-center justify-center border-2 border-purple-200">
|
||||
<i class="fas fa-image text-purple-500 text-2xl"></i>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-600 mb-1">완료 사진 (HEIC)</p>
|
||||
<a href="${updatedIssue.completion_photo_path}" download class="text-xs text-blue-500 hover:text-blue-700 underline">다운로드하여 확인</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
// 일반 이미지는 미리보기 표시
|
||||
photoContainer.innerHTML = `
|
||||
<div class="flex items-center space-x-3">
|
||||
<img src="${updatedIssue.completion_photo_path}" class="w-24 h-24 object-cover rounded-lg cursor-pointer border-2 border-purple-200 hover:border-purple-400 transition-colors" onclick="openPhotoModal('${updatedIssue.completion_photo_path}')" alt="현재 완료 사진">
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-600 mb-1">현재 완료 사진</p>
|
||||
<p class="text-xs text-gray-500">클릭하면 크게 볼 수 있습니다</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert('이슈가 성공적으로 저장되었습니다.');
|
||||
closeIssueEditModal();
|
||||
}
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`저장 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
@@ -1958,8 +2119,8 @@
|
||||
}
|
||||
|
||||
function confirmCompletion(issueId) {
|
||||
// 모든 정보 확인 모달 열기
|
||||
openCompletionConfirmModal(issueId);
|
||||
// 완료 확인 모달 열기 (수정 가능) - 통합 모달 사용
|
||||
openIssueEditModal(issueId, true);
|
||||
}
|
||||
|
||||
// 완료 신청 초기화 (수정 모드로 전환)
|
||||
@@ -1975,7 +2136,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert('완료 대기 상태가 해제되었습니다. 수정이 가능합니다.');
|
||||
loadManagementData(); // 페이지 새로고침
|
||||
initializeManagement(); // 페이지 새로고침
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`상태 변경 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
@@ -2002,7 +2163,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert('완료 신청이 반려되었습니다.');
|
||||
loadManagementData(); // 페이지 새로고침
|
||||
initializeManagement(); // 페이지 새로고침
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`반려 처리 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
@@ -2121,7 +2282,110 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 완료 확인
|
||||
// 저장 후 완료 처리 (최종확인)
|
||||
async function saveAndCompleteIssue(issueId) {
|
||||
if (!confirm('수정 내용을 저장하고 이 부적합을 최종 완료 처리하시겠습니까?\n완료 처리 후에는 수정할 수 없습니다.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const title = document.getElementById(`edit-issue-title-${issueId}`).value.trim();
|
||||
const detail = document.getElementById(`edit-issue-detail-${issueId}`).value.trim();
|
||||
const category = document.getElementById(`edit-category-${issueId}`).value;
|
||||
const solution = document.getElementById(`edit-solution-${issueId}`).value.trim();
|
||||
const department = document.getElementById(`edit-department-${issueId}`).value;
|
||||
const person = document.getElementById(`edit-person-${issueId}`).value.trim();
|
||||
const date = document.getElementById(`edit-date-${issueId}`).value;
|
||||
const causeDepartment = document.getElementById(`edit-cause-department-${issueId}`).value;
|
||||
const managementComment = document.getElementById(`edit-management-comment-${issueId}`).value.trim();
|
||||
|
||||
// 완료 신청 정보 (완료 대기 상태일 때만)
|
||||
const completionCommentElement = document.getElementById(`edit-completion-comment-${issueId}`);
|
||||
const completionPhotoElement = document.getElementById(`edit-completion-photo-${issueId}`);
|
||||
|
||||
let completionComment = null;
|
||||
let completionPhoto = null;
|
||||
|
||||
if (completionCommentElement) {
|
||||
completionComment = completionCommentElement.value.trim();
|
||||
}
|
||||
|
||||
if (completionPhotoElement && completionPhotoElement.files[0]) {
|
||||
try {
|
||||
const file = completionPhotoElement.files[0];
|
||||
console.log('🔍 업로드할 파일 정보:', {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
lastModified: file.lastModified
|
||||
});
|
||||
|
||||
const base64 = await fileToBase64(file);
|
||||
console.log('🔍 Base64 변환 완료 - 전체 길이:', base64.length);
|
||||
console.log('🔍 Base64 헤더:', base64.substring(0, 50));
|
||||
|
||||
completionPhoto = base64.split(',')[1]; // Base64 데이터만 추출
|
||||
console.log('🔍 헤더 제거 후 길이:', completionPhoto.length);
|
||||
console.log('🔍 전송할 Base64 시작 부분:', completionPhoto.substring(0, 50));
|
||||
} catch (error) {
|
||||
console.error('파일 변환 오류:', error);
|
||||
alert('완료 사진 업로드 중 오류가 발생했습니다.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!title) {
|
||||
alert('부적합명을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const combinedDescription = title + (detail ? '\n' + detail : '');
|
||||
|
||||
const requestBody = {
|
||||
final_description: combinedDescription,
|
||||
final_category: category,
|
||||
solution: solution || null,
|
||||
responsible_department: department || null,
|
||||
responsible_person: person || null,
|
||||
expected_completion_date: date || null,
|
||||
cause_department: causeDepartment || null,
|
||||
management_comment: managementComment || null,
|
||||
review_status: 'completed' // 완료 상태로 변경
|
||||
};
|
||||
|
||||
// 완료 신청 정보가 있으면 추가
|
||||
if (completionComment !== null) {
|
||||
requestBody.completion_comment = completionComment || null;
|
||||
}
|
||||
if (completionPhoto !== null) {
|
||||
requestBody.completion_photo = completionPhoto;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 먼저 수정 내용 저장
|
||||
const saveResponse = await fetch(`/api/management/${issueId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
if (saveResponse.ok) {
|
||||
alert('부적합이 수정되고 최종 완료 처리되었습니다.');
|
||||
closeIssueEditModal();
|
||||
initializeManagement(); // 페이지 새로고침
|
||||
} else {
|
||||
const error = await saveResponse.json();
|
||||
alert(`저장 및 완료 처리 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('저장 및 완료 처리 오류:', error);
|
||||
alert('저장 및 완료 처리 중 오류가 발생했습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 완료 확인 (기존 함수 - 필요시 사용)
|
||||
async function finalConfirmCompletion(issueId) {
|
||||
if (!confirm('이 부적합을 최종 완료 처리하시겠습니까?\n완료 처리 후에는 수정할 수 없습니다.')) {
|
||||
return;
|
||||
@@ -2138,8 +2402,8 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert('부적합이 최종 완료 처리되었습니다.');
|
||||
closeCompletionConfirmModal();
|
||||
loadManagementData(); // 페이지 새로고침
|
||||
closeIssueEditModal();
|
||||
initializeManagement(); // 페이지 새로고침
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`완료 처리 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
|
||||
Reference in New Issue
Block a user