fix: 그룹 리더 대시보드 작업 저장/삭제 오류 해결 및 작업 분석 시스템 성능 최적화

🔧 그룹 리더 대시보드 수정사항:
- API 호출 방식 수정 (modern-dashboard.js)
- 서버 API 요구사항에 맞는 데이터 구조 변경
- work_entries 배열 구조로 변경
- work_type_id → task_id 필드명 매핑
- 400 Bad Request 오류 해결

 작업 분석 시스템 성능 최적화:
- 중복 함수 제거 (isWeekend, isVacationProject 통합)
- WorkAnalysisAPI 캐싱 시스템 구현 (5분 만료)
- 네임스페이스 조직화 (utils, ui, analysis, render)
- ErrorHandler 통합 에러 처리 시스템
- 성능 모니터링 및 메모리 누수 방지
- GPU 가속 CSS 애니메이션 추가
- 디바운스/스로틀 함수 적용
- 의미 없는 통계 카드 제거

📊 작업 분석 페이지 개선:
- 프로그레스 바 애니메이션
- 토스트 알림 시스템
- 부드러운 전환 효과
- 반응형 최적화
- 메모리 사용량 모니터링
This commit is contained in:
Hyungi Ahn
2025-11-05 10:12:52 +09:00
parent 052e868599
commit ed40eec261
14 changed files with 8740 additions and 243 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,363 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>작업 분석 | (주)테크니컬코리아</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/work-analysis.css?v=42">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/auth-check.js?v=1" defer></script>
<script src="/js/api-config.js?v=1" defer></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
</head>
<body>
<div class="analysis-container">
<!-- 페이지 헤더 -->
<header class="page-header fade-in">
<h1 class="page-title">
<span class="icon">📊</span>
작업 분석
</h1>
<p class="page-subtitle">기간별/프로젝트별 작업 현황을 분석하고 통계를 확인합니다</p>
</header>
<!-- 분석 모드 탭 -->
<nav class="analysis-tabs fade-in">
<button class="tab-button active" data-mode="period">
📅 기간별 분석
</button>
<button class="tab-button" data-mode="project">
🏗️ 프로젝트별 분석
</button>
</nav>
<!-- 분석 조건 설정 -->
<section class="analysis-controls fade-in">
<div class="controls-grid">
<!-- 기간 설정 -->
<div class="form-group">
<label class="form-label" for="startDate">
<span class="icon">📅</span>
시작일
</label>
<input type="date" id="startDate" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label" for="endDate">
<span class="icon">📅</span>
종료일
</label>
<input type="date" id="endDate" class="form-input" required>
</div>
<!-- 기간 확정 버튼 -->
<div class="form-group">
<button class="confirm-period-button" id="confirmPeriodBtn">
<span class="icon"></span>
기간 확정
</button>
</div>
<!-- 기간 상태 표시 -->
<div class="form-group" id="periodStatusGroup" style="display: none;">
<div class="period-status">
<span class="icon"></span>
<div>
<div style="font-size: 0.8rem; opacity: 0.8; margin-bottom: 2px;">분석 기간</div>
<div id="periodStatus">기간이 설정되지 않았습니다</div>
</div>
</div>
</div>
</div>
</section>
<!-- 분석 결과 영역 -->
<main id="analysisResults" class="fade-in">
<!-- 로딩 상태 -->
<div id="loadingState" class="loading-container" style="display: none;">
<div class="loading-spinner"></div>
<p class="loading-text">분석 중입니다...</p>
</div>
<!-- 분석 탭 네비게이션 -->
<div id="analysisTabNavigation" class="tab-navigation" style="display: none;">
<div class="tab-buttons">
<button class="tab-button active" data-tab="work-status">
<span class="icon">📈</span>
기간별 작업 현황
</button>
<button class="tab-button" data-tab="project-distribution">
<span class="icon">🥧</span>
프로젝트별 분포
</button>
<button class="tab-button" data-tab="worker-performance">
<span class="icon">👤</span>
작업자별 성과
</button>
<button class="tab-button" data-tab="error-analysis">
<span class="icon">⚠️</span>
오류 분석
</button>
</div>
</div>
<!-- 결과 카드 그리드 -->
<div id="resultsGrid" class="results-grid" style="display: none;">
<!-- 통계 카드들 -->
<div class="stats-cards">
<div class="stat-card">
<div class="stat-icon"></div>
<div class="stat-content">
<div class="stat-label">총 작업시간</div>
<div class="stat-value" id="totalHours">0</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon"></div>
<div class="stat-content">
<div class="stat-label">정상 시간</div>
<div class="stat-value" id="normalHours">0</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">⚠️</div>
<div class="stat-content">
<div class="stat-label">오류 시간</div>
<div class="stat-value" id="errorHours">0</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">👥</div>
<div class="stat-content">
<div class="stat-label">참여 작업자</div>
<div class="stat-value" id="workerCount">0</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">📊</div>
<div class="stat-content">
<div class="stat-label">오류율</div>
<div class="stat-value" id="errorRate">0%</div>
</div>
</div>
</div>
<!-- 분석 탭 컨텐츠 -->
<div class="tab-contents">
<!-- 기간별 작업 현황 -->
<div id="work-status-tab" class="tab-content active">
<div class="chart-container table-type">
<div class="chart-header">
<h3 class="chart-title">
<span class="icon">📈</span>
기간별 작업 현황
</h3>
<button class="chart-analyze-btn" onclick="analyzeWorkStatus()" disabled>
<span class="icon">🔍</span>
분석 실행
</button>
</div>
<div class="table-container">
<!-- 테이블이 동적으로 생성됩니다 -->
</div>
</div>
</div>
<!-- 프로젝트별 분포 -->
<div id="project-distribution-tab" class="tab-content">
<div class="chart-container table-type">
<div class="chart-header">
<h3 class="chart-title">
<span class="icon">🥧</span>
프로젝트별 분포
</h3>
<button class="chart-analyze-btn" onclick="analyzeProjectDistribution()" disabled>
<span class="icon">🔍</span>
분석 실행
</button>
</div>
<div class="table-container">
<table class="production-report-table">
<thead>
<tr>
<th class="job-no-header">Job No.</th>
<th class="work-content-header">작업내용</th>
<th class="man-days-header">공수</th>
<th class="load-rate-header">전체 부하율</th>
<th class="labor-cost-header">인건비</th>
</tr>
</thead>
<tbody id="projectDistributionTableBody">
<tr>
<td colspan="5" style="text-align: center; padding: 2rem; color: #666;">
분석을 실행해주세요
</td>
</tr>
</tbody>
<tfoot id="projectDistributionTableFooter" style="display: none;">
<tr class="total-row">
<td colspan="2"><strong>총계</strong></td>
<td><strong id="totalManDays">0</strong></td>
<td><strong>100%</strong></td>
<td><strong id="totalLaborCost">₩0</strong></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<!-- 작업자별 성과 -->
<div id="worker-performance-tab" class="tab-content">
<div class="chart-container chart-type">
<div class="chart-header">
<h3 class="chart-title">
<span class="icon">👤</span>
작업자별 성과
</h3>
<button class="chart-analyze-btn" onclick="analyzeWorkerPerformance()" disabled>
<span class="icon">🔍</span>
분석 실행
</button>
</div>
<canvas id="workerPerformanceChart"></canvas>
</div>
</div>
<!-- 오류 분석 -->
<div id="error-analysis-tab" class="tab-content">
<div class="chart-container table-type">
<div class="chart-header">
<h3 class="chart-title">
<span class="icon">⚠️</span>
오류 분석
</h3>
<button class="chart-analyze-btn" onclick="analyzeErrorAnalysis()" disabled>
<span class="icon">🔍</span>
분석 실행
</button>
</div>
<div class="table-container">
<table class="error-analysis-table">
<thead>
<tr>
<th>Job No.</th>
<th>작업내용</th>
<th>총 시간</th>
<th>세부시간</th>
<th>작업 타입</th>
<th>오류율</th>
</tr>
</thead>
<tbody id="errorAnalysisTableBody">
<tr>
<td colspan="6" style="text-align: center; padding: 2rem; color: #666;">
분석을 실행해주세요
</td>
</tr>
</tbody>
<tfoot id="errorAnalysisTableFooter" style="display: none;">
<tr class="total-row">
<td colspan="2"><strong>총계</strong></td>
<td><strong id="totalErrorHours">0h</strong></td>
<td><strong>-</strong></td>
<td><strong>-</strong></td>
<td><strong>0.0%</strong></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- 모듈화된 JavaScript 로딩 -->
<script src="/js/work-analysis/module-loader.js?v=1" defer></script>
<script>
// 서울 표준시(KST) 기준 날짜 함수들 (하위 호환성 유지)
function getKSTDate() {
const now = new Date();
// UTC 시간에 9시간 추가 (KST = UTC+9)
const kstOffset = 9 * 60; // 9시간을 분으로 변환
const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
const kst = new Date(utc + (kstOffset * 60000));
return kst;
}
function formatDateToString(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 날짜 문자열을 간단한 형식으로 변환하는 함수 (하위 호환성 유지)
function formatSimpleDate(dateStr) {
if (!dateStr) return '날짜 없음';
if (typeof dateStr === 'string' && dateStr.includes('T')) {
return dateStr.split('T')[0]; // 2025-11-01T00:00:00.000Z → 2025-11-01
}
return dateStr;
}
// 현재 시간 업데이트 (하위 호환성 유지)
function updateTime() {
const now = new Date();
const timeString = now.toLocaleTimeString('ko-KR', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// 시간 표시 요소가 있다면 업데이트
const timeElement = document.querySelector('.time-value');
if (timeElement) {
timeElement.textContent = timeString;
}
}
// 페이지 로드 시 초기화
document.addEventListener('DOMContentLoaded', function() {
console.log('📦 작업 분석 모듈 로딩 시작...');
// 서울 표준시(KST) 기준 날짜 설정
const today = getKSTDate();
const monthStart = new Date(today.getFullYear(), today.getMonth(), 1); // 이번 달 1일
const monthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0); // 이번 달 마지막 날
document.getElementById('startDate').value = formatDateToString(monthStart);
document.getElementById('endDate').value = formatDateToString(monthEnd);
// 시간 업데이트 시작
updateTime();
setInterval(updateTime, 1000);
});
// 모듈 로딩 완료 후 초기화
window.addEventListener('workAnalysisModulesLoaded', function(event) {
console.log('🎉 작업 분석 모듈 로딩 완료:', event.detail.modules);
// 모듈 로딩 완료 후 추가 초기화 작업이 있다면 여기에 추가
});
// 초기 모드 설정 (하위 호환성 유지)
window.currentAnalysisMode = 'period';
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
<!-- 스크립트 (순서 중요: api-config.js가 먼저 로드되어야 함) -->
<script src="/js/api-config.js"></script>
<script src="/js/auth-check.js" defer></script>
<script src="/js/modern-dashboard.js" defer></script>
<script src="/js/modern-dashboard.js?v=3" defer></script>
</head>
<body>
<!-- 메인 컨테이너 -->