feat: 작업보고서 시간 입력 UX 개선 - 터치 최적화
작업보고서 작성 페이지의 시간 입력을 모바일/터치 환경에 최적화 주요 변경사항: - 기존 number input → 큰 버튼 기반 팝오버 방식으로 전환 - 퀵 선택 버튼 5개 (30분, 1시간, 2시간, 4시간, 8시간) - ±30분 미세 조정 버튼 추가 - 터치 타겟 최소 48-64px로 확대 - "8시간 30분" 형식으로 직관적 표시 - TBM 작업보고 및 수동 입력 모두 적용 기술 구현: - Hidden input + display div 패턴으로 폼 호환성 유지 - 팝오버 오버레이 with ESC/클릭 외부 닫기 - CSS 애니메이션 추가 - 캐시 버스팅 (CSS v9, JS v24) 문서: - 개발 로그: 개발 log/2026-01-27-time-input-ux-improvement.md - 사용자 가이드: docs/guides/work-report-time-input-guide.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
<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?v=2">
|
||||
<link rel="stylesheet" href="/css/daily-work-report.css?v=9">
|
||||
<link rel="icon" type="image/png" href="/img/favicon.png">
|
||||
<script src="/js/auth-check.js" defer></script>
|
||||
</head>
|
||||
@@ -16,129 +16,38 @@
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="work-report-main">
|
||||
<!-- 뒤로가기 버튼 -->
|
||||
<a href="javascript:history.back()" class="back-button">
|
||||
← 뒤로가기
|
||||
</a>
|
||||
|
||||
<!-- 진행 단계 표시 -->
|
||||
<div class="progress-steps">
|
||||
<div class="progress-step active" id="progressStep1">
|
||||
<div class="step-circle">1</div>
|
||||
<div class="step-label">날짜 선택</div>
|
||||
</div>
|
||||
<div class="progress-step" id="progressStep2">
|
||||
<div class="step-circle">2</div>
|
||||
<div class="step-label">작업자 선택</div>
|
||||
</div>
|
||||
<div class="progress-step" id="progressStep3">
|
||||
<div class="step-circle">3</div>
|
||||
<div class="step-label">작업 입력</div>
|
||||
</div>
|
||||
<!-- 탭 메뉴 -->
|
||||
<div class="tab-menu" style="margin-bottom: var(--space-6);">
|
||||
<button class="tab-btn active" id="tbmReportTab" onclick="switchTab('tbm')">
|
||||
작업보고서 작성
|
||||
</button>
|
||||
<button class="tab-btn" id="completedReportTab" onclick="switchTab('completed')">
|
||||
작성 완료 보고서
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 메시지 영역 -->
|
||||
<div id="message-container"></div>
|
||||
|
||||
<!-- 1단계: 날짜 선택 -->
|
||||
<div id="step1" class="step-section active">
|
||||
<div class="step-header">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-title">작업 날짜 선택</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reportDate" class="form-label">작업 날짜를 선택하세요</label>
|
||||
<input type="date" id="reportDate" class="form-input" required>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="nextStep1">다음 단계 →</button>
|
||||
</div>
|
||||
|
||||
<!-- 2단계: 작업자 선택 -->
|
||||
<div id="step2" class="step-section">
|
||||
<div class="step-header">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-title">작업자 선택</div>
|
||||
</div>
|
||||
<div id="workerGrid" class="worker-grid">
|
||||
<!-- 작업자 카드들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="nextStep2" disabled>다음 단계 →</button>
|
||||
</div>
|
||||
|
||||
<!-- 3단계: 작업 내역 입력 -->
|
||||
<div id="step3" class="step-section">
|
||||
<div class="step-header">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-title">작업 내역 입력</div>
|
||||
</div>
|
||||
|
||||
<!-- 총 작업시간 표시 -->
|
||||
<div class="total-hours-display" id="totalHoursDisplay">
|
||||
총 작업시간: 0시간
|
||||
</div>
|
||||
|
||||
<!-- 작업 항목들 -->
|
||||
<div id="workEntriesList">
|
||||
<!-- 작업 항목들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
|
||||
<!-- 작업 추가 버튼 -->
|
||||
<button type="button" class="btn btn-secondary btn-block" id="addWorkBtn">
|
||||
➕ 작업 추가
|
||||
</button>
|
||||
|
||||
<!-- 저장 버튼 -->
|
||||
<button type="button" class="btn btn-success btn-block" id="submitBtn">
|
||||
💾 작업보고서 저장
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 📊 내가 입력한 당일 작업 현황 (수정/삭제 가능) -->
|
||||
<div class="step-section" id="dailyWorkersSection" style="display: none;">
|
||||
<div class="step-header">
|
||||
<div class="step-number">📊</div>
|
||||
<div class="step-title">내가 입력한 작업 현황</div>
|
||||
</div>
|
||||
<p style="color: var(--text-secondary); margin-bottom: var(--space-5);">
|
||||
✏️ 내가 입력한 작업만 표시되며, 각 작업을 <strong>수정</strong>하거나 <strong>삭제</strong>할 수 있습니다.
|
||||
</p>
|
||||
<div id="dailyWorkersContent">
|
||||
<!-- 작업자 현황이 여기에 표시됩니다 -->
|
||||
<!-- TBM 작업보고 섹션 -->
|
||||
<div id="tbmReportSection" class="step-section active">
|
||||
<!-- TBM 작업 목록 -->
|
||||
<div id="tbmWorkList">
|
||||
<!-- TBM 작업 항목들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 사용법 안내 -->
|
||||
<div class="step-section">
|
||||
<div class="step-header">
|
||||
<div class="step-number">📖</div>
|
||||
<div class="step-title">사용 가이드</div>
|
||||
<!-- 작성 완료 보고서 섹션 -->
|
||||
<div id="completedReportSection" class="step-section" style="display: none;">
|
||||
<!-- 날짜 선택 필터 -->
|
||||
<div class="form-group" style="max-width: 300px; margin-bottom: var(--space-5);">
|
||||
<label for="completedReportDate" class="form-label">조회 날짜</label>
|
||||
<input type="date" id="completedReportDate" class="form-input" onchange="loadCompletedReports()">
|
||||
</div>
|
||||
<div class="guide-grid">
|
||||
<div class="guide-item">
|
||||
<div class="guide-icon">📅</div>
|
||||
<strong>1단계</strong><br>
|
||||
작업 날짜 선택
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<div class="guide-icon">👤</div>
|
||||
<strong>2단계</strong><br>
|
||||
작업자 선택 (터치)
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<div class="guide-icon">🔧</div>
|
||||
<strong>3단계</strong><br>
|
||||
작업 내역 입력
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<div class="guide-icon">💾</div>
|
||||
<strong>완료</strong><br>
|
||||
저장하여 마무리
|
||||
</div>
|
||||
<div class="guide-item">
|
||||
<div class="guide-icon">✏️</div>
|
||||
<strong>관리</strong><br>
|
||||
입력한 작업 수정/삭제
|
||||
</div>
|
||||
|
||||
<!-- 완료된 보고서 목록 -->
|
||||
<div id="completedReportsList">
|
||||
<!-- 완료된 보고서들이 여기에 동적으로 추가됩니다 -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -164,9 +73,108 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 작업장소 선택 모달 (지도 기반) -->
|
||||
<div id="workplaceModal" class="modal-overlay" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1002; align-items: center; justify-content: center; overflow-y: auto; padding: 2rem 0;">
|
||||
<div class="modal-container" style="background: white; border-radius: 8px; max-width: 1000px; width: 90%; max-height: none; margin: auto; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column;">
|
||||
<div class="modal-header" style="padding: 1.5rem; border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h2 style="font-size: 1.25rem; font-weight: 600; color: #111827; margin: 0;">
|
||||
<span style="margin-right: 0.5rem;">🗺️</span>작업장소 선택
|
||||
</h2>
|
||||
<button class="modal-close" onclick="closeWorkplaceModal()" style="background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #6b7280; padding: 0; width: 2rem; height: 2rem; display: flex; align-items: center; justify-content: center; border-radius: 4px;">×</button>
|
||||
</div>
|
||||
<div class="modal-body" style="padding: 1.5rem; flex: 1; overflow-y: visible;">
|
||||
<!-- 1단계: 카테고리 선택 -->
|
||||
<div id="categorySelectionArea">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">
|
||||
<span style="margin-right: 0.5rem;">🏭</span>공장 선택
|
||||
</h3>
|
||||
<div id="workplaceCategoryList" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem;">
|
||||
<!-- 카테고리 버튼들 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2단계: 작업장 선택 (지도 + 리스트) -->
|
||||
<div id="workplaceSelectionArea" style="display: none; margin-top: 1.5rem;">
|
||||
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 0.75rem; color: #374151;">
|
||||
<span style="margin-right: 0.5rem;">📍</span>
|
||||
<span id="selectedCategoryTitle">작업장 선택</span>
|
||||
</h3>
|
||||
|
||||
<!-- 지도 기반 선택 영역 -->
|
||||
<div id="layoutMapArea" style="display: none; margin-bottom: 1.5rem; padding: 1rem; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 0.5rem;">
|
||||
<div style="font-size: 0.875rem; color: #6b7280; margin-bottom: 0.75rem;">
|
||||
<span style="margin-right: 0.25rem;">🗺️</span>
|
||||
지도에서 작업장을 클릭하여 선택하세요
|
||||
</div>
|
||||
<div style="text-align: center; position: relative; display: inline-block; max-width: 100%;">
|
||||
<canvas id="workplaceMapCanvas" style="max-width: 100%; border-radius: 0.5rem; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 리스트 선택 영역 -->
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
|
||||
<span style="font-size: 0.875rem; color: #6b7280;">
|
||||
<span style="margin-right: 0.25rem;">📋</span>
|
||||
리스트에서 선택
|
||||
</span>
|
||||
</div>
|
||||
<div id="workplaceListArea" style="display: flex; flex-direction: column; gap: 0.5rem; max-height: 200px; overflow-y: auto; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; background: white;">
|
||||
<!-- 작업장소 목록 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.75rem; justify-content: flex-end; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #e5e7eb;">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeWorkplaceModal()">취소</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmWorkplaceBtn" onclick="confirmWorkplaceSelection()" disabled>선택 완료</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 시간 선택 팝오버 -->
|
||||
<div id="timePickerOverlay" class="time-picker-overlay" style="display: none;" onclick="closeTimePicker()">
|
||||
<div class="time-picker-popup" onclick="event.stopPropagation()">
|
||||
<div class="time-picker-header">
|
||||
<h3 id="timePickerTitle">작업시간 선택</h3>
|
||||
<button class="time-picker-close" onclick="closeTimePicker()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="quick-time-grid">
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(0.5)">
|
||||
<span class="time-value">30분</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(1)">
|
||||
<span class="time-value">1시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(2)">
|
||||
<span class="time-value">2시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(4)">
|
||||
<span class="time-value">4시간</span>
|
||||
</button>
|
||||
<button type="button" class="time-btn" onclick="setTimeValue(8)">
|
||||
<span class="time-value">8시간</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="time-adjust-area">
|
||||
<span class="current-time-label">현재:</span>
|
||||
<strong id="currentTimeDisplay" class="current-time-value">0시간</strong>
|
||||
<div class="adjust-buttons">
|
||||
<button type="button" class="adjust-btn" onclick="adjustTime(-0.5)">-30분</button>
|
||||
<button type="button" class="adjust-btn" onclick="adjustTime(0.5)">+30분</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="confirm-btn" onclick="confirmTimeSelection()">확인</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 스크립트 -->
|
||||
<script type="module" src="/js/api-config.js?v=3"></script>
|
||||
<script type="module" src="/js/load-navbar.js?v=5"></script>
|
||||
<script type="module" src="/js/daily-work-report.js?v=11"></script>
|
||||
<script type="module" src="/js/daily-work-report.js?v=24"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user