feat: 안전 코드 tksafety 이관 + 사용자 관리 정리 + UI Tailwind 전환

Phase 1: tksafety에 출입신청/체크리스트 API·웹 추가, tkfb 안전 코드 삭제
Phase 2: 사용자 관리 페이지 삭제, API 축소, 알림 수신자 tkuser 이관
Phase 3: tkuser 권한 페이지 정의 업데이트
Phase 4: 전체 34개 페이지 Tailwind CSS + tkfb-core.js 전환,
         미사용 CSS 20개·인프라 JS 10개·템플릿·컴포넌트 삭제

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-13 10:46:22 +09:00
parent 8373fe9e75
commit 9fda89a374
133 changed files with 5255 additions and 26181 deletions

View File

@@ -3,129 +3,141 @@
<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">
<title>일일순회점검 - TK 공장관리</title>
<link rel="stylesheet" href="/css/daily-patrol.css?v=4">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/api-base.js?v=3"></script>
<script src="/js/app-init.js?v=9" defer></script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="/static/css/tkfb.css">
</head>
<body>
<!-- 네비게이션 바 -->
<div id="navbar-container"></div>
<!-- 메인 레이아웃 -->
<div class="page-container">
<main class="main-content">
<div class="dashboard-main">
<!-- 페이지 헤더 -->
<div class="page-header">
<div class="page-title-section">
<h1 class="page-title">일일순회점검</h1>
<p class="page-description">작업장을 순회하며 안전 및 정리정돈 상태를 점검합니다</p>
</div>
</div>
<!-- 점검 시작 영역 -->
<div class="patrol-start-section">
<!-- 오늘 점검 현황 요약 -->
<div id="todayStatusSummary" class="today-status-summary">
<!-- JS에서 렌더링 -->
</div>
<button type="button" class="btn btn-primary btn-lg" id="startPatrolBtn" onclick="showFactorySelection()">
<span class="btn-icon"></span> 순회점검 시작
</button>
</div>
<!-- 공장 선택 영역 (점검 시작 후 표시) -->
<div id="factorySelectionArea" class="factory-selection-area" style="display: none;">
<div class="factory-selection-header">
<h3>공장을 선택하세요</h3>
<p class="factory-selection-subtitle" id="patrolSessionInfo"><!-- JS에서 렌더링 --></p>
</div>
<div id="factoryCardsContainer" class="factory-cards-container">
<!-- JS에서 공장 카드 렌더링 -->
</div>
</div>
<!-- 점검 진행 영역 (세션 시작 후 표시) -->
<div id="patrolArea" class="patrol-area" style="display: none;">
<!-- 세션 정보 -->
<div id="sessionInfo" class="session-info-bar">
<!-- JS에서 렌더링 -->
</div>
<!-- 지도 및 체크리스트 영역 -->
<div class="patrol-content">
<!-- 작업장 지도 (좌측) -->
<div class="patrol-map-section">
<div class="map-header">
<h3>작업장 지도</h3>
<div class="map-legend">
<span class="legend-item completed"><span class="dot"></span> 점검완료</span>
<span class="legend-item in-progress"><span class="dot"></span> 점검중</span>
<span class="legend-item pending"><span class="dot"></span> 미점검</span>
<body class="bg-gray-50">
<header class="bg-orange-700 text-white sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-14">
<div class="flex items-center gap-3">
<button id="mobileMenuBtn" class="lg:hidden text-orange-200 hover:text-white"><i class="fas fa-bars text-xl"></i></button>
<i class="fas fa-industry text-xl text-orange-200"></i>
<h1 class="text-lg font-semibold">TK 공장관리</h1>
</div>
</div>
<div id="patrolMapContainer" class="patrol-map-container">
<!-- 지도 이미지 및 작업장 마커 -->
</div>
<!-- 작업장 목록 (지도 대신 사용 가능) -->
<div id="workplaceListContainer" class="workplace-list-container">
<!-- 작업장 목록 -->
</div>
</div>
<!-- 체크리스트 영역 (우측) -->
<div class="patrol-checklist-section">
<div id="checklistHeader" class="checklist-header">
<h3>체크리스트</h3>
<p class="checklist-subtitle">작업장을 선택하면 체크리스트가 표시됩니다</p>
</div>
<div id="checklistContent" class="checklist-content">
<!-- 체크리스트 항목들 -->
<div class="checklist-placeholder">
<p>좌측 지도에서 점검할 작업장을 선택해주세요</p>
<div class="flex items-center gap-4">
<span id="headerUserName" class="text-sm hidden sm:block">-</span>
<div id="headerUserAvatar" class="w-8 h-8 bg-orange-600 rounded-full flex items-center justify-center text-sm font-bold">-</div>
<button onclick="doLogout()" class="text-orange-200 hover:text-white" title="로그아웃"><i class="fas fa-sign-out-alt"></i></button>
</div>
</div>
<div id="checklistActions" class="checklist-actions" style="display: none;">
<button type="button" class="btn btn-secondary" onclick="saveChecklistDraft()">임시저장</button>
<button type="button" class="btn btn-primary" onclick="saveChecklist()">저장 후 다음</button>
</div>
</div>
</div>
<!-- 물품 현황 영역 -->
<div id="itemsSection" class="items-section" style="display: none;">
<div class="items-header">
<h3><span id="selectedWorkplaceName">작업장</span> 물품 현황</h3>
<button type="button" class="btn btn-sm btn-outline" onclick="toggleItemEditMode()">
<span id="itemEditModeText">편집모드</span>
</button>
</div>
<div id="itemsMapContainer" class="items-map-container">
<!-- 작업장 상세 지도 및 물품 마커 -->
</div>
<div id="itemsLegend" class="items-legend">
<!-- 물품 유형 범례 -->
</div>
</div>
<!-- 순회점검 완료 버튼 -->
<div class="patrol-complete-section">
<div class="form-group">
<label for="patrolNotes">특이사항</label>
<textarea id="patrolNotes" class="form-control" rows="2" placeholder="순회 중 발견한 특이사항을 기록하세요..."></textarea>
</div>
<button type="button" class="btn btn-success btn-lg" onclick="completePatrol()">
순회점검 완료
</button>
</div>
</div>
</div>
</main>
</div>
</header>
<div id="mobileOverlay" class="hidden fixed inset-0 bg-black/50 z-30 lg:hidden"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 fade-in">
<div class="flex gap-6">
<nav id="sideNav" class="hidden lg:flex flex-col gap-1 w-52 flex-shrink-0 pt-2 fixed lg:static z-40 bg-white lg:bg-transparent p-4 lg:p-0 rounded-lg lg:rounded-none shadow-lg lg:shadow-none top-14 left-0 bottom-0 overflow-y-auto"></nav>
<div class="flex-1 min-w-0">
<!-- 페이지 헤더 -->
<div class="page-header">
<div class="page-title-section">
<h1 class="page-title">일일순회점검</h1>
<p class="page-description">작업장을 순회하며 안전 및 정리정돈 상태를 점검합니다</p>
</div>
</div>
<!-- 점검 시작 영역 -->
<div class="patrol-start-section">
<!-- 오늘 점검 현황 요약 -->
<div id="todayStatusSummary" class="today-status-summary">
<!-- JS에서 렌더링 -->
</div>
<button type="button" class="btn btn-primary btn-lg" id="startPatrolBtn" onclick="showFactorySelection()">
<span class="btn-icon"></span> 순회점검 시작
</button>
</div>
<!-- 공장 선택 영역 (점검 시작 후 표시) -->
<div id="factorySelectionArea" class="factory-selection-area" style="display: none;">
<div class="factory-selection-header">
<h3>공장을 선택하세요</h3>
<p class="factory-selection-subtitle" id="patrolSessionInfo"><!-- JS에서 렌더링 --></p>
</div>
<div id="factoryCardsContainer" class="factory-cards-container">
<!-- JS에서 공장 카드 렌더링 -->
</div>
</div>
<!-- 점검 진행 영역 (세션 시작 후 표시) -->
<div id="patrolArea" class="patrol-area" style="display: none;">
<!-- 세션 정보 -->
<div id="sessionInfo" class="session-info-bar">
<!-- JS에서 렌더링 -->
</div>
<!-- 지도 및 체크리스트 영역 -->
<div class="patrol-content">
<!-- 작업장 지도 (좌측) -->
<div class="patrol-map-section">
<div class="map-header">
<h3>작업장 지도</h3>
<div class="map-legend">
<span class="legend-item completed"><span class="dot"></span> 점검완료</span>
<span class="legend-item in-progress"><span class="dot"></span> 점검중</span>
<span class="legend-item pending"><span class="dot"></span> 미점검</span>
</div>
</div>
<div id="patrolMapContainer" class="patrol-map-container">
<!-- 지도 이미지 및 작업장 마커 -->
</div>
<!-- 작업장 목록 (지도 대신 사용 가능) -->
<div id="workplaceListContainer" class="workplace-list-container">
<!-- 작업장 목록 -->
</div>
</div>
<!-- 체크리스트 영역 (우측) -->
<div class="patrol-checklist-section">
<div id="checklistHeader" class="checklist-header">
<h3>체크리스트</h3>
<p class="checklist-subtitle">작업장을 선택하면 체크리스트가 표시됩니다</p>
</div>
<div id="checklistContent" class="checklist-content">
<!-- 체크리스트 항목들 -->
<div class="checklist-placeholder">
<p>좌측 지도에서 점검할 작업장을 선택해주세요</p>
</div>
</div>
<div id="checklistActions" class="checklist-actions" style="display: none;">
<button type="button" class="btn btn-secondary" onclick="saveChecklistDraft()">임시저장</button>
<button type="button" class="btn btn-primary" onclick="saveChecklist()">저장 후 다음</button>
</div>
</div>
</div>
<!-- 물품 현황 영역 -->
<div id="itemsSection" class="items-section" style="display: none;">
<div class="items-header">
<h3><span id="selectedWorkplaceName">작업장</span> 물품 현황</h3>
<button type="button" class="btn btn-sm btn-outline" onclick="toggleItemEditMode()">
<span id="itemEditModeText">편집모드</span>
</button>
</div>
<div id="itemsMapContainer" class="items-map-container">
<!-- 작업장 상세 지도 및 물품 마커 -->
</div>
<div id="itemsLegend" class="items-legend">
<!-- 물품 유형 범례 -->
</div>
</div>
<!-- 순회점검 완료 버튼 -->
<div class="patrol-complete-section">
<div class="form-group">
<label for="patrolNotes">특이사항</label>
<textarea id="patrolNotes" class="form-control" rows="2" placeholder="순회 중 발견한 특이사항을 기록하세요..."></textarea>
</div>
<button type="button" class="btn btn-success btn-lg" onclick="completePatrol()">
순회점검 완료
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 물품 추가/수정 모달 -->
<div id="itemModal" class="modal-overlay" style="display: none;">
@@ -197,6 +209,9 @@
}, 50);
})();
</script>
<script src="/static/js/tkfb-core.js"></script>
<script src="/js/api-base.js?v=3"></script>
<script src="/js/daily-patrol.js?v=6"></script>
<script>initAuth();</script>
</body>
</html>

