feat: 부적합명과 상세 내용 분리 시스템 구현
🎯 부적합 정보 구조화 개선: 📝 수신함 검토 모달 개선: - '설명' → '부적합명' + '상세 내용'으로 분리 - 부적합명: 간단한 제목 (필수 입력) - 상세 내용: 자세한 설명 (선택 입력) - 저장 시 첫 번째 줄에 제목, 나머지는 상세 내용으로 결합 🏢 관리함 진행중 페이지 개선: - No. 옆에 프로젝트명 표시 (작은 글씨) - 부적합명을 카드 헤더에 큰 제목으로 표시 - '부적합 내용' → '상세 내용'으로 변경 - getIssueTitle(), getIssueDetail() 헬퍼 함수 추가 📊 현황판 페이지 개선: - No. 옆에 프로젝트명과 카테고리 태그 표시 - 부적합명을 카드 헤더에 제목으로 표시 - '부적합 내용' → '상세 내용'으로 변경 - 동일한 헬퍼 함수로 일관성 유지 💡 핵심 개선사항: - 정보 계층 구조 명확화 (제목 vs 상세) - 시각적 가독성 향상 (헤더에 중요 정보 집중) - 일관된 표시 방식 (수신함 → 관리함 → 현황판) - 기존 데이터 호환성 유지 🔧 기술적 구현: - 첫 번째 줄을 제목으로 추출 - 두 번째 줄부터를 상세 내용으로 분리 - 기존 description 필드 활용 (DB 변경 없음) - 폴백 처리로 안정성 확보 Expected Result: ✅ 부적합 정보의 체계적 관리 ✅ 한눈에 파악 가능한 제목 표시 ✅ 상세 내용과 요약 정보 분리 ✅ 전체 워크플로우 일관성 향상
This commit is contained in:
@@ -471,6 +471,20 @@
|
|||||||
container.innerHTML = dateGroups;
|
container.innerHTML = dateGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 부적합명 추출 (첫 번째 줄)
|
||||||
|
function getIssueTitle(issue) {
|
||||||
|
const description = issue.final_description || issue.description || '';
|
||||||
|
const lines = description.split('\n');
|
||||||
|
return lines[0] || '부적합명 없음';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 상세 내용 추출 (두 번째 줄부터)
|
||||||
|
function getIssueDetail(issue) {
|
||||||
|
const description = issue.final_description || issue.description || '';
|
||||||
|
const lines = description.split('\n');
|
||||||
|
return lines.slice(1).join('\n') || '상세 내용 없음';
|
||||||
|
}
|
||||||
|
|
||||||
// 이슈 카드 생성 (관리함 진행 중 스타일, 읽기 전용)
|
// 이슈 카드 생성 (관리함 진행 중 스타일, 읽기 전용)
|
||||||
function createIssueCard(issue) {
|
function createIssueCard(issue) {
|
||||||
const project = projects.find(p => p.id === issue.project_id);
|
const project = projects.find(p => p.id === issue.project_id);
|
||||||
@@ -526,13 +540,17 @@
|
|||||||
<div class="issue-card bg-white rounded-xl border border-gray-200 p-5 hover:shadow-xl hover:border-blue-300 transition-all duration-300 transform hover:-translate-y-1">
|
<div class="issue-card bg-white rounded-xl border border-gray-200 p-5 hover:shadow-xl hover:border-blue-300 transition-all duration-300 transform hover:-translate-y-1">
|
||||||
<!-- 헤더 -->
|
<!-- 헤더 -->
|
||||||
<div class="flex justify-between items-start mb-4">
|
<div class="flex justify-between items-start mb-4">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">No.${issue.project_sequence_no || '-'}</span>
|
<div class="flex items-center space-x-2">
|
||||||
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse shadow-sm"></div>
|
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">No.${issue.project_sequence_no || '-'}</span>
|
||||||
<span class="inline-flex items-center bg-gradient-to-r from-yellow-400 to-orange-400 text-white px-2 py-1 rounded-full text-xs font-medium shadow-sm">
|
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse shadow-sm"></div>
|
||||||
<i class="fas fa-tag mr-1"></i>
|
<span class="text-sm text-gray-600">${projectName}</span>
|
||||||
${getCategoryText(issue.final_category || issue.category)}
|
<span class="inline-flex items-center bg-gradient-to-r from-yellow-400 to-orange-400 text-white px-2 py-1 rounded-full text-xs font-medium shadow-sm">
|
||||||
</span>
|
<i class="fas fa-tag mr-1"></i>
|
||||||
|
${getCategoryText(issue.final_category || issue.category)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900">${getIssueTitle(issue)}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
${isUrgent() ? '<span class="bg-gradient-to-r from-red-500 to-red-600 text-white text-xs font-medium px-3 py-1 rounded-full shadow-sm">🔥 긴급</span>' : ''}
|
${isUrgent() ? '<span class="bg-gradient-to-r from-red-500 to-red-600 text-white text-xs font-medium px-3 py-1 rounded-full shadow-sm">🔥 긴급</span>' : ''}
|
||||||
@@ -548,13 +566,10 @@
|
|||||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||||
<!-- 첫 번째 행 -->
|
<!-- 첫 번째 행 -->
|
||||||
<div>
|
<div>
|
||||||
<span class="text-gray-600 font-medium">부적합 내용</span>
|
<span class="text-gray-600 font-medium">상세 내용</span>
|
||||||
<div class="mt-2 bg-gray-50 rounded-lg p-3 border-l-4 border-blue-200">
|
<div class="mt-2 bg-gray-50 rounded-lg p-3 border-l-4 border-blue-200">
|
||||||
<div class="text-gray-900 font-semibold text-sm mb-1">
|
<div class="text-gray-700 text-sm leading-relaxed">
|
||||||
${(issue.final_description || issue.description || '중복작업 신고용').split(' ')[0] || '부적합'}
|
${getIssueDetail(issue)}
|
||||||
</div>
|
|
||||||
<div class="text-gray-700 text-xs leading-relaxed">
|
|
||||||
${(issue.final_description || issue.description || '중복작업 신고용').split(' ').slice(1).join(' ') || '상세 내용 없음'}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -377,9 +377,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">설명</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">부적합명</label>
|
||||||
|
<input type="text" id="reviewTitle" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||||
|
placeholder="부적합의 간단한 제목을 입력하세요...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">상세 내용</label>
|
||||||
<textarea id="reviewDescription" rows="4" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
<textarea id="reviewDescription" rows="4" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
||||||
placeholder="부적합 설명을 입력하세요..."></textarea>
|
placeholder="부적합에 대한 상세한 설명을 입력하세요..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end space-x-3">
|
<div class="flex justify-end space-x-3">
|
||||||
@@ -1192,7 +1198,10 @@
|
|||||||
|
|
||||||
// 현재 값들로 폼 초기화
|
// 현재 값들로 폼 초기화
|
||||||
document.getElementById('reviewCategory').value = issue.category;
|
document.getElementById('reviewCategory').value = issue.category;
|
||||||
document.getElementById('reviewDescription').value = issue.description;
|
// 기존 description을 title과 description으로 분리 (첫 번째 줄을 title로 사용)
|
||||||
|
const lines = issue.description.split('\n');
|
||||||
|
document.getElementById('reviewTitle').value = lines[0] || '';
|
||||||
|
document.getElementById('reviewDescription').value = lines.slice(1).join('\n') || issue.description;
|
||||||
|
|
||||||
document.getElementById('reviewModal').classList.remove('hidden');
|
document.getElementById('reviewModal').classList.remove('hidden');
|
||||||
}
|
}
|
||||||
@@ -1209,13 +1218,17 @@
|
|||||||
|
|
||||||
const projectId = document.getElementById('reviewProjectId').value;
|
const projectId = document.getElementById('reviewProjectId').value;
|
||||||
const category = document.getElementById('reviewCategory').value;
|
const category = document.getElementById('reviewCategory').value;
|
||||||
|
const title = document.getElementById('reviewTitle').value.trim();
|
||||||
const description = document.getElementById('reviewDescription').value.trim();
|
const description = document.getElementById('reviewDescription').value.trim();
|
||||||
|
|
||||||
if (!description) {
|
if (!title) {
|
||||||
alert('설명을 입력해주세요.');
|
alert('부적합명을 입력해주세요.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 부적합명과 상세 내용을 합쳐서 저장 (첫 번째 줄에 제목, 나머지는 상세 내용)
|
||||||
|
const combinedDescription = title + (description ? '\n' + description : '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/inbox/${currentIssueId}/review`, {
|
const response = await fetch(`/api/inbox/${currentIssueId}/review`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -1226,7 +1239,7 @@
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
project_id: projectId ? parseInt(projectId) : null,
|
project_id: projectId ? parseInt(projectId) : null,
|
||||||
category: category,
|
category: category,
|
||||||
description: description
|
description: combinedDescription
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -692,18 +692,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 부적합명 추출 (첫 번째 줄)
|
||||||
|
function getIssueTitle(issue) {
|
||||||
|
const description = issue.final_description || issue.description || '';
|
||||||
|
const lines = description.split('\n');
|
||||||
|
return lines[0] || '부적합명 없음';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 상세 내용 추출 (두 번째 줄부터)
|
||||||
|
function getIssueDetail(issue) {
|
||||||
|
const description = issue.final_description || issue.description || '';
|
||||||
|
const lines = description.split('\n');
|
||||||
|
return lines.slice(1).join('\n') || '상세 내용 없음';
|
||||||
|
}
|
||||||
|
|
||||||
// 진행 중 카드 생성
|
// 진행 중 카드 생성
|
||||||
function createInProgressRow(issue, project) {
|
function createInProgressRow(issue, project) {
|
||||||
return `
|
return `
|
||||||
<div class="issue-card bg-white border border-gray-200 rounded-xl p-6 mb-4 shadow-sm hover:shadow-md transition-shadow" data-issue-id="${issue.id}">
|
<div class="issue-card bg-white border border-gray-200 rounded-xl p-6 mb-4 shadow-sm hover:shadow-md transition-shadow" data-issue-id="${issue.id}">
|
||||||
<!-- 카드 헤더 -->
|
<!-- 카드 헤더 -->
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex flex-col space-y-1">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-3">
|
||||||
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">No.${issue.project_sequence_no || '-'}</span>
|
<div class="flex items-center space-x-2">
|
||||||
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
|
<span class="text-xl font-bold bg-gradient-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">No.${issue.project_sequence_no || '-'}</span>
|
||||||
|
<div class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
|
||||||
|
</div>
|
||||||
|
<span class="text-sm text-gray-600">${project ? project.project_name : '프로젝트 미지정'}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-semibold text-gray-900">${project ? project.project_name : '프로젝트 미지정'}</h3>
|
<h3 class="text-lg font-semibold text-gray-900">${getIssueTitle(issue)}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<button onclick="saveIssueChanges(${issue.id})" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
<button onclick="saveIssueChanges(${issue.id})" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||||
@@ -720,9 +737,9 @@
|
|||||||
<!-- 왼쪽: 기본 정보 -->
|
<!-- 왼쪽: 기본 정보 -->
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">부적합 내용</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">상세 내용</label>
|
||||||
<div class="p-3 bg-gray-50 rounded-lg text-gray-800 min-h-[80px]">
|
<div class="p-3 bg-gray-50 rounded-lg text-gray-800 min-h-[80px]">
|
||||||
${issue.final_description || issue.description}
|
${getIssueDetail(issue)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user