refactor: 임시 파일 정리 및 코드 클린업

제거된 파일들:
- migrate-data.html - 데이터 마이그레이션 도구 (완료됨)
- debug-data.html - 디버깅 도구 (완료됨)
- fix-api-data.html - API 데이터 수정 도구 (완료됨)
- fix-project-id.html - 프로젝트 ID 수정 도구 (완료됨)
- create-project-api.html - 프로젝트 API 생성 도구 (완료됨)
- chart.html - 구 버전 차트 페이지 (사용 안함)
- 루트 디렉토리 중복 파일들 (index.html, daily-work.html)

코드 정리:
- 디버깅용 console.log 제거
- 이미지 압축 로그 정리
- 필터링 디버깅 로그 정리

서비스 배포 준비를 위한 클린업 작업
This commit is contained in:
hyungi
2025-10-24 10:31:47 +09:00
parent 0e57cb99e8
commit 85f20aa051
11 changed files with 0 additions and 2868 deletions

View File

@@ -1,417 +0,0 @@
<!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="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--sky-50: #f0f9ff;
--sky-100: #e0f2fe;
--sky-200: #bae6fd;
--sky-300: #7dd3fc;
--sky-400: #38bdf8;
--sky-500: #0ea5e9;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
}
body {
background: linear-gradient(to bottom, #ffffff, #f0f9ff);
min-height: 100vh;
}
.glass {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.stat-card {
background: white;
border-left: 4px solid var(--sky-400);
transition: all 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.gallery-item {
cursor: pointer;
transition: all 0.2s;
}
.gallery-item:hover {
transform: scale(1.05);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
}
.modal.show {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<!-- 헤더 -->
<header class="glass sticky top-0 z-50 shadow-sm">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<h1 class="text-xl font-bold text-gray-800">
<i class="fas fa-chart-bar text-sky-500 mr-2"></i>차트
</h1>
<div class="flex items-center gap-4">
<a href="index.html" class="text-sky-600 hover:text-sky-700">
<i class="fas fa-camera text-xl"></i>
</a>
<span class="text-gray-600 text-sm" id="userName"></span>
</div>
</div>
</header>
<!-- 메인 컨텐츠 -->
<main class="container mx-auto px-4 py-6 max-w-6xl">
<!-- 통계 카드 -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div class="stat-card p-4 rounded-xl shadow-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">전체 사진</p>
<p class="text-2xl font-bold text-gray-800" id="totalCount">0</p>
</div>
<i class="fas fa-images text-3xl text-sky-400"></i>
</div>
</div>
<div class="stat-card p-4 rounded-xl shadow-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">오늘</p>
<p class="text-2xl font-bold text-gray-800" id="todayCount">0</p>
</div>
<i class="fas fa-calendar-day text-3xl text-sky-400"></i>
</div>
</div>
<div class="stat-card p-4 rounded-xl shadow-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">이번 주</p>
<p class="text-2xl font-bold text-gray-800" id="weekCount">0</p>
</div>
<i class="fas fa-calendar-week text-3xl text-sky-400"></i>
</div>
</div>
<div class="stat-card p-4 rounded-xl shadow-sm">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-600">가장 많은</p>
<p class="text-lg font-bold text-gray-800" id="topCategory">-</p>
</div>
<i class="fas fa-trophy text-3xl text-sky-400"></i>
</div>
</div>
</div>
<!-- 차트 영역 -->
<div class="grid md:grid-cols-2 gap-6 mb-8">
<!-- 카테고리별 차트 -->
<div class="glass p-6 rounded-xl shadow-lg">
<h3 class="text-lg font-semibold text-gray-800 mb-4">카테고리별 분포</h3>
<canvas id="categoryChart" width="400" height="300"></canvas>
</div>
<!-- 일별 추이 차트 -->
<div class="glass p-6 rounded-xl shadow-lg">
<h3 class="text-lg font-semibold text-gray-800 mb-4">최근 7일 추이</h3>
<canvas id="trendChart" width="400" height="300"></canvas>
</div>
</div>
<!-- 갤러리 -->
<div class="glass p-6 rounded-xl shadow-lg">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-800">전체 갤러리</h3>
<select id="categoryFilter" class="px-4 py-2 rounded-lg border border-gray-300 focus:border-sky-400 focus:outline-none">
<option value="all">전체 카테고리</option>
<option value="food">음식</option>
<option value="place">장소</option>
<option value="people">사람</option>
<option value="object">물건</option>
<option value="event">행사</option>
<option value="etc">기타</option>
</select>
</div>
<div id="gallery" class="grid grid-cols-3 md:grid-cols-6 gap-3">
<!-- 갤러리 아이템들이 여기에 표시됩니다 -->
</div>
</div>
</main>
<!-- 이미지 모달 -->
<div id="imageModal" class="modal" onclick="closeModal()">
<div class="bg-white rounded-xl p-4 max-w-2xl mx-4" onclick="event.stopPropagation()">
<img id="modalImage" class="w-full rounded-lg mb-4">
<div class="flex justify-between items-start">
<div>
<p class="font-semibold text-lg" id="modalDescription"></p>
<p class="text-sm text-gray-600 mt-1" id="modalInfo"></p>
</div>
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times text-xl"></i>
</button>
</div>
</div>
</div>
<script>
let allData = [];
let currentUser = null;
// 초기화
window.onload = () => {
// 로그인 체크
const params = new URLSearchParams(window.location.search);
currentUser = params.get('user');
if (!currentUser) {
// 로컬스토리지에서 최근 사용자 확인
const recentUser = localStorage.getItem('m-project-recent-user');
if (recentUser) {
currentUser = recentUser;
} else {
window.location.href = 'index.html';
return;
}
}
document.getElementById('userName').textContent = currentUser;
loadData();
};
// 데이터 로드
function loadData() {
const savedData = JSON.parse(localStorage.getItem('m-project-data') || '[]');
allData = savedData.filter(item => item.user === currentUser);
updateStats();
createCharts();
displayGallery();
}
// 통계 업데이트
function updateStats() {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
document.getElementById('totalCount').textContent = allData.length;
// 오늘 카운트
const todayCount = allData.filter(item => {
const itemDate = new Date(item.timestamp);
return itemDate >= today;
}).length;
document.getElementById('todayCount').textContent = todayCount;
// 이번 주 카운트
const weekCount = allData.filter(item => {
const itemDate = new Date(item.timestamp);
return itemDate >= weekAgo;
}).length;
document.getElementById('weekCount').textContent = weekCount;
// 가장 많은 카테고리
const categoryCounts = {};
allData.forEach(item => {
categoryCounts[item.category] = (categoryCounts[item.category] || 0) + 1;
});
const categoryNames = {
food: '음식',
place: '장소',
people: '사람',
object: '물건',
event: '행사',
etc: '기타'
};
if (Object.keys(categoryCounts).length > 0) {
const topCat = Object.entries(categoryCounts)
.sort((a, b) => b[1] - a[1])[0][0];
document.getElementById('topCategory').textContent = categoryNames[topCat];
}
}
// 차트 생성
function createCharts() {
// 카테고리별 차트
const categoryCounts = {};
allData.forEach(item => {
categoryCounts[item.category] = (categoryCounts[item.category] || 0) + 1;
});
const categoryNames = {
food: '음식',
place: '장소',
people: '사람',
object: '물건',
event: '행사',
etc: '기타'
};
const categoryCtx = document.getElementById('categoryChart').getContext('2d');
new Chart(categoryCtx, {
type: 'doughnut',
data: {
labels: Object.keys(categoryCounts).map(key => categoryNames[key]),
datasets: [{
data: Object.values(categoryCounts),
backgroundColor: [
'#0ea5e9',
'#38bdf8',
'#7dd3fc',
'#bae6fd',
'#e0f2fe',
'#f0f9ff'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// 일별 추이 차트
const today = new Date();
const dates = [];
const counts = [];
for (let i = 6; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);
date.setHours(0, 0, 0, 0);
const nextDate = new Date(date);
nextDate.setDate(nextDate.getDate() + 1);
const count = allData.filter(item => {
const itemDate = new Date(item.timestamp);
return itemDate >= date && itemDate < nextDate;
}).length;
dates.push(date.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' }));
counts.push(count);
}
const trendCtx = document.getElementById('trendChart').getContext('2d');
new Chart(trendCtx, {
type: 'line',
data: {
labels: dates,
datasets: [{
label: '업로드 수',
data: counts,
borderColor: '#0ea5e9',
backgroundColor: 'rgba(14, 165, 233, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
// 갤러리 표시
function displayGallery(filter = 'all') {
const gallery = document.getElementById('gallery');
gallery.innerHTML = '';
const filteredData = filter === 'all'
? allData
: allData.filter(item => item.category === filter);
// 최신순으로 정렬
filteredData.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
filteredData.forEach(item => {
const div = document.createElement('div');
div.className = 'gallery-item';
div.innerHTML = `<img src="${item.image}" class="w-full h-24 object-cover rounded-lg shadow-sm">`;
div.onclick = () => showModal(item);
gallery.appendChild(div);
});
if (filteredData.length === 0) {
gallery.innerHTML = '<p class="col-span-full text-center text-gray-500 py-8">표시할 이미지가 없습니다.</p>';
}
}
// 필터 변경
document.getElementById('categoryFilter').addEventListener('change', (e) => {
displayGallery(e.target.value);
});
// 모달 표시
function showModal(item) {
const categoryNames = {
food: '음식',
place: '장소',
people: '사람',
object: '물건',
event: '행사',
etc: '기타'
};
document.getElementById('modalImage').src = item.image;
document.getElementById('modalDescription').textContent = item.description;
document.getElementById('modalInfo').textContent =
`${categoryNames[item.category]}${new Date(item.timestamp).toLocaleString('ko-KR')}`;
document.getElementById('imageModal').classList.add('show');
}
// 모달 닫기
function closeModal() {
document.getElementById('imageModal').classList.remove('show');
}
</script>
</body>
</html>

View File

@@ -1,193 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 프로젝트 생성 - 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-2xl">
<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-plus text-green-500 mr-2"></i>API 프로젝트 생성
</h1>
<div class="space-y-4">
<div class="p-4 bg-green-50 border border-green-200 rounded-lg">
<h3 class="font-semibold text-green-800 mb-2">🎯 작업 내용</h3>
<p class="text-green-700 text-sm">
백엔드 API를 통해 TKR-25009R M Project를 생성합니다.<br>
데이터베이스에 실제 프로젝트 레코드가 생성됩니다.
</p>
</div>
<div id="status" class="space-y-2">
<!-- 상태 메시지가 여기에 표시됩니다 -->
</div>
<button
id="createBtn"
onclick="createProject()"
class="w-full bg-green-500 text-white py-3 px-4 rounded-lg hover:bg-green-600 transition-colors font-medium"
>
<i class="fas fa-plus mr-2"></i>TKR-25009R 프로젝트 생성
</button>
<a href="fix-api-data.html" class="block text-center text-orange-600 hover:text-orange-800 mt-4">
<i class="fas fa-wrench mr-1"></i>API 데이터 수정 도구로 이동
</a>
<a href="debug-data.html" class="block text-center text-blue-600 hover:text-blue-800 mt-2">
<i class="fas fa-bug mr-1"></i>디버그 도구로 이동
</a>
</div>
</div>
</div>
<script>
let currentUser = null;
// 페이지 로드 시 사용자 확인
window.addEventListener('DOMContentLoaded', () => {
const user = TokenManager.getUser();
if (!user) {
alert('로그인이 필요합니다.');
window.location.href = 'index.html';
return;
}
currentUser = user;
if (currentUser.role !== 'admin') {
alert('관리자만 접근 가능합니다.');
window.location.href = 'index.html';
return;
}
addStatus('✅ 관리자 권한 확인됨', 'text-green-600');
});
function addStatus(message, className = 'text-gray-600') {
const statusDiv = document.getElementById('status');
const p = document.createElement('p');
p.className = `text-sm ${className}`;
p.innerHTML = `<i class="fas fa-info-circle mr-2"></i>${message}`;
statusDiv.appendChild(p);
}
async function createProject() {
const btn = document.getElementById('createBtn');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>생성 중...';
try {
// 1. 기존 프로젝트 확인
addStatus('기존 프로젝트 확인 중...');
let existingProjects = [];
try {
existingProjects = await ProjectsAPI.getAll();
addStatus(`기존 프로젝트 ${existingProjects.length}개 발견`, 'text-blue-600');
} catch (error) {
addStatus('기존 프로젝트 조회 실패, 새로 생성합니다.', 'text-yellow-600');
}
// TKR-25009R 프로젝트가 이미 있는지 확인
const existingMProject = existingProjects.find(p => p.job_no === 'TKR-25009R');
if (existingMProject) {
addStatus(`✅ TKR-25009R 프로젝트가 이미 존재합니다 (ID: ${existingMProject.id})`, 'text-green-600');
// localStorage 프로젝트 ID 업데이트
updateLocalStorageProjectId(existingMProject.id);
btn.innerHTML = '<i class="fas fa-check mr-2"></i>이미 존재함';
btn.className = 'w-full bg-blue-500 text-white py-3 px-4 rounded-lg font-medium';
return;
}
// 2. 새 프로젝트 생성
addStatus('TKR-25009R 프로젝트 생성 중...');
const projectData = {
job_no: 'TKR-25009R',
project_name: 'M Project'
};
const newProject = await ProjectsAPI.create(projectData);
addStatus(`✅ 프로젝트 생성 완료! ID: ${newProject.id}`, 'text-green-600');
// 3. localStorage 프로젝트 ID 업데이트
updateLocalStorageProjectId(newProject.id);
// 완료
addStatus('🎉 프로젝트 생성 및 동기화 완료!', 'text-green-600 font-bold');
addStatus('이제 API 데이터 수정 도구를 실행하세요.', 'text-blue-600');
btn.innerHTML = '<i class="fas fa-check mr-2"></i>생성 완료';
btn.className = 'w-full bg-green-500 text-white py-3 px-4 rounded-lg font-medium';
} catch (error) {
addStatus(`❌ 오류 발생: ${error.message}`, 'text-red-600');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-redo mr-2"></i>다시 시도';
}
}
function updateLocalStorageProjectId(apiProjectId) {
try {
// localStorage 프로젝트 데이터 업데이트
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
const mProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (mProject) {
const oldId = mProject.id;
mProject.id = apiProjectId;
localStorage.setItem('work-report-projects', JSON.stringify(projects));
addStatus(`localStorage 프로젝트 ID 업데이트: ${oldId}${apiProjectId}`, 'text-blue-600');
// 일일 공수 데이터도 업데이트
const dailyWorkData = JSON.parse(localStorage.getItem('daily-work-data') || '[]');
let updatedCount = 0;
dailyWorkData.forEach(dayData => {
if (dayData.projects) {
dayData.projects.forEach(project => {
if (project.projectId == oldId || project.projectId == 1) {
project.projectId = apiProjectId;
project.projectName = 'TKR-25009R - M Project';
updatedCount++;
}
});
}
});
localStorage.setItem('daily-work-data', JSON.stringify(dailyWorkData));
addStatus(`일일 공수 데이터 ${updatedCount}개 업데이트`, 'text-blue-600');
// localStorage 부적합 사항도 업데이트
const localIssues = JSON.parse(localStorage.getItem('work-report-issues') || '[]');
let issueUpdatedCount = 0;
localIssues.forEach(issue => {
if (issue.projectId == oldId || issue.projectId == 1 || issue.project_id == oldId || issue.project_id == 1) {
issue.projectId = apiProjectId;
issue.project_id = apiProjectId;
issue.projectName = 'TKR-25009R - M Project';
issue.project_name = 'TKR-25009R - M Project';
issueUpdatedCount++;
}
});
localStorage.setItem('work-report-issues', JSON.stringify(localIssues));
addStatus(`localStorage 부적합 사항 ${issueUpdatedCount}개 업데이트`, 'text-blue-600');
}
} catch (error) {
addStatus(`localStorage 업데이트 실패: ${error.message}`, 'text-red-600');
}
}
</script>
</body>
</html>

View File

@@ -1,158 +0,0 @@
<!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>

View File

@@ -1,171 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API 데이터 수정 - 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-2xl">
<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-wrench text-orange-500 mr-2"></i>API 데이터 수정
</h1>
<div class="space-y-4">
<div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<h3 class="font-semibold text-yellow-800 mb-2">⚠️ 주의사항</h3>
<p class="text-yellow-700 text-sm">
이 작업은 API 데이터베이스의 모든 부적합 사항에 TKR-25009R 프로젝트 정보를 추가합니다.<br>
관리자(hyungi) 계정으로 로그인한 상태에서만 실행하세요.
</p>
</div>
<div id="status" class="space-y-2">
<!-- 상태 메시지가 여기에 표시됩니다 -->
</div>
<button
id="fixBtn"
onclick="fixApiData()"
class="w-full bg-orange-500 text-white py-3 px-4 rounded-lg hover:bg-orange-600 transition-colors font-medium"
>
<i class="fas fa-tools mr-2"></i>API 데이터 수정 시작
</button>
<a href="debug-data.html" class="block text-center text-blue-600 hover:text-blue-800 mt-4">
<i class="fas fa-bug mr-1"></i>디버그 도구로 이동
</a>
<a href="index.html" class="block text-center text-gray-600 hover:text-gray-800 mt-2">
<i class="fas fa-arrow-left mr-1"></i>메인으로 돌아가기
</a>
</div>
</div>
</div>
<script>
let currentUser = null;
// 페이지 로드 시 사용자 확인
window.addEventListener('DOMContentLoaded', () => {
const user = TokenManager.getUser();
if (!user) {
alert('로그인이 필요합니다.');
window.location.href = 'index.html';
return;
}
currentUser = user;
if (currentUser.role !== 'admin') {
alert('관리자만 접근 가능합니다.');
window.location.href = 'index.html';
return;
}
addStatus('✅ 관리자 권한 확인됨', 'text-green-600');
});
function addStatus(message, className = 'text-gray-600') {
const statusDiv = document.getElementById('status');
const p = document.createElement('p');
p.className = `text-sm ${className}`;
p.innerHTML = `<i class="fas fa-info-circle mr-2"></i>${message}`;
statusDiv.appendChild(p);
}
async function fixApiData() {
const btn = document.getElementById('fixBtn');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>수정 중...';
try {
// 1. TKR-25009R 프로젝트 찾기
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
const mProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (!mProject) {
throw new Error('TKR-25009R 프로젝트를 찾을 수 없습니다. 먼저 마이그레이션을 실행하세요.');
}
addStatus(`TKR-25009R 프로젝트 발견 (ID: ${mProject.id})`, 'text-blue-600');
// 2. API에서 모든 부적합 사항 가져오기
addStatus('API에서 부적합 사항 조회 중...');
const issues = await IssuesAPI.getAll();
addStatus(`${issues.length}개 부적합 사항 발견`, 'text-blue-600');
// 3. project_id가 null인 부적합 사항들 찾기
const issuesWithoutProject = issues.filter(issue => !issue.project_id);
addStatus(`프로젝트 정보가 없는 부적합 사항: ${issuesWithoutProject.length}`, 'text-yellow-600');
if (issuesWithoutProject.length === 0) {
addStatus('✅ 모든 부적합 사항에 이미 프로젝트 정보가 있습니다.', 'text-green-600');
btn.innerHTML = '<i class="fas fa-check mr-2"></i>완료';
btn.className = 'w-full bg-green-500 text-white py-3 px-4 rounded-lg font-medium';
return;
}
// 4. 각 부적합 사항 업데이트
let successCount = 0;
let errorCount = 0;
for (let i = 0; i < issuesWithoutProject.length; i++) {
const issue = issuesWithoutProject[i];
try {
addStatus(`${i + 1}/${issuesWithoutProject.length}: 부적합 사항 ID ${issue.id} 업데이트 중...`);
await IssuesAPI.update(issue.id, {
project_id: mProject.id
});
successCount++;
// UI 업데이트를 위한 짧은 대기
if (i % 5 === 0) {
await new Promise(resolve => setTimeout(resolve, 100));
}
} catch (error) {
console.error(`부적합 사항 ID ${issue.id} 업데이트 실패:`, error);
addStatus(`❌ 부적합 사항 ID ${issue.id} 업데이트 실패: ${error.message}`, 'text-red-600');
errorCount++;
}
}
// 5. 결과 표시
if (successCount > 0) {
addStatus(`${successCount}개 부적합 사항 업데이트 완료!`, 'text-green-600 font-bold');
}
if (errorCount > 0) {
addStatus(`${errorCount}개 부적합 사항 업데이트 실패`, 'text-red-600');
}
// 6. 완료 처리
if (errorCount === 0) {
btn.innerHTML = '<i class="fas fa-check mr-2"></i>모든 업데이트 완료';
btn.className = 'w-full bg-green-500 text-white py-3 px-4 rounded-lg font-medium';
addStatus('🎉 모든 API 데이터 수정이 완료되었습니다!', 'text-green-600 font-bold');
addStatus('이제 부적합 조회에서 프로젝트 필터가 정상 작동합니다.', 'text-blue-600');
} else {
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-redo mr-2"></i>다시 시도';
btn.className = 'w-full bg-orange-500 text-white py-3 px-4 rounded-lg hover:bg-orange-600 transition-colors font-medium';
}
} catch (error) {
addStatus(`❌ 오류 발생: ${error.message}`, 'text-red-600');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-redo mr-2"></i>다시 시도';
}
}
</script>
</body>
</html>

View File

@@ -1,134 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>프로젝트 ID 수정 - 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">
</head>
<body class="bg-gray-50">
<div class="container mx-auto px-4 py-8 max-w-2xl">
<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-edit text-blue-500 mr-2"></i>프로젝트 ID 수정
</h1>
<div class="space-y-4">
<div class="p-4 bg-blue-50 border border-blue-200 rounded-lg">
<h3 class="font-semibold text-blue-800 mb-2">📋 작업 내용</h3>
<p class="text-blue-700 text-sm">
큰 타임스탬프 ID를 작은 정수 ID로 변경합니다.<br>
TKR-25009R 프로젝트 ID: <code>1761264279704</code><code>1</code>
</p>
</div>
<div id="status" class="space-y-2">
<!-- 상태 메시지가 여기에 표시됩니다 -->
</div>
<button
id="fixBtn"
onclick="fixProjectId()"
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-tools mr-2"></i>프로젝트 ID 수정 시작
</button>
<a href="fix-api-data.html" class="block text-center text-orange-600 hover:text-orange-800 mt-4">
<i class="fas fa-wrench mr-1"></i>API 데이터 수정 도구로 이동
</a>
<a href="debug-data.html" class="block text-center text-blue-600 hover:text-blue-800 mt-2">
<i class="fas fa-bug mr-1"></i>디버그 도구로 이동
</a>
</div>
</div>
</div>
<script>
function addStatus(message, className = 'text-gray-600') {
const statusDiv = document.getElementById('status');
const p = document.createElement('p');
p.className = `text-sm ${className}`;
p.innerHTML = `<i class="fas fa-info-circle mr-2"></i>${message}`;
statusDiv.appendChild(p);
}
function fixProjectId() {
const btn = document.getElementById('fixBtn');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>수정 중...';
try {
// 1. 프로젝트 데이터 수정
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
const mProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (!mProject) {
throw new Error('TKR-25009R 프로젝트를 찾을 수 없습니다.');
}
const oldId = mProject.id;
const newId = 1;
addStatus(`기존 프로젝트 ID: ${oldId}`, 'text-blue-600');
addStatus(`새로운 프로젝트 ID: ${newId}`, 'text-blue-600');
// 프로젝트 ID 변경
mProject.id = newId;
localStorage.setItem('work-report-projects', JSON.stringify(projects));
addStatus('✅ 프로젝트 ID 변경 완료', 'text-green-600');
// 2. 일일 공수 데이터 수정
const dailyWorkData = JSON.parse(localStorage.getItem('daily-work-data') || '[]');
let updatedWorkDays = 0;
dailyWorkData.forEach(dayData => {
if (dayData.projects) {
dayData.projects.forEach(project => {
if (project.projectId == oldId) {
project.projectId = newId;
project.projectName = 'TKR-25009R - M Project';
updatedWorkDays++;
}
});
}
});
localStorage.setItem('daily-work-data', JSON.stringify(dailyWorkData));
addStatus(`${updatedWorkDays}개 일일 공수 데이터 업데이트 완료`, 'text-green-600');
// 3. localStorage 부적합 사항 수정
const localIssues = JSON.parse(localStorage.getItem('work-report-issues') || '[]');
let updatedIssues = 0;
localIssues.forEach(issue => {
if (issue.projectId == oldId || issue.project_id == oldId) {
issue.projectId = newId;
issue.project_id = newId;
issue.projectName = 'TKR-25009R - M Project';
issue.project_name = 'TKR-25009R - M Project';
updatedIssues++;
}
});
localStorage.setItem('work-report-issues', JSON.stringify(localIssues));
addStatus(`${updatedIssues}개 localStorage 부적합 사항 업데이트 완료`, 'text-green-600');
// 완료
addStatus('🎉 모든 데이터 ID 수정 완료!', 'text-green-600 font-bold');
addStatus('이제 API 데이터 수정 도구를 실행하세요.', 'text-blue-600');
btn.innerHTML = '<i class="fas fa-check mr-2"></i>완료';
btn.className = 'w-full bg-green-500 text-white py-3 px-4 rounded-lg font-medium';
} catch (error) {
addStatus(`❌ 오류 발생: ${error.message}`, 'text-red-600');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-redo mr-2"></i>다시 시도';
}
}
</script>
</body>
</html>

View File

@@ -602,8 +602,6 @@
// 원본 파일 크기 확인
const originalSize = file.size;
console.log(`원본 이미지 크기: ${ImageUtils.formatFileSize(originalSize)}`);
// 이미지 압축
const compressedImage = await ImageUtils.compressImage(file, {
maxWidth: 1280,
@@ -611,11 +609,6 @@
quality: 0.75
});
// 압축된 크기 확인
const compressedSize = ImageUtils.getBase64Size(compressedImage);
console.log(`압축된 이미지 크기: ${ImageUtils.formatFileSize(compressedSize)}`);
console.log(`압축률: ${Math.round((1 - compressedSize/originalSize) * 100)}%`);
currentPhotos.push(compressedImage);
updatePhotoPreview();
}
@@ -829,7 +822,6 @@
updateProgress(90);
updateLoadingMessage('완료 처리 중...', '거의 다 되었습니다');
console.log(`업로드 완료: ${uploadTime}ms`);
updateProgress(100);
@@ -1607,19 +1599,13 @@
let dailyWorkTotal = 0;
const dailyWorkData = JSON.parse(localStorage.getItem('daily-work-data') || '[]');
console.log('일일공수 데이터:', dailyWorkData);
console.log('선택된 프로젝트 ID:', selectedProjectId);
if (selectedProjectId) {
// 선택된 프로젝트의 일일 공수만 합계
dailyWorkData.forEach(dayWork => {
console.log('일일공수 항목:', dayWork);
if (dayWork.projects) {
dayWork.projects.forEach(project => {
console.log('프로젝트:', project, '매칭 확인:', project.projectId == selectedProjectId);
if (project.projectId == selectedProjectId || project.projectId.toString() === selectedProjectId.toString()) {
dailyWorkTotal += project.hours || 0;
console.log('시간 추가:', project.hours, '누적:', dailyWorkTotal);
}
});
}
@@ -1627,13 +1613,10 @@
} else {
// 전체 프로젝트의 일일 공수 합계
dailyWorkData.forEach(dayWork => {
console.log('전체 일일공수 항목:', dayWork);
dailyWorkTotal += dayWork.totalHours || 0;
});
}
console.log('최종 일일공수 합계:', dailyWorkTotal);
// 부적합 사항 해결 시간 계산 (필터링된 이슈만)
const issueHours = filteredIssues.reduce((sum, issue) => sum + (issue.work_hours || 0), 0);
const categoryCount = {};

View File

@@ -317,8 +317,6 @@
}
function filterIssues() {
console.log('필터링 시작 - 전체 이슈:', issues.length);
// 필터 값 가져오기
const selectedProjectId = document.getElementById('projectFilter').value;
const reviewStatusFilter = document.getElementById('reviewStatusFilter').value;
@@ -332,7 +330,6 @@
const issueProjectId = issue.project_id || issue.projectId;
return issueProjectId && (issueProjectId == selectedProjectId || issueProjectId.toString() === selectedProjectId.toString());
});
console.log('프로젝트 필터 후:', filteredIssues.length);
}
// 검토 상태 필터 적용
@@ -341,13 +338,11 @@
const isCompleted = isReviewCompleted(issue);
return reviewStatusFilter === 'completed' ? isCompleted : !isCompleted;
});
console.log('검토 상태 필터 후:', filteredIssues.length);
}
// 날짜 필터 적용
if (dateFilter) {
filteredIssues = filterByDate(filteredIssues, dateFilter);
console.log('날짜 필터 후:', filteredIssues.length);
}
// 전역 변수에 필터링된 결과 저장

View File

@@ -1,307 +0,0 @@
<!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-2xl">
<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-database text-blue-500 mr-2"></i>데이터 마이그레이션
</h1>
<div class="space-y-4">
<div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<h3 class="font-semibold text-yellow-800 mb-2">⚠️ 주의사항</h3>
<p class="text-yellow-700 text-sm">
이 작업은 기존 데이터를 "M Project"로 마이그레이션합니다.<br>
관리자(hyungi) 계정으로 로그인한 상태에서만 실행하세요.
</p>
</div>
<div id="status" class="space-y-2">
<!-- 상태 메시지가 여기에 표시됩니다 -->
</div>
<button
id="migrateBtn"
onclick="startMigration()"
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-play mr-2"></i>마이그레이션 시작
</button>
<a href="index.html" class="block text-center text-gray-600 hover:text-gray-800 mt-4">
<i class="fas fa-arrow-left mr-1"></i>메인으로 돌아가기
</a>
</div>
</div>
</div>
<script>
let currentUser = null;
// 페이지 로드 시 사용자 확인
window.addEventListener('DOMContentLoaded', () => {
const userData = localStorage.getItem('currentUser');
if (!userData) {
alert('로그인이 필요합니다.');
window.location.href = 'index.html';
return;
}
try {
currentUser = JSON.parse(userData);
} catch (e) {
currentUser = { username: userData };
}
const username = currentUser.username || currentUser;
if (username !== 'hyungi' && currentUser.role !== 'admin') {
alert('관리자만 접근 가능합니다.');
window.location.href = 'index.html';
return;
}
addStatus('✅ 관리자 권한 확인됨', 'text-green-600');
});
function addStatus(message, className = 'text-gray-600') {
const statusDiv = document.getElementById('status');
const p = document.createElement('p');
p.className = `text-sm ${className}`;
p.innerHTML = `<i class="fas fa-info-circle mr-2"></i>${message}`;
statusDiv.appendChild(p);
}
async function startMigration() {
const btn = document.getElementById('migrateBtn');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>마이그레이션 중...';
try {
// 1. M Project 생성
await createMProject();
// 2. 기존 부적합 사항 마이그레이션
await migrateIssues();
// 3. 기존 368시간 데이터 생성
await createSampleHours();
addStatus('🎉 모든 마이그레이션이 완료되었습니다!', 'text-green-600 font-bold');
btn.innerHTML = '<i class="fas fa-check mr-2"></i>완료';
btn.className = 'w-full bg-green-500 text-white py-3 px-4 rounded-lg font-medium';
} catch (error) {
addStatus(`❌ 오류 발생: ${error.message}`, 'text-red-600');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-redo mr-2"></i>다시 시도';
}
}
async function createMProject() {
addStatus('기존 M Project 확인 중...');
// 기존 프로젝트 확인
let projects = [];
const saved = localStorage.getItem('work-report-projects');
if (saved) {
projects = JSON.parse(saved);
}
// 기존 TKR-25009R M Project 찾기
let existingMProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (existingMProject) {
addStatus('✅ 기존 TKR-25009R M Project 발견', 'text-green-600');
return existingMProject.id;
}
// M-2024-001로 생성된 잘못된 프로젝트 찾기
const wrongProject = projects.find(p => p.jobNo === 'M-2024-001');
if (wrongProject) {
// 잘못된 프로젝트를 올바른 Job No.로 수정
wrongProject.jobNo = 'TKR-25009R';
wrongProject.projectName = 'M Project';
addStatus('✅ 기존 프로젝트를 TKR-25009R로 수정 완료', 'text-green-600');
localStorage.setItem('work-report-projects', JSON.stringify(projects));
return wrongProject.id;
}
// 새로 생성 (기존 프로젝트가 없는 경우)
const mProject = {
id: Date.now(),
jobNo: 'TKR-25009R',
projectName: 'M Project',
createdBy: 'hyungi',
createdByName: '관리자',
createdAt: new Date().toISOString(),
isActive: true
};
projects.push(mProject);
localStorage.setItem('work-report-projects', JSON.stringify(projects));
addStatus('✅ TKR-25009R M Project 생성 완료', 'text-green-600');
return mProject.id;
}
async function migrateIssues() {
addStatus('기존 부적합 사항 마이그레이션 중...');
// 기존 부적합 사항 로드 (localStorage와 API 모두 확인)
let issues = [];
// 먼저 localStorage에서 확인
const savedLocal = localStorage.getItem('work-report-issues');
if (savedLocal) {
issues = JSON.parse(savedLocal);
addStatus('localStorage에서 부적합 사항 발견', 'text-blue-600');
}
// API에서도 확인 (현재 시스템이 API 기반이므로)
try {
if (typeof IssuesAPI !== 'undefined') {
const apiIssues = await IssuesAPI.getAll();
if (apiIssues && apiIssues.length > 0) {
issues = apiIssues;
addStatus('API에서 부적합 사항 발견', 'text-blue-600');
}
}
} catch (error) {
addStatus('API 조회 실패, localStorage 데이터 사용', 'text-yellow-600');
}
if (issues.length === 0) {
addStatus('마이그레이션할 부적합 사항이 없습니다.', 'text-yellow-600');
return;
}
// TKR-25009R M Project ID 가져오기
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
const mProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (!mProject) {
throw new Error('TKR-25009R M Project를 찾을 수 없습니다.');
}
// 모든 부적합 사항에 프로젝트 ID 추가
let migratedCount = 0;
for (let issue of issues) {
if (!issue.project_id && !issue.projectId) {
// API 방식과 localStorage 방식 모두 지원
issue.project_id = mProject.id;
issue.projectId = mProject.id;
issue.project_name = 'TKR-25009R - M Project';
issue.projectName = 'TKR-25009R - M Project';
migratedCount++;
// API로 업데이트 시도
try {
if (typeof IssuesAPI !== 'undefined' && issue.id) {
await IssuesAPI.update(issue.id, {
project_id: mProject.id,
project_name: 'TKR-25009R - M Project'
});
}
} catch (error) {
console.log('API 업데이트 실패, localStorage만 업데이트');
}
}
}
// localStorage에도 저장
localStorage.setItem('work-report-issues', JSON.stringify(issues));
addStatus(`${migratedCount}개 부적합 사항 마이그레이션 완료`, 'text-green-600');
}
async function createSampleHours() {
addStatus('368시간 샘플 데이터 생성 중...');
// TKR-25009R M Project ID 가져오기
const projects = JSON.parse(localStorage.getItem('work-report-projects') || '[]');
const mProject = projects.find(p => p.jobNo === 'TKR-25009R');
if (!mProject) {
throw new Error('TKR-25009R M Project를 찾을 수 없습니다.');
}
// 기존 일일 공수 데이터 로드
let dailyWorkData = [];
const saved = localStorage.getItem('daily-work-data');
if (saved) {
dailyWorkData = JSON.parse(saved);
}
// 기존 데이터 중 잘못된 프로젝트로 등록된 것들 수정
let updatedCount = 0;
dailyWorkData.forEach(dayData => {
if (dayData.projects) {
dayData.projects.forEach(project => {
if (project.projectName && project.projectName.includes('M-2024-001')) {
project.projectId = mProject.id;
project.projectName = 'TKR-25009R - M Project';
updatedCount++;
}
});
}
});
if (updatedCount > 0) {
addStatus(`✅ 기존 ${updatedCount}개 프로젝트 데이터를 TKR-25009R로 수정`, 'text-green-600');
}
// 368시간을 여러 날짜에 분산해서 생성 (기존 데이터가 없는 경우만)
const workDays = [
{ date: '2024-10-01', hours: 48 },
{ date: '2024-10-02', hours: 52 },
{ date: '2024-10-03', hours: 44 },
{ date: '2024-10-04', hours: 40 },
{ date: '2024-10-07', hours: 56 },
{ date: '2024-10-08', hours: 48 },
{ date: '2024-10-09', hours: 36 },
{ date: '2024-10-10', hours: 44 }
];
let addedDays = 0;
workDays.forEach(workDay => {
// 해당 날짜에 이미 데이터가 있는지 확인
const existingData = dailyWorkData.find(d => d.date === workDay.date);
if (!existingData) {
const newData = {
date: workDay.date,
projects: [{
projectId: mProject.id,
projectName: 'TKR-25009R - M Project',
hours: workDay.hours
}],
totalHours: workDay.hours,
createdAt: new Date().toISOString(),
createdBy: 'hyungi'
};
dailyWorkData.push(newData);
addedDays++;
}
});
// 저장
localStorage.setItem('daily-work-data', JSON.stringify(dailyWorkData));
const totalCreatedHours = workDays.reduce((sum, day) => sum + day.hours, 0);
if (addedDays > 0) {
addStatus(`${addedDays}일간 총 ${totalCreatedHours}시간 데이터 생성 완료`, 'text-green-600');
} else {
addStatus('✅ 기존 시간 데이터 수정 완료', 'text-green-600');
}
}
</script>
</body>
</html>