feat: 현황판을 관리함 진행 중 카드 형식으로 완전 변경
🎨 UI Format Change: - ❌ 가로 테이블 형식 (정보 압축, 가독성 저하) - ✅ 관리함 진행 중 카드 형식 (직관적, 모바일 친화적) 📋 Card Layout Features: - 헤더: No., 긴급 표시, 등록일 - 기본 정보: 프로젝트, 부적합 내용, 원인 분류 - 관리 정보: 해결방안, 담당부서, 담당자, 조치 예상일, 원인부서 - 업로드 사진: 클릭 시 모달 확대 - 진행 중 표시: 애니메이션 점 + 상태 표시 🎯 UX Improvements: - 3컬럼 반응형 그리드 (데스크톱/태블릿/모바일) - 카드 hover 효과: 위로 이동 + 그림자 + 좌측 파란 테두리 - 긴급 표시: 예상완료일 3일 이내 시 빨간 배지 - 부드러운 애니메이션: 0.3초 transition - 사진 hover 효과: 확대 + 색상 변경 📱 Mobile Optimization: - 1컬럼 레이아웃으로 모바일 최적화 - 터치 친화적 카드 인터페이스 - 스크롤 가능한 세로 배치 - 적절한 패딩과 간격 🎨 Visual Enhancements: - 진행 중 상태 애니메이션 (pulse 효과) - 일관된 색상 체계 (파란색 강조) - 깔끔한 카드 디자인 - 정보 계층 구조 명확화 🔧 Code Structure: - createIssueRow → createIssueCard 함수 변경 - 테이블 HTML → 카드 그리드 HTML - 테이블 CSS → 카드 CSS 스타일 - 반응형 그리드 시스템 적용 Expected Result: ✅ 관리함과 일관된 UI/UX ✅ 대폭 향상된 가독성 ✅ 모바일 최적화 ✅ 직관적인 정보 표시
This commit is contained in:
@@ -27,34 +27,46 @@
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 이슈 테이블 스타일 (가로 테이블 레이아웃) */
|
||||
.dashboard-table {
|
||||
min-width: 1200px;
|
||||
/* 이슈 카드 스타일 (관리함 진행 중 스타일) */
|
||||
.issue-card {
|
||||
transition: all 0.3s ease;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
|
||||
.dashboard-table th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
.issue-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-left-color: #3b82f6;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.issue-card label {
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.issue-card .bg-gray-50 {
|
||||
background-color: #f9fafb;
|
||||
z-index: 10;
|
||||
border: 1px solid #e5e7eb;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dashboard-table td {
|
||||
vertical-align: middle;
|
||||
.issue-card .bg-gray-50:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.dashboard-table tr:hover {
|
||||
background-color: #f8fafc;
|
||||
.issue-card .fas.fa-image:hover {
|
||||
transform: scale(1.2);
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
/* 진행 중 애니메이션 */
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.dashboard-table .fas.fa-image:hover {
|
||||
transform: scale(1.1);
|
||||
.animate-pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
@@ -369,7 +381,7 @@
|
||||
document.getElementById('activeProjects').textContent = activeProjectIds.size;
|
||||
}
|
||||
|
||||
// 이슈 테이블 업데이트 (가로 테이블 스타일)
|
||||
// 이슈 카드 업데이트 (관리함 진행 중 카드 스타일)
|
||||
function updateProjectCards() {
|
||||
const container = document.getElementById('projectDashboard');
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
@@ -382,39 +394,18 @@
|
||||
|
||||
emptyState.classList.add('hidden');
|
||||
|
||||
// 가로 스크롤 테이블 생성
|
||||
const tableHTML = `
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dashboard-table">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[80px]">No.</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[120px]">프로젝트</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[200px]">부적합 내용</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[80px]">카테고리</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[150px]">해결방안</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[100px]">담당부서</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[100px]">담당자</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[120px]">예상완료일</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[100px]">원인부서</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[80px]">사진</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider min-w-[100px]">등록일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
${filteredIssues.map(issue => createIssueRow(issue)).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
// 카드 그리드 생성
|
||||
const cardsHTML = `
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
${filteredIssues.map(issue => createIssueCard(issue)).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.innerHTML = tableHTML;
|
||||
container.innerHTML = cardsHTML;
|
||||
}
|
||||
|
||||
// 이슈 테이블 행 생성 (가로 테이블 스타일, 읽기 전용)
|
||||
function createIssueRow(issue) {
|
||||
// 이슈 카드 생성 (관리함 진행 중 스타일, 읽기 전용)
|
||||
function createIssueCard(issue) {
|
||||
const project = projects.find(p => p.id === issue.project_id);
|
||||
const projectName = project ? project.project_name : '미지정';
|
||||
|
||||
@@ -464,72 +455,93 @@
|
||||
return diffDays <= 3; // 3일 이내 또는 지연
|
||||
};
|
||||
|
||||
// 사진 아이콘 생성
|
||||
const getPhotoIcons = () => {
|
||||
const photos = [];
|
||||
if (issue.photo_path) photos.push(issue.photo_path);
|
||||
if (issue.photo_path2) photos.push(issue.photo_path2);
|
||||
|
||||
if (photos.length === 0) return '-';
|
||||
|
||||
return photos.map(photo =>
|
||||
`<i class="fas fa-image text-blue-500 cursor-pointer hover:text-blue-700 mr-1"
|
||||
onclick="openPhotoModal('${photo}')" title="사진 보기"></i>`
|
||||
).join('');
|
||||
};
|
||||
|
||||
// 긴급 표시가 있는 No. 생성
|
||||
const getNoWithUrgent = () => {
|
||||
const noText = issue.project_sequence_no || '-';
|
||||
if (isUrgent()) {
|
||||
return `<div class="flex items-center space-x-2">
|
||||
<span class="font-medium text-blue-600">${noText}</span>
|
||||
<span class="bg-red-100 text-red-800 text-xs px-2 py-1 rounded">긴급</span>
|
||||
</div>`;
|
||||
}
|
||||
return `<span class="font-medium text-blue-600">${noText}</span>`;
|
||||
};
|
||||
|
||||
return `
|
||||
<tr class="hover:bg-gray-50 transition-colors duration-150">
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm">${getNoWithUrgent()}</td>
|
||||
<td class="px-4 py-4 text-sm text-gray-900">
|
||||
<div class="max-w-[120px] truncate" title="${projectName}">${projectName}</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 text-sm text-gray-900">
|
||||
<div class="max-w-[200px] truncate" title="${issue.final_description || issue.description || '-'}">
|
||||
${issue.final_description || issue.description || '-'}
|
||||
<div class="issue-card bg-white rounded-lg border border-gray-200 p-6 hover:shadow-lg transition-all duration-200">
|
||||
<!-- 헤더 -->
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-lg font-bold text-blue-600">No.${issue.project_sequence_no || '-'}</span>
|
||||
${isUrgent() ? '<span class="bg-red-100 text-red-800 text-xs font-medium px-2 py-1 rounded">긴급</span>' : ''}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
${getCategoryText(issue.final_category || issue.category)}
|
||||
</td>
|
||||
<td class="px-4 py-4 text-sm text-gray-900">
|
||||
<div class="max-w-[150px] truncate" title="${issue.solution || '-'}">
|
||||
${issue.solution || '-'}
|
||||
<span class="text-sm text-gray-500">${formatKSTDate(issue.report_date)}</span>
|
||||
</div>
|
||||
|
||||
<!-- 기본 정보 -->
|
||||
<div class="space-y-3 mb-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">프로젝트</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded">${projectName}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
${getDepartmentText(issue.responsible_department)}
|
||||
</td>
|
||||
<td class="px-4 py-4 text-sm text-gray-900">
|
||||
<div class="max-w-[100px] truncate" title="${issue.responsible_person || '-'}">
|
||||
${issue.responsible_person || '-'}
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">부적합 내용</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded min-h-[60px]">${issue.final_description || issue.description || '-'}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900 ${isUrgent() ? 'text-red-600 font-medium' : ''}">
|
||||
${formatKSTDate(issue.expected_completion_date)}
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
${getDepartmentText(issue.cause_department)}
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm">
|
||||
${getPhotoIcons()}
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
${formatKSTDate(issue.report_date)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">원인 분류</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded">${getCategoryText(issue.final_category || issue.category)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 관리 정보 -->
|
||||
<div class="space-y-3 border-t pt-4">
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">해결방안</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded min-h-[40px]">${issue.solution || '-'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">담당부서</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded">${getDepartmentText(issue.responsible_department)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">담당자</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded">${issue.responsible_person || '-'}</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">조치 예상일</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded ${isUrgent() ? 'text-red-600 font-medium' : ''}">${formatKSTDate(issue.expected_completion_date)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">원인부서</label>
|
||||
<div class="text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded">${getDepartmentText(issue.cause_department)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 업로드 사진 -->
|
||||
${issue.photo_path || issue.photo_path2 ? `
|
||||
<div class="border-t pt-4 mt-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
|
||||
<div class="flex space-x-2">
|
||||
${issue.photo_path ? `
|
||||
<div class="w-16 h-16 bg-gray-100 rounded border flex items-center justify-center cursor-pointer hover:bg-gray-200 transition-colors" onclick="openPhotoModal('${issue.photo_path}')">
|
||||
<i class="fas fa-image text-gray-400"></i>
|
||||
</div>
|
||||
` : ''}
|
||||
${issue.photo_path2 ? `
|
||||
<div class="w-16 h-16 bg-gray-100 rounded border flex items-center justify-center cursor-pointer hover:bg-gray-200 transition-colors" onclick="openPhotoModal('${issue.photo_path2}')">
|
||||
<i class="fas fa-image text-gray-400"></i>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- 진행 중 표시 -->
|
||||
<div class="border-t pt-4 mt-4 flex items-center justify-between">
|
||||
<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-sm font-medium text-blue-600">진행 중</span>
|
||||
</div>
|
||||
<span class="text-xs text-gray-500">신고일: ${formatKSTDate(issue.report_date)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user