feat: 현황판을 관리함 스타일로 완전 변경 (날짜 그룹화 + 편집 가능)

🔄 Complete Layout Change:
-  단순 카드 그리드 →  관리함 스타일 날짜별 그룹화
- 날짜별 접기/펼치기 기능 추가
- 업로드일 기준으로 그룹화 및 최신순 정렬

📋 Card Layout (Management Style):
- 헤더: No., 긴급 표시 + 저장/완료처리 버튼
- 업로드 사진: 맨 위로 이동 (2개 슬롯, 없으면 '사진 없음')
- 부적합 내용: 읽기 전용 텍스트
- 원인 분류: 노란색 배지로 표시
- 관리 정보: 편집 가능한 입력 필드들

🎯 Interactive Features:
- 해결방안: textarea (편집 가능)
- 담당부서: select dropdown (편집 가능)
- 담당자: text input (편집 가능)
- 조치 예상일: date input (편집 가능)
- 원인부서: 읽기 전용

📅 Date Grouping:
- 업로드일(report_date) 기준 그룹화
- 날짜 헤더: 클릭으로 접기/펼치기
- 각 그룹별 건수 표시
- '업로드일' 배지로 기준 명시

🎨 Visual Improvements:
- 이모지 아이콘으로 필드 구분 (💡🏢👤📅)
- 진행 중 상태 애니메이션 유지
- 부드러운 hover 효과
- 일관된 색상 체계

🔧 Functionality:
- toggleDateGroup() 함수로 그룹 접기/펼치기
- 사진 모달 확대 기능 유지
- 긴급 표시 (3일 이내 마감)
- 저장/완료처리 버튼 (UI만, 기능은 추후)

Expected Result:
 관리함과 완전히 동일한 UI/UX
 날짜별 체계적 정리
 편집 가능한 관리 인터페이스
 직관적인 정보 표시
This commit is contained in:
Hyungi Ahn
2025-10-26 10:52:12 +09:00
parent b4fb461a32
commit 2a5455b221

View File

