Files
tk-factory-services/system1-factory/web/pages/inspection/zone-detail.html
Hyungi Ahn d36303101e 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>
2026-02-24 08:20:50 +09:00

298 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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/admin-pages.css?v=8">
<link rel="stylesheet" href="/css/zone-detail.css?v=3">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/api-base.js"></script>
<script src="/js/app-init.js?v=3" defer></script>
</head>
<body>
<!-- 네비게이션 바 -->
<div id="navbar-container"></div>
<!-- 메인 레이아웃 -->
<div class="page-container">
<main class="main-content">
<div class="dashboard-main">
<!-- 페이지 헤더 -->
<div class="zone-header">
<div class="zone-header-left">
<button class="btn btn-back" onclick="goBack()">
<span></span> 돌아가기
</button>
</div>
<div class="zone-header-center">
<h1 id="zoneName" class="zone-title">작업장</h1>
<p id="zoneCategory" class="zone-subtitle">공장</p>
</div>
<div class="zone-header-right">
<span id="currentDate" class="current-date"></span>
</div>
</div>
<!-- 요약 카드 -->
<div id="summaryCards" class="summary-cards">
<!-- JS에서 렌더링 -->
</div>
<!-- 탭 네비게이션 -->
<div class="tab-navigation">
<button class="tab-btn active" data-tab="map" onclick="switchTab('map')">
🗺️ 구역 현황
</button>
<button class="tab-btn" data-tab="issues" onclick="switchTab('issues')">
🚨 안전신고/부적합
</button>
<button class="tab-btn" data-tab="equipment" onclick="switchTab('equipment')">
⚙️ 설비/수리
</button>
<button class="tab-btn" data-tab="visits" onclick="switchTab('visits')">
🚶 출입현황
</button>
<button class="tab-btn" data-tab="tbm" onclick="switchTab('tbm')">
📋 TBM
</button>
<button class="tab-btn" data-tab="patrol" onclick="switchTab('patrol')">
🔍 순회점검
</button>
</div>
<!-- 탭 콘텐츠 -->
<div class="tab-contents">
<!-- 구역 현황 탭 -->
<div id="tab-map" class="tab-content active">
<div class="map-editor-section">
<div class="map-editor-header">
<h3>구역 현황</h3>
<div class="map-editor-actions">
<button class="btn btn-primary btn-sm" id="addItemBtn" onclick="startAddItem()">
현황 등록
</button>
</div>
</div>
<div class="map-editor-container">
<div id="zoneMapContainer" class="zone-map-container">
<div class="map-placeholder">지도를 로딩 중...</div>
</div>
<div class="map-legend">
<div class="legend-title">주의 수준</div>
<div class="legend-items">
<div class="legend-item"><span class="legend-color" style="background: #10b981;"></span> 양호</div>
<div class="legend-item"><span class="legend-color" style="background: #f59e0b;"></span> 주의</div>
<div class="legend-item"><span class="legend-color" style="background: #ef4444;"></span> 관리필요</div>
</div>
<div class="legend-title" style="margin-top: 1rem;">설비 상태</div>
<div class="legend-items">
<div class="legend-item"><span style="margin-right: 4px;">⚙️</span> 정상 가동</div>
<div class="legend-item"><span style="margin-right: 4px;">🔧</span> 수리 필요</div>
<div class="legend-item"><span style="margin-right: 4px;">⚠️</span> 점검중</div>
<div class="legend-item"><span style="margin-right: 4px;">📤</span> 타 작업장 이동</div>
<div class="legend-item"><span style="margin-right: 4px;">📥</span> 임시 배치</div>
</div>
</div>
</div>
<div id="zoneItemsList" class="zone-items-list">
<!-- JS에서 렌더링 -->
</div>
</div>
</div>
<!-- 안전신고/부적합 탭 -->
<div id="tab-issues" class="tab-content">
<div id="issuesContent" class="content-loading">
<div class="loading-spinner"></div>
<p>로딩 중...</p>
</div>
</div>
<!-- 설비/수리 탭 -->
<div id="tab-equipment" class="tab-content">
<div id="equipmentContent" class="content-loading">
<div class="loading-spinner"></div>
<p>로딩 중...</p>
</div>
</div>
<!-- 출입현황 탭 -->
<div id="tab-visits" class="tab-content">
<div id="visitsContent" class="content-loading">
<div class="loading-spinner"></div>
<p>로딩 중...</p>
</div>
</div>
<!-- TBM 탭 -->
<div id="tab-tbm" class="tab-content">
<div id="tbmContent" class="content-loading">
<div class="loading-spinner"></div>
<p>로딩 중...</p>
</div>
</div>
<!-- 순회점검 탭 -->
<div id="tab-patrol" class="tab-content">
<div id="patrolContent" class="content-loading">
<div class="loading-spinner"></div>
<p>로딩 중...</p>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- 현황 등록/수정 모달 -->
<div id="zoneItemModal" class="modal-overlay" style="display: none;">
<div class="modal-container" style="max-width: 520px;">
<div class="modal-header">
<h2 id="zoneItemModalTitle">현황 등록</h2>
<button class="btn-close" onclick="closeZoneItemModal()">&times;</button>
</div>
<div class="modal-body">
<form id="zoneItemForm">
<input type="hidden" id="zoneItemId">
<input type="hidden" id="zoneItemX">
<input type="hidden" id="zoneItemY">
<input type="hidden" id="zoneItemWidth">
<input type="hidden" id="zoneItemHeight">
<!-- 프로젝트 여부 -->
<div class="form-group">
<label>프로젝트 여부 *</label>
<div class="radio-group">
<label class="radio-label">
<input type="radio" name="zoneItemProjectType" value="project" onchange="onProjectTypeChange(this.value)">
<span class="radio-text">프로젝트</span>
</label>
<label class="radio-label">
<input type="radio" name="zoneItemProjectType" value="non_project" onchange="onProjectTypeChange(this.value)" checked>
<span class="radio-text">프로젝트 아님</span>
</label>
<label class="radio-label">
<input type="radio" name="zoneItemProjectType" value="unknown" onchange="onProjectTypeChange(this.value)">
<span class="radio-text">판단 못함</span>
</label>
</div>
</div>
<!-- 프로젝트 선택 (프로젝트일 경우만 표시) -->
<div class="form-group" id="projectSelectGroup" style="display: none;">
<label for="zoneItemProject">프로젝트 선택</label>
<select id="zoneItemProject" class="form-control">
<option value="">프로젝트를 선택하세요</option>
<!-- JS에서 동적 로드 -->
</select>
</div>
<!-- 명칭 -->
<div class="form-group">
<label for="zoneItemName">명칭 *</label>
<input type="text" id="zoneItemName" class="form-control" placeholder="예: A사 제품, 작업 자재, 이동 설비" required>
</div>
<!-- 상태/유형 + 주의수준 -->
<div class="form-row">
<div class="form-group" style="flex: 1.5;">
<label for="zoneItemType">상태/유형</label>
<div class="select-with-add">
<select id="zoneItemType" class="form-control">
<option value="working">작업중</option>
<option value="temp_storage">임시적치</option>
<option value="moved_equipment">이동설비</option>
<option value="unreported">미신고품</option>
</select>
<button type="button" class="btn-add-option" onclick="addCustomType()" title="유형 추가">+</button>
</div>
</div>
<div class="form-group" style="flex: 1;">
<label for="zoneItemWarning">주의 수준</label>
<select id="zoneItemWarning" class="form-control">
<option value="good">양호</option>
<option value="caution">주의</option>
<option value="needs_management">관리필요</option>
</select>
</div>
</div>
<!-- 상세 설명 -->
<div class="form-group">
<label for="zoneItemDesc">상세 설명</label>
<textarea id="zoneItemDesc" class="form-control" rows="2" placeholder="현황에 대한 상세 설명, 주의사항, 담당자 등"></textarea>
</div>
<!-- 사진 등록 -->
<div class="form-group">
<label>사진</label>
<div class="photo-upload-area">
<input type="file" id="zoneItemPhoto" accept="image/*" multiple onchange="onPhotoSelected(event)" style="display: none;">
<div id="photoPreviewList" class="photo-preview-list">
<!-- 미리보기 이미지들 -->
</div>
<button type="button" class="btn-add-photo" onclick="document.getElementById('zoneItemPhoto').click()">
<span class="photo-icon">📷</span>
<span>사진 추가</span>
</button>
</div>
</div>
<!-- 표시 색상 -->
<div class="form-group">
<label>표시 색상</label>
<div class="color-picker-row">
<input type="color" id="zoneItemColor" class="form-control color-input" value="#3b82f6">
<div class="color-presets">
<button type="button" class="color-preset" style="background: #10b981;" onclick="setItemColor('#10b981')" title="양호"></button>
<button type="button" class="color-preset" style="background: #f59e0b;" onclick="setItemColor('#f59e0b')" title="주의"></button>
<button type="button" class="color-preset" style="background: #ef4444;" onclick="setItemColor('#ef4444')" title="관리필요"></button>
<button type="button" class="color-preset" style="background: #3b82f6;" onclick="setItemColor('#3b82f6')" title="기본"></button>
<button type="button" class="color-preset" style="background: #8b5cf6;" onclick="setItemColor('#8b5cf6')" title="기타"></button>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeZoneItemModal()">취소</button>
<button type="button" class="btn btn-danger" id="deleteZoneItemBtn" onclick="deleteZoneItem()" style="display: none;">삭제</button>
<button type="button" class="btn btn-primary" onclick="saveZoneItem()">저장</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script type="module">
import '/js/api-config.js?v=3';
</script>
<script>
(function() {
const checkApiConfig = setInterval(() => {
if (window.API_BASE_URL) {
clearInterval(checkApiConfig);
axios.defaults.baseURL = window.API_BASE_URL;
const token = localStorage.getItem('sso_token');
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
alert('세션이 만료되었습니다. 다시 로그인해주세요.');
window.location.href = '/pages/login.html';
}
return Promise.reject(error);
}
);
}
}, 50);
})();
</script>
<script src="/js/zone-detail.js?v=6"></script>
</body>
</html>