feat: 캘린더 기반 작업 현황 확인 시스템 구현
- 월별 캘린더 UI로 작업 현황을 한눈에 확인 가능 - 미입력(빨강), 부분입력(주황), 확인필요(보라), 이상없음(초록) 상태 표시 - 범례 아이콘(●)을 사용한 직관적인 상태 표시 - 날짜 클릭 시 해당일 작업자별 상세 현황 모달 - 작업자 클릭 시 개별 작업 입력/수정 모달 - 휴가 처리 기능 (연차, 반차, 반반차, 조퇴) - 월별 집계 데이터 최적화로 API 호출 최소화 백엔드: - monthly_worker_status, monthly_summary 테이블 추가 - 자동 집계 stored procedure 및 trigger 구현 - 확인필요(12시간 초과) 상태 감지 로직 - 출석 관리 시스템 확장 프론트엔드: - 캘린더 그리드 UI 구현 - 상태별 색상 및 아이콘 표시 - 모달 기반 상세 정보 표시 - 반응형 디자인 적용
This commit is contained in:
@@ -3,98 +3,284 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>일일 작업보고서 조회</title>
|
||||
<link rel="stylesheet" href="/css/daily-report-viewer.css">
|
||||
<title>작업 현황 확인 - TK 건설</title>
|
||||
<link rel="stylesheet" href="/css/common.css?v=13">
|
||||
<link rel="stylesheet" href="/css/modern-dashboard.css?v=13">
|
||||
<link rel="stylesheet" href="/css/work-report-calendar.css?v=22">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="page-header">
|
||||
<h1>📊 일일 작업보고서 조회</h1>
|
||||
<p class="subtitle">날짜를 선택하여 해당일의 작업 현황을 확인하세요</p>
|
||||
</header>
|
||||
|
||||
<div class="date-selector">
|
||||
<div class="date-input-group">
|
||||
<label for="reportDate">📅 조회 날짜:</label>
|
||||
<input type="date" id="reportDate" class="date-input">
|
||||
<button id="searchBtn" class="search-btn">조회</button>
|
||||
<button id="todayBtn" class="today-btn">오늘</button>
|
||||
<!-- 대시보드 헤더 -->
|
||||
<header class="dashboard-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<div class="brand">
|
||||
<img src="/img/logo.png" alt="테크니컬코리아" class="brand-logo">
|
||||
<div class="brand-text">
|
||||
<h1 class="brand-title">테크니컬코리아</h1>
|
||||
<p class="brand-subtitle">작업 현황 확인</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-center">
|
||||
<div class="current-time" id="currentTime">
|
||||
<span class="time-label">현재 시각</span>
|
||||
<span class="time-value" id="timeValue">--:--:--</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<div class="user-profile" id="userProfile">
|
||||
<div class="user-avatar">
|
||||
<span class="avatar-text" id="userInitial">사</span>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="user-name" id="userName">사용자</span>
|
||||
<span class="user-role" id="userRole">작업자</span>
|
||||
</div>
|
||||
<div class="profile-menu" id="profileMenu">
|
||||
<a href="/pages/profile/my-profile.html" class="menu-item">
|
||||
<span class="menu-icon">👤</span>
|
||||
내 프로필
|
||||
</a>
|
||||
<a href="/pages/profile/change-password.html" class="menu-item">
|
||||
<span class="menu-icon">🔐</span>
|
||||
비밀번호 변경
|
||||
</a>
|
||||
<a href="/pages/dashboard/group-leader.html" class="menu-item">
|
||||
<span class="menu-icon">📊</span>
|
||||
대시보드
|
||||
</a>
|
||||
<button class="menu-item logout-btn" id="logoutBtn">
|
||||
<span class="menu-icon">🚪</span>
|
||||
로그아웃
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="loadingSpinner" class="loading-spinner" style="display: none;">
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="dashboard-main">
|
||||
<div class="calendar-page-container">
|
||||
<!-- 페이지 제목 -->
|
||||
<div class="page-title-section">
|
||||
<h2 class="page-title">📅 작업 현황 확인</h2>
|
||||
<p class="page-subtitle">월별 작업자 현황을 한눈에 확인하세요</p>
|
||||
</div>
|
||||
|
||||
<!-- 캘린더 카드 -->
|
||||
<div class="calendar-card">
|
||||
<!-- 월 네비게이션 -->
|
||||
<div class="calendar-nav">
|
||||
<button id="prevMonthBtn" class="nav-btn prev-btn">
|
||||
<span class="nav-icon">‹</span>
|
||||
<span class="nav-text">이전</span>
|
||||
</button>
|
||||
|
||||
<div class="calendar-title">
|
||||
<h3 id="monthYearTitle">2025년 11월</h3>
|
||||
<button id="todayBtn" class="today-btn">오늘</button>
|
||||
</div>
|
||||
|
||||
<button id="nextMonthBtn" class="nav-btn next-btn">
|
||||
<span class="nav-text">다음</span>
|
||||
<span class="nav-icon">›</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 범례 -->
|
||||
<div class="calendar-legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot has-overtime-warning"></div>
|
||||
<span>확인필요</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot has-errors"></div>
|
||||
<span>미입력</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot has-issues"></div>
|
||||
<span>부분입력</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-dot has-normal"></div>
|
||||
<span>이상 없음</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 캘린더 -->
|
||||
<div class="calendar-grid">
|
||||
<div class="calendar-header">
|
||||
<div class="day-header sunday">일</div>
|
||||
<div class="day-header">월</div>
|
||||
<div class="day-header">화</div>
|
||||
<div class="day-header">수</div>
|
||||
<div class="day-header">목</div>
|
||||
<div class="day-header">금</div>
|
||||
<div class="day-header saturday">토</div>
|
||||
</div>
|
||||
<div class="calendar-days" id="calendarDays">
|
||||
<!-- 캘린더 날짜들이 여기에 동적으로 생성됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 로딩 스피너 -->
|
||||
<div id="loadingSpinner" class="loading-overlay" style="display: none;">
|
||||
<div class="loading-content">
|
||||
<div class="spinner"></div>
|
||||
<p>데이터를 불러오는 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="errorMessage" class="error-message" style="display: none;">
|
||||
<div class="error-content">
|
||||
<span class="error-icon">⚠️</span>
|
||||
<span class="error-text"></span>
|
||||
<!-- 일일 작업 현황 모달 -->
|
||||
<div id="dailyWorkModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container large-modal">
|
||||
<div class="modal-header">
|
||||
<h2 id="modalTitle">2025년 11월 3일 작업 현황</h2>
|
||||
<button class="modal-close-btn" onclick="closeDailyWorkModal()">×</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="noDataMessage" class="no-data-message" style="display: none;">
|
||||
<div class="no-data-content">
|
||||
<span class="no-data-icon">📭</span>
|
||||
<h3>해당 날짜의 작업보고서가 없습니다</h3>
|
||||
<p>다른 날짜를 선택해 주세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reportSummary" class="report-summary" style="display: none;">
|
||||
<div class="summary-cards">
|
||||
<div class="summary-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">👥</span>
|
||||
<span class="card-title">작업자 수</span>
|
||||
|
||||
<div class="modal-body">
|
||||
<!-- 요약 정보 -->
|
||||
<div class="daily-summary">
|
||||
<div class="summary-card">
|
||||
<div class="summary-icon success">👥</div>
|
||||
<div class="summary-content">
|
||||
<div class="summary-label">총 작업자</div>
|
||||
<div class="summary-value" id="modalTotalWorkers">0명</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-value" id="totalWorkers">0</div>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">⏰</span>
|
||||
<span class="card-title">총 작업시간</span>
|
||||
<div class="summary-card">
|
||||
<div class="summary-icon primary">⏰</div>
|
||||
<div class="summary-content">
|
||||
<div class="summary-label">총 작업시간</div>
|
||||
<div class="summary-value" id="modalTotalHours">0.0h</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-value" id="totalHours">0시간</div>
|
||||
</div>
|
||||
<div class="summary-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">📝</span>
|
||||
<span class="card-title">작업 항목</span>
|
||||
<div class="summary-card">
|
||||
<div class="summary-icon warning">📝</div>
|
||||
<div class="summary-content">
|
||||
<div class="summary-label">작업 건수</div>
|
||||
<div class="summary-value" id="modalTotalTasks">0건</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-value" id="totalEntries">0개</div>
|
||||
</div>
|
||||
<div class="summary-card error-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon">⚠️</span>
|
||||
<span class="card-title">에러 항목</span>
|
||||
<div class="summary-card">
|
||||
<div class="summary-icon error">⚠️</div>
|
||||
<div class="summary-content">
|
||||
<div class="summary-label">오류 건수</div>
|
||||
<div class="summary-value" id="modalErrorCount">0건</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-value" id="errorCount">0개</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="workersReport" class="workers-report" style="display: none;">
|
||||
<h2 class="section-title">👥 작업자별 상세 현황</h2>
|
||||
<div id="workersList" class="workers-list">
|
||||
<!-- 작업자별 데이터가 여기에 표시됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="exportSection" class="export-section" style="display: none;">
|
||||
<h3>📤 데이터 내보내기</h3>
|
||||
<div class="export-buttons">
|
||||
<button id="exportExcelBtn" class="export-btn excel-btn">
|
||||
📊 Excel로 내보내기
|
||||
</button>
|
||||
<button id="printBtn" class="export-btn print-btn">
|
||||
🖨️ 인쇄
|
||||
</button>
|
||||
<!-- 작업자 현황 리스트 -->
|
||||
<div class="modal-work-status">
|
||||
<div class="work-status-header">
|
||||
<h3>작업자별 현황</h3>
|
||||
<div class="status-filter">
|
||||
<select id="statusFilter">
|
||||
<option value="all">전체</option>
|
||||
<option value="incomplete">미입력</option>
|
||||
<option value="partial">부분입력</option>
|
||||
<option value="complete">완료</option>
|
||||
<option value="overtime">연장근로</option>
|
||||
<option value="error">오류</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modalWorkersList" class="worker-status-list">
|
||||
<!-- 작업자 리스트가 여기에 동적으로 생성됩니다 -->
|
||||
</div>
|
||||
|
||||
<div id="modalNoData" class="empty-state" style="display: none;">
|
||||
<div class="empty-icon">📭</div>
|
||||
<h3>해당 날짜의 작업 보고서가 없습니다</h3>
|
||||
<p>다른 날짜를 선택해 주세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/daily-report-viewer.js"></script>
|
||||
<!-- 작업 입력 모달 -->
|
||||
<div id="workEntryModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<h2 id="workEntryModalTitle">작업 입력</h2>
|
||||
<button class="modal-close-btn" onclick="closeWorkEntryModal()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form id="workEntryForm">
|
||||
<!-- 작업자 정보 -->
|
||||
<div class="form-section">
|
||||
<h3>작업자 정보</h3>
|
||||
<div class="form-group">
|
||||
<label class="form-label">작업자</label>
|
||||
<input type="text" id="workerNameDisplay" class="form-control" readonly>
|
||||
<input type="hidden" id="workerId">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">작업 날짜</label>
|
||||
<input type="date" id="workDate" class="form-control" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 작업 내용 -->
|
||||
<div class="form-section">
|
||||
<h3>작업 내용</h3>
|
||||
<div class="form-group">
|
||||
<label class="form-label">프로젝트 *</label>
|
||||
<select id="projectSelect" class="form-control" required>
|
||||
<option value="">프로젝트를 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">작업 시간 (시간) *</label>
|
||||
<input type="number" id="workHours" class="form-control" min="0" max="24" step="0.5" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">작업 상태 *</label>
|
||||
<select id="workStatusSelect" class="form-control" required>
|
||||
<option value="">상태를 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">작업 설명</label>
|
||||
<textarea id="workDescription" class="form-control" rows="3" placeholder="작업 내용을 상세히 입력하세요"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 휴가 처리 -->
|
||||
<div class="form-section">
|
||||
<h3>휴가 처리</h3>
|
||||
<div class="vacation-buttons">
|
||||
<button type="button" class="btn-vacation" onclick="handleVacation('full')">연차 (8시간)</button>
|
||||
<button type="button" class="btn-vacation" onclick="handleVacation('half')">반차 (4시간)</button>
|
||||
<button type="button" class="btn-vacation" onclick="handleVacation('quarter')">반반차 (2시간)</button>
|
||||
<button type="button" class="btn-vacation" onclick="handleVacation('early')">조퇴 (6시간)</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeWorkEntryModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveWorkEntry()">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="/js/api-config.js?v=13"></script>
|
||||
<script src="/js/auth-check.js?v=13"></script>
|
||||
<script src="/js/load-navbar.js?v=13"></script>
|
||||
<script src="/js/work-report-calendar.js?v=27"></script>
|
||||
</body>
|
||||
</html>
|
||||
170
web-ui/pages/common/worker-individual-report.html
Normal file
170
web-ui/pages/common/worker-individual-report.html
Normal file
@@ -0,0 +1,170 @@
|
||||
<!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="stylesheet" href="/css/design-system.css">
|
||||
<link rel="stylesheet" href="/css/daily-work-report.css">
|
||||
<link rel="icon" type="image/png" href="/img/favicon.png">
|
||||
|
||||
<script src="/js/api-config.js"></script>
|
||||
<script src="/js/auth-check.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="work-report-container">
|
||||
<!-- 네비게이션 바 -->
|
||||
<div id="navbar-container"></div>
|
||||
|
||||
<!-- 헤더 -->
|
||||
<header class="work-report-header">
|
||||
<h1 id="pageTitle">👤 개별 작업 보고서</h1>
|
||||
<p class="subtitle" id="pageSubtitle">작업자의 일일 작업 내용을 입력하고 수정합니다.</p>
|
||||
</header>
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="work-report-main">
|
||||
<!-- 뒤로가기 버튼 -->
|
||||
<a href="javascript:history.back()" class="back-button">
|
||||
← 뒤로가기
|
||||
</a>
|
||||
|
||||
<!-- 작업자 정보 카드 -->
|
||||
<div class="worker-info-card" id="workerInfoCard">
|
||||
<div class="worker-avatar-large">
|
||||
<span id="workerInitial">작</span>
|
||||
</div>
|
||||
<div class="worker-info-details">
|
||||
<h2 id="workerName">작업자명</h2>
|
||||
<p id="workerJob">직종</p>
|
||||
<p id="selectedDate">날짜</p>
|
||||
</div>
|
||||
<div class="worker-status-summary" id="workerStatusSummary">
|
||||
<div class="status-item">
|
||||
<span class="status-label">총 작업시간</span>
|
||||
<span class="status-value" id="totalHours">0h</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">작업 건수</span>
|
||||
<span class="status-value" id="workCount">0건</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 메시지 영역 -->
|
||||
<div id="message-container"></div>
|
||||
|
||||
<!-- 기존 작업 목록 -->
|
||||
<div class="existing-work-section" id="existingWorkSection">
|
||||
<div class="section-header">
|
||||
<h3>📋 기존 작업 목록</h3>
|
||||
<button class="btn btn-primary" id="addNewWorkBtn">
|
||||
➕ 새 작업 추가
|
||||
</button>
|
||||
</div>
|
||||
<div id="existingWorkList">
|
||||
<!-- 기존 작업들이 여기에 표시됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 새 작업 추가 폼 -->
|
||||
<div class="new-work-section" id="newWorkSection" style="display: none;">
|
||||
<div class="section-header">
|
||||
<h3>➕ 새 작업 추가</h3>
|
||||
<button class="btn btn-secondary" id="cancelNewWorkBtn">
|
||||
✖️ 취소
|
||||
</button>
|
||||
</div>
|
||||
<div class="work-entry" id="newWorkEntry">
|
||||
<div class="work-entry-grid">
|
||||
<div class="form-field-group">
|
||||
<label class="form-field-label">
|
||||
<span class="form-field-icon">🏗️</span>
|
||||
프로젝트
|
||||
</label>
|
||||
<select id="newProjectSelect" class="form-select" required>
|
||||
<option value="">프로젝트를 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-field-group">
|
||||
<label class="form-field-label">
|
||||
<span class="form-field-icon">⚙️</span>
|
||||
작업 유형
|
||||
</label>
|
||||
<select id="newWorkTypeSelect" class="form-select" required>
|
||||
<option value="">작업 유형을 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field-group">
|
||||
<label class="form-field-label">
|
||||
<span class="form-field-icon">📊</span>
|
||||
업무 상태
|
||||
</label>
|
||||
<select id="newWorkStatusSelect" class="form-select" required>
|
||||
<option value="">업무 상태를 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="error-type-section" id="newErrorTypeSection">
|
||||
<label class="form-field-label">
|
||||
<span class="form-field-icon">⚠️</span>
|
||||
에러 유형
|
||||
</label>
|
||||
<select id="newErrorTypeSelect" class="form-select">
|
||||
<option value="">에러 유형을 선택하세요</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="time-input-section">
|
||||
<label class="form-field-label">
|
||||
<span class="form-field-icon">⏰</span>
|
||||
작업 시간 (시간)
|
||||
</label>
|
||||
<input type="number" id="newWorkHours" class="time-input" step="0.25" min="0.25" max="24" value="1.00" required>
|
||||
<div class="quick-time-buttons">
|
||||
<button type="button" class="quick-time-btn" data-hours="0.5">0.5h</button>
|
||||
<button type="button" class="quick-time-btn" data-hours="1">1h</button>
|
||||
<button type="button" class="quick-time-btn" data-hours="2">2h</button>
|
||||
<button type="button" class="quick-time-btn" data-hours="4">4h</button>
|
||||
<button type="button" class="quick-time-btn" data-hours="8">8h</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-success" id="saveNewWorkBtn">
|
||||
💾 작업 저장
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 휴가 처리 섹션 -->
|
||||
<div class="vacation-section" id="vacationSection">
|
||||
<div class="section-header">
|
||||
<h3>🏖️ 휴가 처리</h3>
|
||||
</div>
|
||||
<div class="vacation-buttons">
|
||||
<button class="btn btn-warning vacation-process-btn" data-type="full">
|
||||
🏖️ 연차 (8시간)
|
||||
</button>
|
||||
<button class="btn btn-warning vacation-process-btn" data-type="half-half">
|
||||
🌤️ 반반차 (6시간)
|
||||
</button>
|
||||
<button class="btn btn-warning vacation-process-btn" data-type="half">
|
||||
🌅 반차 (4시간)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 스크립트 -->
|
||||
<script src="/js/load-navbar.js"></script>
|
||||
<script src="/js/worker-individual-report.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user