feat: 진행 중 탭 카드 형식으로 개편 - 입력 편의성 대폭 향상
🎯 Card-Based Interface for In-Progress Issues: - 테이블 형태에서 카드 형태로 완전 변경 - 입력하기 편한 사용자 친화적 인터페이스 구현 - 진행 중/완료됨 탭별 다른 레이아웃 적용 📋 Enhanced Card Layout: - 2컬럼 그리드 레이아웃 (기본정보 vs 편집정보) - 카드 헤더: No. + 프로젝트명 + 액션 버튼들 - 왼쪽: 읽기전용 정보 (내용, 원인, 업로드사진) - 오른쪽: 편집가능 정보 (해결방안, 담당부서/담당자, 예상일) 🎨 Visual Improvements: - 아이콘과 색상으로 필드 구분 - 호버 효과 (카드 상승, 입력 필드 확대) - 상태 배지 및 진행 상황 표시 - 사진 썸네일 개선 (테두리, 호버 효과) ✏️ Input Field Enhancements: - 해결방안: 3줄 textarea with placeholder - 담당부서: 아이콘이 있는 select 드롭다운 - 담당자: placeholder가 있는 text input - 조치예상일: date picker with 아이콘 - 모든 필드에 focus 효과 적용 🔄 Tab-Specific Rendering: - 진행 중: 카드 형식 (space-y-4 컨테이너) - 완료됨: 테이블 형식 (기존 유지) - displayIssues()에서 currentTab에 따른 조건부 렌더링 🎯 User Experience Focus: - 입력하기 편한 넓은 필드들 - 시각적으로 구분되는 정보 영역 - 직관적인 아이콘과 라벨링 - 부드러운 애니메이션 효과 🔧 Technical Implementation: - createInProgressRow(): 카드 HTML 생성 - CSS 애니메이션 및 호버 효과 - 반응형 그리드 레이아웃 (lg:grid-cols-2) - 아이콘 정렬 및 스타일링 Expected Result: ✅ 진행 중 이슈 입력이 훨씬 편리해짐 ✅ 시각적으로 구분되는 정보 영역 ✅ 카드별 독립적인 작업 공간 제공 ✅ 완료됨은 기존 테이블 형태 유지
This commit is contained in:
@@ -181,6 +181,36 @@
|
|||||||
max-height: 0;
|
max-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 진행 중 카드 스타일 */
|
||||||
|
.issue-card {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-card label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-card input:focus,
|
||||||
|
.issue-card select:focus,
|
||||||
|
.issue-card textarea:focus {
|
||||||
|
transform: scale(1.01);
|
||||||
|
transition: transform 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-card .bg-gray-50 {
|
||||||
|
border-left: 4px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 카드 내 아이콘 스타일 */
|
||||||
|
.issue-card i {
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.badge-new { background: #dbeafe; color: #1e40af; }
|
.badge-new { background: #dbeafe; color: #1e40af; }
|
||||||
.badge-processing { background: #fef3c7; color: #92400e; }
|
.badge-processing { background: #fef3c7; color: #92400e; }
|
||||||
.badge-pending { background: #ede9fe; color: #7c3aed; }
|
.badge-pending { background: #ede9fe; color: #7c3aed; }
|
||||||
@@ -605,18 +635,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapse-content" id="${groupId}">
|
<div class="collapse-content" id="${groupId}">
|
||||||
<div class="issue-table-container">
|
${currentTab === 'in_progress' ?
|
||||||
<table class="issue-table">
|
// 진행 중: 카드 형식
|
||||||
<thead>
|
`<div class="space-y-4 mt-4">
|
||||||
<tr>
|
${issues.map(issue => createIssueRow(issue)).join('')}
|
||||||
${createTableHeader()}
|
</div>` :
|
||||||
</tr>
|
// 완료됨: 테이블 형식
|
||||||
</thead>
|
`<div class="issue-table-container">
|
||||||
<tbody>
|
<table class="issue-table">
|
||||||
${issues.map(issue => createIssueRow(issue)).join('')}
|
<thead>
|
||||||
</tbody>
|
<tr>
|
||||||
</table>
|
${createTableHeader()}
|
||||||
</div>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${issues.map(issue => createIssueRow(issue)).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -640,40 +677,105 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 진행 중 행 생성
|
// 진행 중 카드 생성
|
||||||
function createInProgressRow(issue, project) {
|
function createInProgressRow(issue, project) {
|
||||||
return `
|
return `
|
||||||
<tr 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}">
|
||||||
<td class="col-no font-medium">${issue.project_sequence_no || '-'}</td>
|
<!-- 카드 헤더 -->
|
||||||
<td class="col-project">${project ? project.project_name : '-'}</td>
|
<div class="flex items-center justify-between mb-4">
|
||||||
<td class="col-content text-wrap">${issue.final_description || issue.description}</td>
|
<div class="flex items-center space-x-3">
|
||||||
<td class="col-cause">${getCategoryText(issue.final_category || issue.category)}</td>
|
<span class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm font-medium">No.${issue.project_sequence_no || '-'}</span>
|
||||||
<td class="col-solution">
|
<h3 class="text-lg font-semibold text-gray-900">${project ? project.project_name : '프로젝트 미지정'}</h3>
|
||||||
${createEditableField('solution', issue.solution || '', 'textarea', issue.id, true)}
|
|
||||||
</td>
|
|
||||||
<td class="col-department">
|
|
||||||
${createEditableField('responsible_department', issue.responsible_department || '', 'select', issue.id, true, getDepartmentOptions())}
|
|
||||||
</td>
|
|
||||||
<td class="col-person">
|
|
||||||
${createEditableField('responsible_person', issue.responsible_person || '', 'text', issue.id, true)}
|
|
||||||
</td>
|
|
||||||
<td class="col-date">
|
|
||||||
${createEditableField('expected_completion_date', issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : '', 'date', issue.id, true)}
|
|
||||||
</td>
|
|
||||||
<td class="col-completion">
|
|
||||||
<button onclick="completeIssue(${issue.id})" class="btn-sm bg-green-500 text-white hover:bg-green-600 whitespace-nowrap">완료처리</button>
|
|
||||||
</td>
|
|
||||||
<td class="col-photos">
|
|
||||||
<div class="photo-container">
|
|
||||||
${issue.photo_path ? `<img src="${issue.photo_path}" class="issue-photo" onclick="openPhotoModal('${issue.photo_path}')" alt="업로드 사진 1">` : ''}
|
|
||||||
${issue.photo_path2 ? `<img src="${issue.photo_path2}" class="issue-photo" onclick="openPhotoModal('${issue.photo_path2}')" alt="업로드 사진 2">` : ''}
|
|
||||||
${!issue.photo_path && !issue.photo_path2 ? '-' : ''}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="flex space-x-2">
|
||||||
<td class="col-actions">
|
<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="btn-sm bg-blue-500 text-white hover:bg-blue-600">저장</button>
|
<i class="fas fa-save mr-1"></i>저장
|
||||||
</td>
|
</button>
|
||||||
</tr>
|
<button onclick="completeIssue(${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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 카드 내용 -->
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- 왼쪽: 기본 정보 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<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]">
|
||||||
|
${issue.final_description || issue.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<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">
|
||||||
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
|
||||||
|
${getCategoryText(issue.final_category || issue.category)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
${issue.photo_path ? `<img src="${issue.photo_path}" class="w-20 h-20 object-cover rounded-lg cursor-pointer border-2 border-gray-200 hover:border-blue-400 transition-colors" onclick="openPhotoModal('${issue.photo_path}')" alt="업로드 사진 1">` : '<div class="w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-xs border-2 border-dashed border-gray-300">사진 없음</div>'}
|
||||||
|
${issue.photo_path2 ? `<img src="${issue.photo_path2}" class="w-20 h-20 object-cover rounded-lg cursor-pointer border-2 border-gray-200 hover:border-blue-400 transition-colors" onclick="openPhotoModal('${issue.photo_path2}')" alt="업로드 사진 2">` : '<div class="w-20 h-20 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-xs border-2 border-dashed border-gray-300">사진 없음</div>'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 오른쪽: 편집 가능한 정보 -->
|
||||||
|
<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>해결방안
|
||||||
|
</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" placeholder="해결 방안을 입력하세요...">${issue.solution || ''}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
<i class="fas fa-building text-blue-500 mr-1"></i>담당부서
|
||||||
|
</label>
|
||||||
|
<select id="responsible_department_${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">
|
||||||
|
${getDepartmentOptions().map(opt =>
|
||||||
|
`<option value="${opt.value}" ${opt.value === (issue.responsible_department || '') ? 'selected' : ''}>${opt.text}</option>`
|
||||||
|
).join('')}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
<i class="fas fa-user text-purple-500 mr-1"></i>담당자
|
||||||
|
</label>
|
||||||
|
<input type="text" id="responsible_person_${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" placeholder="담당자 이름" value="${issue.responsible_person || ''}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
<i class="fas fa-calendar-alt text-red-500 mr-1"></i>조치 예상일
|
||||||
|
</label>
|
||||||
|
<input type="date" id="expected_completion_date_${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" value="${issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : ''}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 진행 상태 표시 -->
|
||||||
|
<div class="mt-4 p-3 bg-blue-50 rounded-lg">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-sm text-blue-700 font-medium">
|
||||||
|
<i class="fas fa-clock mr-1"></i>진행 중
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-blue-600">
|
||||||
|
신고일: ${new Date(issue.report_date).toLocaleDateString('ko-KR')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,6 +1053,9 @@
|
|||||||
let value = element.value.trim();
|
let value = element.value.trim();
|
||||||
if (value === '' || value === '선택하세요') {
|
if (value === '' || value === '선택하세요') {
|
||||||
value = null;
|
value = null;
|
||||||
|
} else if (field === 'expected_completion_date' && value) {
|
||||||
|
// 날짜 필드는 ISO datetime 형식으로 변환
|
||||||
|
value = value + 'T00:00:00';
|
||||||
}
|
}
|
||||||
updates[field] = value;
|
updates[field] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user