- 목록 관리 페이지에 고급 필터링 시스템 추가 - 프로젝트별, 검토상태별, 날짜별 필터링 - 검토 완료/필요 항목 시각적 구분 및 정렬 - 해결 시간 입력 + 확인 버튼으로 검토 완료 처리 - 부적합 조회 페이지에 동일한 필터링 기능 적용 - 검토 상태에 따른 카드 스타일링 (음영 처리) - JavaScript 템플릿 리터럴 오류 수정 - 보고서 페이지 프로젝트별 분석 기능 추가 - 프로젝트 선택 드롭다운 추가 - 총 작업 공수를 프로젝트별 일일공수 데이터로 계산 - 부적합 처리 시간, 카테고리 분석, 상세 목록 모두 프로젝트별 필터링 - localStorage 키 이름 통일 (daily-work-data)
159 lines
8.9 KiB
HTML
159 lines
8.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>데이터 디버그 - M Project</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="/static/js/api.js?v=20250917"></script>
|
|
</head>
|
|
<body class="bg-gray-50">
|
|
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
|
<div class="bg-white rounded-xl shadow-sm p-6">
|
|
<h1 class="text-2xl font-bold text-gray-800 mb-6">
|
|
<i class="fas fa-bug text-red-500 mr-2"></i>데이터 디버그
|
|
</h1>
|
|
|
|
<div class="space-y-6">
|
|
<button
|
|
onclick="debugData()"
|
|
class="w-full bg-blue-500 text-white py-3 px-4 rounded-lg hover:bg-blue-600 transition-colors font-medium"
|
|
>
|
|
<i class="fas fa-search mr-2"></i>데이터 상태 확인
|
|
</button>
|
|
|
|
<div id="results" class="space-y-4">
|
|
<!-- 결과가 여기에 표시됩니다 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function debugData() {
|
|
const resultsDiv = document.getElementById('results');
|
|
resultsDiv.innerHTML = '<div class="text-center py-4"><i class="fas fa-spinner fa-spin text-2xl"></i></div>';
|
|
|
|
try {
|
|
// 1. 프로젝트 데이터 확인
|
|
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
|
|
|
|
// 2. 부적합 사항 데이터 확인 (API)
|
|
let apiIssues = [];
|
|
try {
|
|
apiIssues = await IssuesAPI.getAll();
|
|
} catch (error) {
|
|
console.error('API 조회 실패:', error);
|
|
}
|
|
|
|
// 3. 부적합 사항 데이터 확인 (localStorage)
|
|
const localIssues = JSON.parse(localStorage.getItem('work-report-issues') || '[]');
|
|
|
|
// 4. 일일 공수 데이터 확인
|
|
const dailyWork = JSON.parse(localStorage.getItem('daily-work-data') || '[]');
|
|
|
|
// 결과 표시
|
|
resultsDiv.innerHTML = `
|
|
<div class="space-y-6">
|
|
<!-- 프로젝트 데이터 -->
|
|
<div class="bg-blue-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-blue-800 mb-3">📁 프로젝트 데이터 (${projects.length}개)</h3>
|
|
<div class="space-y-2">
|
|
${projects.map(p => `
|
|
<div class="bg-white p-3 rounded border">
|
|
<div class="font-medium">ID: ${p.id}</div>
|
|
<div>Job No: ${p.jobNo}</div>
|
|
<div>이름: ${p.projectName}</div>
|
|
<div>활성: ${p.isActive ? '✅' : '❌'}</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API 부적합 사항 -->
|
|
<div class="bg-green-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-green-800 mb-3">🔗 API 부적합 사항 (${apiIssues.length}개)</h3>
|
|
<div class="space-y-2 max-h-60 overflow-y-auto">
|
|
${apiIssues.map(issue => `
|
|
<div class="bg-white p-3 rounded border text-sm">
|
|
<div><strong>ID:</strong> ${issue.id}</div>
|
|
<div><strong>project_id:</strong> ${issue.project_id || 'null'}</div>
|
|
<div><strong>project_name:</strong> ${issue.project_name || 'null'}</div>
|
|
<div><strong>설명:</strong> ${issue.description?.substring(0, 50)}...</div>
|
|
<div><strong>카테고리:</strong> ${issue.category}</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- localStorage 부적합 사항 -->
|
|
<div class="bg-yellow-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-yellow-800 mb-3">💾 localStorage 부적합 사항 (${localIssues.length}개)</h3>
|
|
<div class="space-y-2 max-h-60 overflow-y-auto">
|
|
${localIssues.map(issue => `
|
|
<div class="bg-white p-3 rounded border text-sm">
|
|
<div><strong>ID:</strong> ${issue.id}</div>
|
|
<div><strong>project_id:</strong> ${issue.project_id || 'null'}</div>
|
|
<div><strong>projectId:</strong> ${issue.projectId || 'null'}</div>
|
|
<div><strong>project_name:</strong> ${issue.project_name || 'null'}</div>
|
|
<div><strong>projectName:</strong> ${issue.projectName || 'null'}</div>
|
|
<div><strong>설명:</strong> ${issue.description?.substring(0, 50)}...</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 일일 공수 데이터 -->
|
|
<div class="bg-purple-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-purple-800 mb-3">⏰ 일일 공수 데이터 (${dailyWork.length}개)</h3>
|
|
<div class="space-y-2 max-h-60 overflow-y-auto">
|
|
${dailyWork.map(day => `
|
|
<div class="bg-white p-3 rounded border text-sm">
|
|
<div><strong>날짜:</strong> ${day.date}</div>
|
|
<div><strong>총 시간:</strong> ${day.totalHours}시간</div>
|
|
<div><strong>프로젝트들:</strong></div>
|
|
${day.projects?.map(p => `
|
|
<div class="ml-4 text-xs">
|
|
- ID: ${p.projectId}, 이름: ${p.projectName}, 시간: ${p.hours}
|
|
</div>
|
|
`).join('') || '<div class="ml-4 text-xs text-red-500">프로젝트 데이터 없음</div>'}
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 필터링 테스트 -->
|
|
<div class="bg-red-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-red-800 mb-3">🔍 필터링 테스트</h3>
|
|
<div class="space-y-2">
|
|
${projects.map(project => {
|
|
const matchingApiIssues = apiIssues.filter(issue => issue.project_id == project.id);
|
|
const matchingLocalIssues = localIssues.filter(issue => issue.project_id == project.id || issue.projectId == project.id);
|
|
|
|
return `
|
|
<div class="bg-white p-3 rounded border">
|
|
<div class="font-medium">${project.jobNo} - ${project.projectName} (ID: ${project.id})</div>
|
|
<div class="text-sm">API 매칭: ${matchingApiIssues.length}개</div>
|
|
<div class="text-sm">localStorage 매칭: ${matchingLocalIssues.length}개</div>
|
|
</div>
|
|
`;
|
|
}).join('')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
} catch (error) {
|
|
resultsDiv.innerHTML = `
|
|
<div class="bg-red-50 p-4 rounded-lg">
|
|
<h3 class="font-bold text-red-800 mb-3">❌ 오류 발생</h3>
|
|
<pre class="text-sm">${error.message}</pre>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|