- sw.js: Network-First 캐시 전략 (GET + same-origin + res.ok만 캐시) - tkfb-core.js: SW 등록 + 업데이트 감지 시 자동 새로고침 (최초 설치 시 토스트 방지: controller 체크) - manifest.json: start_url → dashboard-new.html - nginx: sw.js, manifest.json no-cache 헤더 - 배포 시 sw.js의 APP_VERSION만 변경하면 전 사용자 자동 갱신 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
123 lines
5.9 KiB
HTML
123 lines
5.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>대리입력 - TK 공장관리</title>
|
|
<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?v=2026040103">
|
|
<link rel="stylesheet" href="/css/proxy-input.css?v=2026033201">
|
|
</head>
|
|
<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">
|
|
|
|
<!-- ═══ STEP 1: 작업자 선택 ═══ -->
|
|
<div id="step1">
|
|
<div class="pi-title-row">
|
|
<h2 class="pi-title">대리입력</h2>
|
|
<div class="pi-date-group">
|
|
<input type="date" id="dateInput" class="pi-date-input" onchange="loadWorkers()">
|
|
<button class="pi-refresh-btn" onclick="loadWorkers()"><i class="fas fa-sync-alt"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pi-status-bar" id="statusBar">
|
|
<span>전체 <strong id="totalNum">0</strong></span>
|
|
<span>완료 <strong id="doneNum" class="text-green-600">0</strong></span>
|
|
<span>미입력 <strong id="missingNum" class="text-red-500">0</strong></span>
|
|
<span>휴가 <strong id="vacNum" class="text-blue-500">0</strong></span>
|
|
</div>
|
|
|
|
<div class="pi-select-all">
|
|
<label><input type="checkbox" id="selectAll" onchange="toggleSelectAll(this.checked)"> 전체 선택</label>
|
|
</div>
|
|
|
|
<div class="pi-worker-list" id="workerList">
|
|
<div class="pi-skeleton"></div>
|
|
<div class="pi-skeleton"></div>
|
|
<div class="pi-skeleton"></div>
|
|
</div>
|
|
|
|
<div class="pi-bottom-bar" id="editBar">
|
|
<button class="pi-edit-btn" id="editBtn" onclick="openEditMode()" disabled>
|
|
<i class="fas fa-pen mr-2"></i><span id="editBtnText">작업자를 선택하세요</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ STEP 2: 일괄 편집 ═══ -->
|
|
<div id="step2" class="hidden">
|
|
<div class="pi-title-row">
|
|
<button class="pi-back-btn" onclick="closeEditMode()"><i class="fas fa-arrow-left"></i></button>
|
|
<h2 class="pi-title" id="editTitle">일괄 편집</h2>
|
|
</div>
|
|
|
|
<div class="pi-bulk-form">
|
|
<div class="pi-edit-row">
|
|
<select id="bulkProject" class="pi-select" required></select>
|
|
<select id="bulkWorkType" class="pi-select" required></select>
|
|
</div>
|
|
<div class="pi-edit-row">
|
|
<label class="pi-field"><span>시간</span><input type="number" id="bulkHours" value="8" step="0.5" min="0" max="24" class="pi-input"></label>
|
|
<label class="pi-field"><span>부적합 시간</span><input type="number" id="bulkDefect" value="0" step="0.5" min="0" max="24" class="pi-input" onchange="onDefectChange()"></label>
|
|
</div>
|
|
<div id="defectCategoryRow" class="hidden">
|
|
<div class="pi-edit-row">
|
|
<select id="bulkDefectCategory" class="pi-select" onchange="onDefectCategoryChange()">
|
|
<option value="">부적합 대분류 *</option>
|
|
</select>
|
|
<select id="bulkDefectItem" class="pi-select">
|
|
<option value="">소분류 *</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<input type="text" id="bulkNote" placeholder="비고 (선택)" class="pi-note-input">
|
|
</div>
|
|
|
|
<div class="pi-target-section">
|
|
<div class="pi-target-label">적용 대상</div>
|
|
<div class="pi-target-list" id="targetWorkers"></div>
|
|
</div>
|
|
|
|
<div class="pi-bottom-bar">
|
|
<button class="pi-save-btn" id="saveBtn" onclick="saveAll()">
|
|
<i class="fas fa-save mr-2"></i><span id="saveBtnText">전체 저장</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div id="toastContainer" class="toast-container"></div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/static/js/tkfb-core.js?v=2026040105"></script>
|
|
<script src="/js/api-base.js?v=2026031701"></script>
|
|
<script src="/js/proxy-input.js?v=2026033202"></script>
|
|
<script>initAuth();</script>
|
|
</body>
|
|
</html>
|