refactor: 완료 반려 필드 분리 및 데이터 구조 개선
- backend: completion_rejection_reason 등 전용 필드 추가 - 기존 management_comment에 섞여있던 완료 반려 내용 분리 - 현황판: 완료 반려 내역 별도 카드로 표시 - 관리함: 해결방안에 완료 반려 내용 제외하여 표시 - DB 마이그레이션: completion_rejected_at, completion_rejected_by_id, completion_rejection_reason 필드 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -143,7 +143,12 @@ class Issue(Base):
|
||||
completion_requested_by_id = Column(Integer, ForeignKey("users.id")) # 완료 신청자
|
||||
completion_photo_path = Column(String(500)) # 완료 사진 경로
|
||||
completion_comment = Column(Text) # 완료 코멘트
|
||||
|
||||
|
||||
# 완료 반려 관련 필드들
|
||||
completion_rejected_at = Column(DateTime) # 완료 반려 시간
|
||||
completion_rejected_by_id = Column(Integer, ForeignKey("users.id")) # 완료 반려자
|
||||
completion_rejection_reason = Column(Text) # 완료 반려 사유
|
||||
|
||||
# Relationships
|
||||
reporter = relationship("User", back_populates="issues", foreign_keys=[reporter_id])
|
||||
reviewer = relationship("User", foreign_keys=[reviewed_by_id], overlaps="reviewed_issues")
|
||||
@@ -183,14 +188,28 @@ class DailyWork(Base):
|
||||
|
||||
class ProjectDailyWork(Base):
|
||||
__tablename__ = "project_daily_works"
|
||||
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
date = Column(DateTime, nullable=False, index=True)
|
||||
project_id = Column(BigInteger, ForeignKey("projects.id"), nullable=False)
|
||||
hours = Column(Float, nullable=False)
|
||||
created_by_id = Column(Integer, ForeignKey("users.id"))
|
||||
created_at = Column(DateTime, default=get_kst_now)
|
||||
|
||||
|
||||
# Relationships
|
||||
project = relationship("Project")
|
||||
created_by = relationship("User")
|
||||
|
||||
class DeletionLog(Base):
|
||||
__tablename__ = "deletion_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
entity_type = Column(String(50), nullable=False) # 'issue', 'project', 'daily_work' 등
|
||||
entity_id = Column(Integer, nullable=False) # 삭제된 엔티티의 ID
|
||||
entity_data = Column(JSONB, nullable=False) # 삭제된 데이터 전체 (JSON)
|
||||
deleted_by_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
deleted_at = Column(DateTime, default=get_kst_now, nullable=False)
|
||||
reason = Column(Text) # 삭제 사유 (선택사항)
|
||||
|
||||
# Relationships
|
||||
deleted_by = relationship("User")
|
||||
|
||||
@@ -152,7 +152,12 @@ class Issue(IssueBase):
|
||||
completion_requested_by_id: Optional[int] = None # 완료 신청자
|
||||
completion_photo_path: Optional[str] = None # 완료 사진 경로
|
||||
completion_comment: Optional[str] = None # 완료 코멘트
|
||||
|
||||
|
||||
# 완료 반려 관련 필드들
|
||||
completion_rejected_at: Optional[datetime] = None # 완료 반려 시간
|
||||
completion_rejected_by_id: Optional[int] = None # 완료 반려자
|
||||
completion_rejection_reason: Optional[str] = None # 완료 반려 사유
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@@ -200,6 +205,10 @@ class CompletionRequestRequest(BaseModel):
|
||||
completion_photo: str # 완료 사진 (Base64)
|
||||
completion_comment: Optional[str] = None # 완료 코멘트
|
||||
|
||||
class CompletionRejectionRequest(BaseModel):
|
||||
"""완료 신청 반려 요청"""
|
||||
rejection_reason: str # 반려 사유
|
||||
|
||||
class ManagementUpdateRequest(BaseModel):
|
||||
"""관리함에서 이슈 업데이트 요청"""
|
||||
final_description: Optional[str] = None
|
||||
|
||||
@@ -186,21 +186,66 @@ async def delete_issue(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
from database.models import DeletionLog
|
||||
import json
|
||||
|
||||
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="Issue not found")
|
||||
|
||||
# 권한 확인 (관리자만 삭제 가능)
|
||||
if current_user.role != UserRole.admin:
|
||||
raise HTTPException(status_code=403, detail="Only admin can delete issues")
|
||||
|
||||
|
||||
# 권한 확인 (관리자 또는 본인이 등록한 경우 삭제 가능)
|
||||
if current_user.role != UserRole.admin and issue.reporter_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="본인이 등록한 부적합만 삭제할 수 있습니다.")
|
||||
|
||||
# 이 이슈를 중복 대상으로 참조하는 다른 이슈들의 참조 제거
|
||||
referencing_issues = db.query(Issue).filter(Issue.duplicate_of_issue_id == issue_id).all()
|
||||
if referencing_issues:
|
||||
print(f"DEBUG: {len(referencing_issues)}개의 이슈가 이 이슈를 중복 대상으로 참조하고 있습니다. 참조를 제거합니다.")
|
||||
for ref_issue in referencing_issues:
|
||||
ref_issue.duplicate_of_issue_id = None
|
||||
db.flush() # 참조 제거를 먼저 커밋
|
||||
|
||||
# 삭제 로그 생성 (삭제 전 데이터 저장)
|
||||
issue_data = {
|
||||
"id": issue.id,
|
||||
"category": issue.category.value if issue.category else None,
|
||||
"description": issue.description,
|
||||
"status": issue.status.value if issue.status else None,
|
||||
"reporter_id": issue.reporter_id,
|
||||
"project_id": issue.project_id,
|
||||
"report_date": issue.report_date.isoformat() if issue.report_date else None,
|
||||
"work_hours": issue.work_hours,
|
||||
"detail_notes": issue.detail_notes,
|
||||
"photo_path": issue.photo_path,
|
||||
"photo_path2": issue.photo_path2,
|
||||
"review_status": issue.review_status.value if issue.review_status else None,
|
||||
"solution": issue.solution,
|
||||
"responsible_department": issue.responsible_department.value if issue.responsible_department else None,
|
||||
"responsible_person": issue.responsible_person,
|
||||
"expected_completion_date": issue.expected_completion_date.isoformat() if issue.expected_completion_date else None,
|
||||
"actual_completion_date": issue.actual_completion_date.isoformat() if issue.actual_completion_date else None,
|
||||
}
|
||||
|
||||
deletion_log = DeletionLog(
|
||||
entity_type="issue",
|
||||
entity_id=issue.id,
|
||||
entity_data=issue_data,
|
||||
deleted_by_id=current_user.id,
|
||||
reason=f"사용자 {current_user.username}에 의해 삭제됨"
|
||||
)
|
||||
db.add(deletion_log)
|
||||
|
||||
# 이미지 파일 삭제
|
||||
if issue.photo_path:
|
||||
delete_file(issue.photo_path)
|
||||
|
||||
if issue.photo_path2:
|
||||
delete_file(issue.photo_path2)
|
||||
if issue.completion_photo_path:
|
||||
delete_file(issue.completion_photo_path)
|
||||
|
||||
db.delete(issue)
|
||||
db.commit()
|
||||
return {"detail": "Issue deleted successfully"}
|
||||
return {"detail": "Issue deleted successfully", "logged": True}
|
||||
|
||||
@router.get("/stats/summary")
|
||||
async def get_issue_stats(
|
||||
@@ -353,3 +398,66 @@ async def request_completion(
|
||||
except:
|
||||
pass
|
||||
raise HTTPException(status_code=500, detail=f"완료 신청 처리 중 오류가 발생했습니다: {str(e)}")
|
||||
|
||||
@router.post("/{issue_id}/reject-completion")
|
||||
async def reject_completion_request(
|
||||
issue_id: int,
|
||||
request: schemas.CompletionRejectionRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
완료 신청 반려 - 관리자가 완료 신청을 반려
|
||||
"""
|
||||
# 이슈 조회
|
||||
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="부적합을 찾을 수 없습니다.")
|
||||
|
||||
# 완료 신청이 있는지 확인
|
||||
if not issue.completion_requested_at:
|
||||
raise HTTPException(status_code=400, detail="완료 신청이 없는 부적합입니다.")
|
||||
|
||||
# 권한 확인 (관리자 또는 관리함 접근 권한이 있는 사용자)
|
||||
if current_user.role != UserRole.admin and not check_page_access(current_user.id, 'issues_management', db):
|
||||
raise HTTPException(status_code=403, detail="완료 반려 권한이 없습니다.")
|
||||
|
||||
try:
|
||||
print(f"DEBUG: 완료 반려 시작 - Issue ID: {issue_id}, User: {current_user.username}")
|
||||
|
||||
# 완료 사진 파일 삭제
|
||||
if issue.completion_photo_path:
|
||||
try:
|
||||
delete_file(issue.completion_photo_path)
|
||||
print(f"DEBUG: 완료 사진 삭제 완료")
|
||||
except Exception as e:
|
||||
print(f"WARNING: 완료 사진 삭제 실패 - {str(e)}")
|
||||
|
||||
# 완료 신청 정보 초기화
|
||||
issue.completion_requested_at = None
|
||||
issue.completion_requested_by_id = None
|
||||
issue.completion_photo_path = None
|
||||
issue.completion_comment = None
|
||||
|
||||
# 완료 반려 정보 기록 (전용 필드 사용)
|
||||
issue.completion_rejected_at = datetime.now()
|
||||
issue.completion_rejected_by_id = current_user.id
|
||||
issue.completion_rejection_reason = request.rejection_reason
|
||||
|
||||
# 상태는 in_progress로 유지
|
||||
issue.review_status = ReviewStatus.in_progress
|
||||
|
||||
db.commit()
|
||||
db.refresh(issue)
|
||||
print(f"DEBUG: 완료 반려 처리 완료")
|
||||
|
||||
return {
|
||||
"message": "완료 신청이 반려되었습니다.",
|
||||
"issue_id": issue.id,
|
||||
"rejection_reason": request.rejection_reason
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: 완료 반려 처리 오류 - {str(e)}")
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"완료 반려 처리 중 오류가 발생했습니다: {str(e)}")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -425,6 +425,13 @@
|
||||
let currentIssueId = null;
|
||||
let currentTab = 'in_progress'; // 기본값: 진행 중
|
||||
|
||||
// 완료 반려 패턴 제거 (해결방안 표시용)
|
||||
function cleanManagementComment(text) {
|
||||
if (!text) return '';
|
||||
// 기존 데이터에서 완료 반려 패턴 제거
|
||||
return text.replace(/\[완료 반려[^\]]*\][^\n]*\n*/g, '').trim();
|
||||
}
|
||||
|
||||
// API 로드 후 초기화 함수
|
||||
async function initializeManagement() {
|
||||
const token = localStorage.getItem('access_token');
|
||||
@@ -782,6 +789,9 @@
|
||||
<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="confirmDeleteIssue(${issue.id})" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
|
||||
<i class="fas fa-trash 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 mr-1"></i>완료처리
|
||||
</button>
|
||||
@@ -852,9 +862,9 @@
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
<i class="fas fa-lightbulb text-yellow-500 mr-1"></i>해결방안
|
||||
<i class="fas fa-lightbulb text-yellow-500 mr-1"></i>해결방안 (확정)
|
||||
</label>
|
||||
<textarea id="solution_${issue.id}" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none ${isPendingCompletion ? 'bg-gray-100 cursor-not-allowed' : ''}" placeholder="해결 방안을 입력하세요..." ${isPendingCompletion ? 'readonly' : ''}>${issue.solution || ''}</textarea>
|
||||
<textarea id="management_comment_${issue.id}" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none ${isPendingCompletion ? 'bg-gray-100 cursor-not-allowed' : ''}" placeholder="확정된 해결 방안을 입력하세요..." ${isPendingCompletion ? 'readonly' : ''}>${cleanManagementComment(issue.management_comment)}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@@ -987,11 +997,11 @@
|
||||
관리 정보
|
||||
</h4>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div><span class="font-medium text-blue-700">해결방안:</span> <span class="text-blue-900">${issue.solution || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">해결방안 (확정):</span> <span class="text-blue-900">${cleanManagementComment(issue.management_comment) || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">담당부서:</span> <span class="text-blue-900">${getDepartmentText(issue.responsible_department) || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">담당자:</span> <span class="text-blue-900">${issue.responsible_person || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">원인부서:</span> <span class="text-blue-900">${getDepartmentText(issue.cause_department) || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">관리 코멘트:</span> <span class="text-blue-900">${issue.management_comment || '-'}</span></div>
|
||||
<div><span class="font-medium text-blue-700">관리 코멘트:</span> <span class="text-blue-900">${cleanManagementComment(issue.management_comment) || '-'}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1277,7 +1287,7 @@
|
||||
try {
|
||||
// 편집된 필드들의 값 수집
|
||||
const updates = {};
|
||||
const fields = ['solution', 'responsible_department', 'responsible_person', 'expected_completion_date', 'cause_department', 'management_comment'];
|
||||
const fields = ['management_comment', 'responsible_department', 'responsible_person', 'expected_completion_date', 'cause_department'];
|
||||
|
||||
fields.forEach(field => {
|
||||
const element = document.getElementById(`${field}_${issueId}`);
|
||||
@@ -1406,8 +1416,8 @@
|
||||
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">해결방안</label>
|
||||
<textarea id="modal_solution" rows="3" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">${issue.solution || ''}</textarea>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">해결방안 (확정)</label>
|
||||
<textarea id="modal_management_comment" rows="3" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="확정된 해결 방안을 입력하세요...">${cleanManagementComment(issue.management_comment)}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -1440,7 +1450,7 @@
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">의견</label>
|
||||
<textarea id="modal_management_comment" rows="3" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">${issue.management_comment || ''}</textarea>
|
||||
<textarea id="modal_management_comment" rows="3" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">${cleanManagementComment(issue.management_comment)}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -1465,7 +1475,7 @@
|
||||
try {
|
||||
// 편집된 필드들의 값 수집
|
||||
const updates = {};
|
||||
const fields = ['solution', 'responsible_department', 'responsible_person', 'expected_completion_date', 'cause_department', 'management_comment'];
|
||||
const fields = ['management_comment', 'responsible_department', 'responsible_person', 'expected_completion_date', 'cause_department'];
|
||||
|
||||
fields.forEach(field => {
|
||||
const element = document.getElementById(`modal_${field}`);
|
||||
@@ -1872,8 +1882,8 @@
|
||||
<h4 class="font-semibold text-green-800 mb-3">관리 정보</h4>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">해결방안</label>
|
||||
<textarea id="edit-solution-${issue.id}" rows="3" 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.solution || ''}</textarea>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">해결방안 (확정)</label>
|
||||
<textarea id="edit-management-comment-${issue.id}" rows="3" 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="확정된 해결 방안을 입력하세요...">${cleanManagementComment(issue.management_comment)}</textarea>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
@@ -1903,7 +1913,7 @@
|
||||
</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>
|
||||
<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="관리 코멘트를 입력하세요...">${cleanManagementComment(issue.management_comment)}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1964,6 +1974,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>
|
||||
<button onclick="confirmDeleteIssue(${issue.id})" class="px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
|
||||
<i class="fas fa-trash 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>
|
||||
@@ -2016,12 +2029,11 @@
|
||||
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 managementComment = document.getElementById(`edit-management-comment-${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}`);
|
||||
@@ -2064,16 +2076,15 @@
|
||||
}
|
||||
|
||||
const combinedDescription = title + (detail ? '\n' + detail : '');
|
||||
|
||||
|
||||
const requestBody = {
|
||||
final_description: combinedDescription,
|
||||
final_category: category,
|
||||
solution: solution || null,
|
||||
management_comment: managementComment || null,
|
||||
responsible_department: department || null,
|
||||
responsible_person: person || null,
|
||||
expected_completion_date: date || null,
|
||||
cause_department: causeDepartment || null,
|
||||
management_comment: managementComment || null
|
||||
cause_department: causeDepartment || null
|
||||
};
|
||||
|
||||
// 완료 신청 정보가 있으면 추가
|
||||
@@ -2267,7 +2278,7 @@
|
||||
<div class="bg-green-50 p-4 rounded-lg">
|
||||
<h4 class="font-semibold text-green-800 mb-2">관리 정보</h4>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div><span class="font-medium">해결방안:</span> ${issue.solution || '-'}</div>
|
||||
<div><span class="font-medium">해결방안 (확정):</span> ${cleanManagementComment(issue.management_comment) || '-'}</div>
|
||||
<div><span class="font-medium">담당부서:</span> ${issue.responsible_department || '-'}</div>
|
||||
<div><span class="font-medium">담당자:</span> ${issue.responsible_person || '-'}</div>
|
||||
<div><span class="font-medium">조치예상일:</span> ${issue.expected_completion_date ? new Date(issue.expected_completion_date).toLocaleDateString('ko-KR') : '-'}</div>
|
||||
@@ -2344,12 +2355,11 @@
|
||||
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 managementComment = document.getElementById(`edit-management-comment-${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}`);
|
||||
@@ -2392,16 +2402,15 @@
|
||||
}
|
||||
|
||||
const combinedDescription = title + (detail ? '\n' + detail : '');
|
||||
|
||||
|
||||
const requestBody = {
|
||||
final_description: combinedDescription,
|
||||
final_category: category,
|
||||
solution: solution || null,
|
||||
management_comment: managementComment || 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' // 완료 상태로 변경
|
||||
};
|
||||
|
||||
@@ -2466,6 +2475,75 @@
|
||||
alert('완료 처리 중 오류가 발생했습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// 삭제 확인 다이얼로그
|
||||
function confirmDeleteIssue(issueId) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[60]';
|
||||
modal.onclick = (e) => {
|
||||
if (e.target === modal) modal.remove();
|
||||
};
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="bg-white rounded-lg p-6 w-96 max-w-md mx-4">
|
||||
<div class="text-center mb-4">
|
||||
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4">
|
||||
<i class="fas fa-exclamation-triangle text-red-600 text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold mb-2">부적합 삭제</h3>
|
||||
<p class="text-sm text-gray-600">
|
||||
이 부적합 사항을 삭제하시겠습니까?<br>
|
||||
<strong class="text-red-600">삭제된 데이터는 로그로 보관되지만 복구할 수 없습니다.</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button onclick="this.closest('.fixed').remove()"
|
||||
class="flex-1 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
취소
|
||||
</button>
|
||||
<button onclick="handleDeleteIssueFromManagement(${issueId})"
|
||||
class="flex-1 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
|
||||
<i class="fas fa-trash mr-1"></i>삭제
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
}
|
||||
|
||||
// 삭제 처리 함수
|
||||
async function handleDeleteIssueFromManagement(issueId) {
|
||||
try {
|
||||
const response = await fetch(`/api/issues/${issueId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('부적합이 삭제되었습니다.\n삭제 로그가 기록되었습니다.');
|
||||
|
||||
// 모달들 닫기
|
||||
const deleteModal = document.querySelector('.fixed');
|
||||
if (deleteModal) deleteModal.remove();
|
||||
|
||||
closeIssueEditModal();
|
||||
|
||||
// 페이지 새로고침
|
||||
initializeManagement();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`삭제 실패: ${error.detail || '알 수 없는 오류'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('삭제 오류:', error);
|
||||
alert('삭제 중 오류가 발생했습니다: ' + error.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 추가 정보 입력 모달 -->
|
||||
|
||||
Reference in New Issue
Block a user