@@ -68,6 +68,24 @@
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* 날짜 그룹 스타일 */
.date-group {
margin-bottom: 1.5rem;
}
.date-header {
cursor: pointer;
transition: all 0.2s ease;
}
.date-header:hover {
background-color: #f3f4f6 !important;
}
.collapse-content {
transition: all 0.3s ease;
}
.progress-bar {
background: linear-gradient(90deg, #10b981 0%, #059669 100%);
@@ -381,7 +399,7 @@
document.getElementById('activeProjects').textContent = activeProjectIds.size;
}
// 이슈 카드 업데이트 (관리함 진행 중 카드 스타일)
// 이슈 카드 업데이트 (관리함 스타일 - 날짜별 그룹화)
function updateProjectCards() {
const container = document.getElementById('projectDashboard');
const emptyState = document.getElementById('emptyState');
@@ -394,14 +412,47 @@
emptyState.classList.add('hidden');
// 카드 그리드 생성
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 = cardsHTML;
// 날짜별로 그룹화
const groupedByDate = {};
filteredIssues.forEach(issue => {
const dateKey = new Date(issue.report_date).toLocaleDateString('ko-KR');
if (!groupedByDate[dateKey]) {
groupedByDate[dateKey] = [];
}
groupedByDate[dateKey].push(issue);
});
// 날짜별 그룹 생성
const dateGroups = Object.keys(groupedByDate)
.sort((a, b) => new Date(b) - new Date(a)) // 최신순
.map(dateKey => {
const issues = groupedByDate[dateKey];
const formattedDate = new Date(dateKey).toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}).replace(/\./g, '. ').trim();
return `
<div class="date-group mb-6">
<div class="date-header flex items-center justify-between p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition-colors" onclick="toggleDateGroup('${dateKey}')">
<div class="flex items-center space-x-3">
<i class="fas fa-chevron-down transition-transform duration-200" id="chevron-${dateKey}"></i>
<span class="font-semibold text-gray-800">${formattedDate}</span>
<span class="text-sm text-gray-500">(${issues.length}건)</span>
<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">업로드일</span>
</div>
</div>
<div class="collapse-content mt-4" id="content-${dateKey}">
<div class="grid grid-cols-1 gap-4">
${issues.map(issue => createIssueCard(issue)).join('')}
</div>
</div>
</div>
`;
}).join('');
container.innerHTML = dateGroups;
}
// 이슈 카드 생성 (관리함 진행 중 스타일, 읽기 전용)
@@ -456,83 +507,111 @@
};
return `
<div class="issue-card bg-white rounded-lg border border-gray-200 p-6 hover:shadow-lg transition-all duration-200">
<div class="issue-card bg-white rounded-lg border border-gray-200 p-6 hover:shadow-md 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>
<span class="text-sm text-gray-500">${formatKSTDate(issue.report_date)}</span>
<div class="flex items-center space-x-2">
<button class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600 transition-colors">
<i class="fas fa-edit mr-1"></i>저장
</button>
<button class="bg-green-500 text-white px-3 py-1 rounded text-sm hover:bg-green-600 transition-colors">
<i class="fas fa-check mr-1"></i>완료처리
</button>
</div>
</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>
<!-- 업로드 사진 (맨 위로) -->
${issue.photo_path || issue.photo_path2 ? `
<div class="mb-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-20 h-20 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>
` : `
<div class="w-20 h-20 bg-gray-100 rounded border flex items-center justify-center">
<span class="text-xs text-gray-400">사진 없음</span>
</div>
`}
${issue.photo_path2 ? `
<div class="w-20 h-20 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 class="w-20 h-20 bg-gray-100 rounded border flex items-center justify-center">
<span class="text-xs text-gray-400">사진 없음</span>
</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 min-h-[60px]">${issue.final_description || issue.description || '-'}</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">${getCategoryText(issue.final_category || issue.category)}</div>
` : `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">업로드 사진</label>
<div class="flex space-x-2">
<div class="w-20 h-20 bg-gray-100 rounded border flex items-center justify-center">
<span class="text-xs text-gray-400">사진 없음</span>
</div>
<div class="w-20 h-20 bg-gray-100 rounded border flex items-center justify-center">
<span class="text-xs text-gray-400">사진 없음</span>
</div>
</div>
</div>
`}
<!-- 부적합 내용 -->
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">부적합 내용</label>
<div class="text-sm text-gray-900">${issue.final_description || issue.description || '중복작업 신고용'}</div>
</div>
<!-- 원인 분류 -->
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">원인 분류</label>
<span class="inline-block bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-sm">${getCategoryText(issue.final_category || issue.category)}</span>
</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>
<label class="block text-sm font-medium text-gray-700 mb-1">💡 해결방안</label>
<textarea class="w-full text-sm border border-gray-300 rounded px-3 py-2 resize-none" rows="2" placeholder="해결 방안을 입력하세요...">${issue.solution || ''}</textarea>
</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>
<label class="block text-sm font-medium text-gray-700 mb-1">🏢 담당부서</label>
<select class="w-full text-sm border border-gray-300 rounded px-3 py-2">
<option value="">선택하세요</option>
<option value="production" ${issue.responsible_department === 'production' ? 'selected' : ''}>생산</option>
<option value="quality" ${issue.responsible_department === 'quality' ? 'selected' : ''}>품질</option>
<option value="purchasing" ${issue.responsible_department === 'purchasing' ? 'selected' : ''}>구매</option>
<option value="design" ${issue.responsible_department === 'design' ? 'selected' : ''}>설계</option>
<option value="sales" ${issue.responsible_department === 'sales' ? 'selected' : ''}>영업</option>
</select>
</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>
<label class="block text-sm font-medium text-gray-700 mb-1">👤 담당자</label>
<input type="text" class="w-full text-sm border border-gray-300 rounded px-3 py-2" placeholder="담당자 이름" value="${issue.responsible_person || ''}">
</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>
<label class="block text-sm font-medium text-gray-700 mb-1">📅 조치 예상일</label>
<input type="date" class="w-full text-sm border border-gray-300 rounded px-3 py-2" value="${issue.expected_completion_date ? issue.expected_completion_date.split('T')[0] : '2025-10-26'}">
</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 class="text-sm text-gray-900">${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">
@@ -584,6 +663,22 @@
}
});
// 날짜 그룹 토글 기능
function toggleDateGroup(dateKey) {
const content = document.getElementById(`content-${dateKey}`);
const chevron = document.getElementById(`chevron-${dateKey}`);
if (content.style.display === 'none') {
content.style.display = 'block';
chevron.classList.remove('fa-chevron-right');
chevron.classList.add('fa-chevron-down');
} else {
content.style.display = 'none';
chevron.classList.remove('fa-chevron-down');
chevron.classList.add('fa-chevron-right');
}
}
// 필터 및 정렬 함수들
function filterByProject() {
const projectId = document.getElementById('projectFilter').value;