View File

@@ -3,148 +3,160 @@
<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">
<title>구역 상세 - TK 공장관리</title>
<link rel="stylesheet" href="/css/zone-detail.css?v=4">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/api-base.js?v=3"></script>
<script src="/js/app-init.js?v=9" defer></script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="/static/css/tkfb.css">
</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>
<body class="bg-gray-50">
<header class="bg-orange-700 text-white sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-14">
<div class="flex items-center gap-3">
<button id="mobileMenuBtn" class="lg:hidden text-orange-200 hover:text-white"><i class="fas fa-bars text-xl"></i></button>
<i class="fas fa-industry text-xl text-orange-200"></i>
<h1 class="text-lg font-semibold">TK 공장관리</h1>
</div>
<div class="flex items-center gap-4">
<span id="headerUserName" class="text-sm hidden sm:block">-</span>
<div id="headerUserAvatar" class="w-8 h-8 bg-orange-600 rounded-full flex items-center justify-center text-sm font-bold">-</div>
<button onclick="doLogout()" class="text-orange-200 hover:text-white" title="로그아웃"><i class="fas fa-sign-out-alt"></i></button>
</div>
</div>
</div>
</header>
<div id="mobileOverlay" class="hidden fixed inset-0 bg-black/50 z-30 lg:hidden"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 fade-in">
<div class="flex gap-6">
<nav id="sideNav" class="hidden lg:flex flex-col gap-1 w-52 flex-shrink-0 pt-2 fixed lg:static z-40 bg-white lg:bg-transparent p-4 lg:p-0 rounded-lg lg:rounded-none shadow-lg lg:shadow-none top-14 left-0 bottom-0 overflow-y-auto"></nav>
<div class="flex-1 min-w-0">
<!-- 페이지 헤더 -->
<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 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()">
현황 등록
<!-- 탭 네비게이션 -->
<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>
<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 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 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 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>
<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>
<!-- 현황 등록/수정 모달 -->
<div id="zoneItemModal" class="modal-overlay" style="display: none;">
@@ -292,6 +304,9 @@
}, 50);
})();
</script>
<script src="/static/js/tkfb-core.js"></script>
<script src="/js/api-base.js?v=3"></script>
<script src="/js/zone-detail.js?v=6"></script>
<script>initAuth();</script>
</body>
</html>