feat: 부적합 조회 페이지 UI/UX 대폭 개선
🔄 Major Changes: - ❌ 복잡한 모바일 캘린더 → ✅ 간단한 시작/끝 날짜 입력 - ❌ 전체 부적합 조회 → ✅ 자신이 올린 내용만 조회 📅 Date Filter Improvements: - 시작날짜/끝날짜 직접 입력 방식 - 빠른 선택 버튼: 오늘, 이번 주, 이번 달, 전체 - 날짜 기준: report_date (등록일) 기준으로 필터링 - 기본값: 이번 주 (일요일~오늘) 🔒 Privacy Enhancement: - 자신이 올린 부적합 사항만 표시 (reporter_id 기준) - 전체 항목은 현황판에서 확인 가능 - 개인 데이터 보호 강화 🎯 UX Improvements: - 모바일 캘린더 CSS/JS 제거로 페이지 로딩 속도 향상 - 직관적인 날짜 입력 인터페이스 - 날짜 유효성 검증 (시작날짜 ≤ 끝날짜) - 명확한 에러 메시지 📱 Mobile Friendly: - 네이티브 date picker 사용 - 터치 친화적 인터페이스 - 반응형 레이아웃 유지 🔧 Code Cleanup: - 모바일 캘린더 관련 코드 완전 제거 - 불필요한 CSS/JS 의존성 제거 - 함수 간소화 및 최적화 Expected Result: ✅ 더 빠르고 직관적인 날짜 선택 ✅ 개인 데이터 보호 ✅ 모바일 최적화 ✅ 코드 복잡성 감소
This commit is contained in:
@@ -11,8 +11,6 @@
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
<!-- 모바일 캘린더 스타일 -->
|
||||
<link rel="stylesheet" href="/static/css/mobile-calendar.css">
|
||||
|
||||
<!-- Custom Styles -->
|
||||
<style>
|
||||
@@ -148,26 +146,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 날짜 필터 (캘린더) -->
|
||||
<!-- 날짜 필터 (시작/끝 날짜) -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm font-medium text-gray-700">📅 기간 선택</label>
|
||||
<button id="toggleCalendar" class="text-sm text-blue-600 hover:text-blue-800 flex items-center">
|
||||
<span id="calendarToggleText">캘린더 열기</span>
|
||||
<i class="fas fa-chevron-down ml-1" id="calendarToggleIcon"></i>
|
||||
<label class="text-sm font-medium text-gray-700 mb-3 block">📅 기간 선택</label>
|
||||
|
||||
<!-- 빠른 선택 버튼 -->
|
||||
<div class="flex flex-wrap gap-2 mb-3">
|
||||
<button onclick="setDateRange('today')" class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-full hover:bg-blue-200 transition-colors">오늘</button>
|
||||
<button onclick="setDateRange('week')" class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-full hover:bg-blue-200 transition-colors">이번 주</button>
|
||||
<button onclick="setDateRange('month')" class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-full hover:bg-blue-200 transition-colors">이번 달</button>
|
||||
<button onclick="setDateRange('all')" class="px-3 py-1 text-xs bg-gray-100 text-gray-700 rounded-full hover:bg-gray-200 transition-colors">전체</button>
|
||||
</div>
|
||||
|
||||
<!-- 날짜 입력 필드 -->
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 mb-1 block">시작날짜:</label>
|
||||
<input type="date" id="startDateInput" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 mb-1 block">끝날짜:</label>
|
||||
<input type="date" id="endDateInput" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
||||
</div>
|
||||
<button onclick="applyDateFilter()" class="w-full px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<i class="fas fa-search mr-2"></i>조회
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 캘린더 컨테이너 -->
|
||||
<div id="calendarContainer" class="bg-white border border-gray-200 rounded-xl p-4" style="display: none;">
|
||||
<!-- 모바일 캘린더가 여기에 렌더링됩니다 -->
|
||||
</div>
|
||||
|
||||
<!-- 선택된 날짜 범위 표시 -->
|
||||
<div id="currentDateRange" class="mt-2 text-sm text-gray-600 bg-gray-50 px-3 py-2 rounded-lg">
|
||||
<i class="fas fa-calendar-alt mr-2"></i>
|
||||
<span id="dateRangeText">이번 주</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -191,15 +195,11 @@
|
||||
<script src="/static/js/core/permissions.js?v=20251025"></script>
|
||||
<script src="/static/js/components/common-header.js?v=20251025"></script>
|
||||
<script src="/static/js/core/page-manager.js?v=20251025"></script>
|
||||
<script src="/static/js/components/mobile-calendar.js?v=20251025"></script>
|
||||
<script>
|
||||
let currentUser = null;
|
||||
let issues = [];
|
||||
let projects = []; // 프로젝트 데이터 캐시
|
||||
let currentRange = 'week'; // 기본값: 이번 주
|
||||
let mobileCalendar = null; // 모바일 캘린더 인스턴스
|
||||
let selectedStartDate = null;
|
||||
let selectedEndDate = null;
|
||||
|
||||
// 애니메이션 함수들
|
||||
function animateHeaderAppearance() {
|
||||
@@ -287,10 +287,11 @@
|
||||
// 프로젝트 로드
|
||||
await loadProjects();
|
||||
|
||||
// 캘린더 초기화
|
||||
initializeMobileCalendar();
|
||||
// 기본 날짜 설정 (이번 주)
|
||||
setDefaultDateRange();
|
||||
|
||||
// 기본값: 이번 주 데이터 로드
|
||||
await loadIssues();
|
||||
setDateRange('week');
|
||||
}
|
||||
|
||||
@@ -320,69 +321,41 @@
|
||||
|
||||
// 네비게이션은 공통 헤더에서 처리됨
|
||||
|
||||
// 모바일 캘린더 초기화
|
||||
function initializeMobileCalendar() {
|
||||
// 캘린더 토글 버튼 이벤트
|
||||
document.getElementById('toggleCalendar').addEventListener('click', () => {
|
||||
const container = document.getElementById('calendarContainer');
|
||||
const toggleText = document.getElementById('calendarToggleText');
|
||||
const toggleIcon = document.getElementById('calendarToggleIcon');
|
||||
|
||||
if (container.style.display === 'none') {
|
||||
container.style.display = 'block';
|
||||
toggleText.textContent = '캘린더 닫기';
|
||||
toggleIcon.classList.remove('fa-chevron-down');
|
||||
toggleIcon.classList.add('fa-chevron-up');
|
||||
|
||||
// 캘린더 인스턴스가 없으면 생성
|
||||
if (!mobileCalendar) {
|
||||
mobileCalendar = new MobileCalendar('calendarContainer', {
|
||||
onRangeSelect: (startDate, endDate) => {
|
||||
handleDateRangeSelect(startDate, endDate);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
container.style.display = 'none';
|
||||
toggleText.textContent = '캘린더 열기';
|
||||
toggleIcon.classList.remove('fa-chevron-up');
|
||||
toggleIcon.classList.add('fa-chevron-down');
|
||||
}
|
||||
});
|
||||
// 기본 날짜 범위 설정
|
||||
function setDefaultDateRange() {
|
||||
const today = new Date();
|
||||
const weekStart = new Date(today);
|
||||
weekStart.setDate(today.getDate() - today.getDay()); // 이번 주 일요일
|
||||
|
||||
// 날짜 입력 필드에 기본값 설정
|
||||
document.getElementById('startDateInput').value = formatDateForInput(weekStart);
|
||||
document.getElementById('endDateInput').value = formatDateForInput(today);
|
||||
}
|
||||
|
||||
// 날짜 범위 선택 처리
|
||||
function handleDateRangeSelect(startDate, endDate) {
|
||||
selectedStartDate = startDate;
|
||||
selectedEndDate = endDate;
|
||||
// 날짜를 input[type="date"] 형식으로 포맷
|
||||
function formatDateForInput(date) {
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
// 날짜 필터 적용
|
||||
function applyDateFilter() {
|
||||
const startDate = document.getElementById('startDateInput').value;
|
||||
const endDate = document.getElementById('endDateInput').value;
|
||||
|
||||
// 날짜 범위 텍스트 업데이트
|
||||
updateDateRangeText(startDate, endDate);
|
||||
if (!startDate || !endDate) {
|
||||
alert('시작날짜와 끝날짜를 모두 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (new Date(startDate) > new Date(endDate)) {
|
||||
alert('시작날짜는 끝날짜보다 이전이어야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 필터 적용
|
||||
filterIssues();
|
||||
}
|
||||
|
||||
// 날짜 범위 텍스트 업데이트
|
||||
function updateDateRangeText(startDate, endDate) {
|
||||
const dateRangeText = document.getElementById('dateRangeText');
|
||||
|
||||
if (!startDate && !endDate) {
|
||||
dateRangeText.textContent = '전체 기간';
|
||||
currentRange = 'all';
|
||||
} else if (startDate && endDate) {
|
||||
const start = startDate.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
|
||||
const end = endDate.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
|
||||
const daysDiff = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
|
||||
dateRangeText.textContent = `${start} ~ ${end} (${daysDiff}일)`;
|
||||
currentRange = 'custom';
|
||||
} else if (startDate) {
|
||||
const start = startDate.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
|
||||
dateRangeText.textContent = `${start} (선택 중...)`;
|
||||
currentRange = 'custom';
|
||||
}
|
||||
}
|
||||
|
||||
// 사용자 역할에 따른 페이지 제목 업데이트
|
||||
function updatePageTitle(user) {
|
||||
const titleElement = document.getElementById('pageTitle');
|
||||
@@ -532,20 +505,20 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 날짜 범위 필터 적용 (캘린더에서 선택된 범위)
|
||||
if (selectedStartDate && selectedEndDate) {
|
||||
// 날짜 범위 필터 적용 (입력 필드에서 선택된 범위)
|
||||
const startDateInput = document.getElementById('startDateInput').value;
|
||||
const endDateInput = document.getElementById('endDateInput').value;
|
||||
|
||||
if (startDateInput && endDateInput) {
|
||||
filteredIssues = filteredIssues.filter(issue => {
|
||||
const issueDate = new Date(issue.created_at);
|
||||
const startOfDay = new Date(selectedStartDate);
|
||||
const issueDate = new Date(issue.report_date);
|
||||
const startOfDay = new Date(startDateInput);
|
||||
startOfDay.setHours(0, 0, 0, 0);
|
||||
const endOfDay = new Date(selectedEndDate);
|
||||
const endOfDay = new Date(endDateInput);
|
||||
endOfDay.setHours(23, 59, 59, 999);
|
||||
|
||||
return issueDate >= startOfDay && issueDate <= endOfDay;
|
||||
});
|
||||
} else if (currentRange && currentRange !== 'all' && currentRange !== 'custom') {
|
||||
// 빠른 선택 범위 적용
|
||||
filteredIssues = filterByDateRange(filteredIssues, currentRange);
|
||||
}
|
||||
|
||||
// 전역 변수에 필터링된 결과 저장
|
||||
@@ -570,29 +543,44 @@
|
||||
}
|
||||
|
||||
// 날짜 범위 설정 및 자동 조회
|
||||
async function setDateRange(range) {
|
||||
function setDateRange(range) {
|
||||
currentRange = range;
|
||||
|
||||
// 버튼 스타일 업데이트
|
||||
document.querySelectorAll('button[onclick^="setDateRange"]').forEach(btn => {
|
||||
if (btn.textContent.includes('전체') && range === 'all') {
|
||||
btn.className = 'px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-sm';
|
||||
} else if (btn.textContent.includes('오늘') && range === 'today') {
|
||||
btn.className = 'px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-sm';
|
||||
} else if (btn.textContent.includes('이번 주') && range === 'week') {
|
||||
btn.className = 'px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-sm';
|
||||
} else if (btn.textContent.includes('이번 달') && range === 'month') {
|
||||
btn.className = 'px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-sm';
|
||||
} else {
|
||||
btn.className = 'px-3 py-1.5 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 transition-colors text-sm';
|
||||
}
|
||||
});
|
||||
const today = new Date();
|
||||
let startDate, endDate;
|
||||
|
||||
await loadIssues(range);
|
||||
switch (range) {
|
||||
case 'today':
|
||||
startDate = new Date(today);
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
case 'week':
|
||||
startDate = new Date(today);
|
||||
startDate.setDate(today.getDate() - today.getDay()); // 이번 주 일요일
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
case 'month':
|
||||
startDate = new Date(today.getFullYear(), today.getMonth(), 1); // 이번 달 1일
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
case 'all':
|
||||
startDate = new Date(2020, 0, 1); // 충분히 과거 날짜
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// 날짜 입력 필드 업데이트
|
||||
document.getElementById('startDateInput').value = formatDateForInput(startDate);
|
||||
document.getElementById('endDateInput').value = formatDateForInput(endDate);
|
||||
|
||||
// 필터 적용
|
||||
filterIssues();
|
||||
}
|
||||
|
||||
// 부적합 사항 로드
|
||||
async function loadIssues(range) {
|
||||
// 부적합 사항 로드 (자신이 올린 내용만)
|
||||
async function loadIssues() {
|
||||
const container = document.getElementById('issueResults');
|
||||
container.innerHTML = `
|
||||
<div class="text-gray-500 text-center py-8">
|
||||
@@ -605,46 +593,23 @@
|
||||
// 모든 이슈 가져오기
|
||||
const allIssues = await IssuesAPI.getAll();
|
||||
|
||||
// 날짜 필터링
|
||||
const today = new Date();
|
||||
today.setHours(23, 59, 59, 999);
|
||||
let startDate = new Date();
|
||||
|
||||
switch(range) {
|
||||
case 'today':
|
||||
startDate = new Date();
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
break;
|
||||
case 'week':
|
||||
startDate.setDate(today.getDate() - 7);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
break;
|
||||
case 'month':
|
||||
startDate.setMonth(today.getMonth() - 1);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
break;
|
||||
case 'all':
|
||||
startDate = new Date(2020, 0, 1); // 충분히 과거 날짜
|
||||
break;
|
||||
}
|
||||
|
||||
// 필터링 및 정렬 (최신순)
|
||||
// 자신이 올린 이슈만 필터링
|
||||
issues = allIssues
|
||||
.filter(issue => {
|
||||
const issueDate = new Date(issue.report_date);
|
||||
return issueDate >= startDate && issueDate <= today;
|
||||
})
|
||||
.filter(issue => issue.reporter_id === currentUser.id)
|
||||
.sort((a, b) => new Date(b.report_date) - new Date(a.report_date));
|
||||
|
||||
// 결과 표시
|
||||
displayResults();
|
||||
filterIssues();
|
||||
|
||||
} catch (error) {
|
||||
console.error('조회 실패:', error);
|
||||
console.error('부적합 사항 로드 실패:', error);
|
||||
container.innerHTML = `
|
||||
<div class="text-red-500 text-center py-8">
|
||||
<i class="fas fa-exclamation-circle text-3xl mb-3"></i>
|
||||
<i class="fas fa-exclamation-triangle text-3xl mb-3"></i>
|
||||
<p>데이터를 불러오는데 실패했습니다.</p>
|
||||
<button onclick="loadIssues()" class="mt-3 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors">
|
||||
다시 시도
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user