feat: 모바일 UX 대폭 개선 + PWA 구현 + 로그인 루프 수정

- 모바일 하단 네비: 메뉴 제거, 4개 핵심 기능(홈/TBM/작업보고/출근) SVG 아이콘
- 모바일 사이드바 스킵: 768px 이하에서 사이드바 미로드, 레이아웃 오프셋 해결
- 모바일 헤더: 햄버거 메뉴 숨김, 본문 margin/overflow 정리
- TBM 모바일: 풀스크린 모달, 저장 버튼 하단 고정, 터치 UX 개선
- PWA: manifest.json, sw.js(network-first), 앱 아이콘, iOS 메타태그, 킬스위치
- 로그인 무한루프 수정: 토큰 만료 검증, 쿠키 정리, loginPage 경로 수정
- 신고 메뉴 tkreport 리다이렉트: navbar + sidebar cross-system-link 적용
- TBM API: 작업장별 안전점검 체크리스트 조회 엔드포인트 추가
- 안전점검 체크리스트 관리 UI 개선
- tkuser: 이슈유형 관리 기능 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-24 08:20:50 +09:00
parent 3cc29c03a8
commit d36303101e
60 changed files with 1418 additions and 270 deletions

View File

@@ -1,26 +1,37 @@
<!-- components/mobile-nav.html -->
<!-- 모바일 하단 네비게이션 -->
<!-- 모바일 하단 네비게이션 (4개 핵심 기능) -->
<nav class="mobile-bottom-nav" id="mobileBottomNav">
<a href="/pages/dashboard.html" class="mobile-nav-item" data-page="dashboard">
<span class="mobile-nav-icon">🏠</span>
<svg class="mobile-nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
<span class="mobile-nav-label"></span>
</a>
<a href="/pages/work/tbm.html" class="mobile-nav-item" data-page="tbm">
<span class="mobile-nav-icon">📋</span>
<svg class="mobile-nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 11l3 3L22 4"/>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
</svg>
<span class="mobile-nav-label">TBM</span>
</a>
<a href="/pages/work/report-create.html" class="mobile-nav-item" data-page="report">
<span class="mobile-nav-icon">📝</span>
<svg class="mobile-nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
<polyline points="10 9 9 9 8 9"/>
</svg>
<span class="mobile-nav-label">작업보고</span>
</a>
<a href="/pages/attendance/checkin.html" class="mobile-nav-item" data-page="checkin">
<span class="mobile-nav-icon"></span>
<svg class="mobile-nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span class="mobile-nav-label">출근</span>
</a>
<button class="mobile-nav-item" id="mobileMoreBtn">
<span class="mobile-nav-icon"></span>
<span class="mobile-nav-label">메뉴</span>
</button>
</nav>
<style>
@@ -31,10 +42,10 @@
bottom: 0;
left: 0;
right: 0;
height: 64px;
height: 68px;
background: #ffffff;
border-top: 1px solid #e5e7eb;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
box-shadow: 0 -2px 12px rgba(0,0,0,0.08);
z-index: 1000;
padding-bottom: env(safe-area-inset-bottom);
}
@@ -46,9 +57,8 @@
justify-content: space-around;
}
/* 바디 패딩 추가 */
body {
padding-bottom: calc(64px + env(safe-area-inset-bottom)) !important;
padding-bottom: calc(68px + env(safe-area-inset-bottom)) !important;
}
}
@@ -60,76 +70,78 @@
flex: 1;
height: 100%;
text-decoration: none;
color: #6b7280;
color: #9ca3af;
background: none;
border: none;
font-family: inherit;
cursor: pointer;
transition: color 0.2s;
padding: 0.5rem;
padding: 0.5rem 0.25rem;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
position: relative;
transition: color 0.15s;
}
.mobile-nav-item:active {
background: #f3f4f6;
color: #6b7280;
}
.mobile-nav-item.active {
color: #2563eb;
}
.mobile-nav-icon {
font-size: 1.5rem;
line-height: 1;
margin-bottom: 0.25rem;
/* SVG 아이콘 */
.mobile-nav-svg {
width: 26px;
height: 26px;
margin-bottom: 4px;
transition: transform 0.15s;
}
.mobile-nav-label {
font-size: 0.6875rem;
font-weight: 500;
line-height: 1;
letter-spacing: -0.01em;
}
/* 활성 상태 */
.mobile-nav-item.active .mobile-nav-icon {
transform: scale(1.1);
.mobile-nav-item.active {
color: #2563eb;
}
.mobile-nav-item.active .mobile-nav-svg {
transform: scale(1.08);
stroke-width: 2.5;
}
.mobile-nav-item.active .mobile-nav-label {
font-weight: 600;
font-weight: 700;
}
/* 활성 인디케이터 점 */
.mobile-nav-item.active::before {
content: '';
position: absolute;
top: 4px;
width: 4px;
height: 4px;
border-radius: 50%;
background: #2563eb;
}
</style>
<script>
(function() {
// 현재 페이지 하이라이트
const currentPath = window.location.pathname;
const navItems = document.querySelectorAll('.mobile-nav-item[data-page]');
var currentPath = window.location.pathname;
var navItems = document.querySelectorAll('.mobile-nav-item[data-page]');
navItems.forEach(item => {
const href = item.getAttribute('href');
navItems.forEach(function(item) {
var href = item.getAttribute('href');
if (href && currentPath.includes(href.replace('/pages/', '').replace('.html', ''))) {
item.classList.add('active');
}
});
// 대시보드 페이지 체크
if (currentPath.includes('dashboard')) {
document.querySelector('[data-page="dashboard"]')?.classList.add('active');
}
// 더보기 버튼 - 사이드바 열기
const moreBtn = document.getElementById('mobileMoreBtn');
if (moreBtn) {
moreBtn.addEventListener('click', () => {
const sidebar = document.getElementById('sidebarNav');
const overlay = document.getElementById('sidebarOverlay');
if (sidebar) {
sidebar.classList.add('mobile-open');
overlay?.classList.add('show');
document.body.classList.add('sidebar-mobile-open');
}
});
var dashItem = document.querySelector('[data-page="dashboard"]');
if (dashItem) dashItem.classList.add('active');
}
})();
</script>

View File

@@ -56,7 +56,7 @@
<span class="btn-text">대시보드</span>
</a>
<a href="/pages/safety/report.html" class="report-btn">
<a href="https://tkreport.technicalkorea.net" class="report-btn">
<span class="btn-icon">&#9888;</span>
<span class="btn-text">신고</span>
</a>
@@ -739,12 +739,7 @@ body {
}
.mobile-menu-btn {
width: 32px;
height: 32px;
margin-right: 0.25rem;
font-size: 1.125rem;
background: rgba(255, 255, 255, 0.12);
border: none;
display: none !important;
}
.user-profile {

View File

@@ -139,7 +139,7 @@
<a href="/pages/admin/equipments.html" class="nav-item" data-page-key="admin.equipments">
<span class="nav-text">설비 관리</span>
</a>
<a href="/pages/admin/issue-categories.html" class="nav-item" data-page-key="admin.issue_categories">
<a href="#" class="nav-item cross-system-link admin-only" data-system="report" data-path="/pages/admin/issue-categories.html" data-page-key="admin.issue_categories">
<span class="nav-text">신고 카테고리 관리</span>
</a>
<a href="/pages/admin/attendance-report.html" class="nav-item" data-page-key="admin.attendance_report">