Files
tk-factory-services/system1-factory/web/pages/purchase/request-mobile.html
Hyungi Ahn cf75462380 feat(purchase): 소모품 신청 시스템 v2 — 모바일 최적화, 스마트 검색, 그룹화, 입고 알림
- 4단계 상태 플로우: pending → grouped → purchased → received
- 한국어 스마트 검색: 초성 매칭(ㅁㅈㄱ→면장갑), 별칭 테이블, 인메모리 캐시
- 모바일 전용 신청 페이지: 바텀시트 UI, FAB, 카드 리스트, 스크롤 페이지네이션
- 인라인 품목 등록: 미등록 품목 검색→등록→신청 단일 트랜잭션
- 관리자 그룹화: 체크박스 다중 선택, 구매 그룹(batch) 생성/일괄 구매/입고
- 입고 처리: 사진+보관위치 등록, 부분 입고 허용, batch 자동 상태 전환
- 알림: notifyHelper에 target_user_ids 추가, 구매진행중/입고완료 시 신청자 ntfy+push

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 09:21:20 +09:00

148 lines
7.7 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=2026040101">
<link rel="stylesheet" href="/css/purchase-mobile.css?v=2026040101">
<script src="https://cdn.jsdelivr.net/npm/heic2any@0.0.4/dist/heic2any.min.js"></script>
</head>
<body class="bg-gray-50">
<!-- 헤더 -->
<header class="bg-orange-700 text-white sticky top-0 z-50">
<div class="px-4 flex justify-between items-center h-12">
<div class="flex items-center gap-3">
<button id="mobileMenuBtn" class="text-orange-200 hover:text-white"><i class="fas fa-bars text-lg"></i></button>
<h1 class="text-base font-semibold">소모품 신청</h1>
</div>
<div class="flex items-center gap-2">
<span id="headerUserName" class="text-sm text-orange-200"></span>
</div>
</div>
</header>
<!-- 사이드 네비 -->
<nav id="sideNav" class="fixed inset-y-0 left-0 w-64 bg-white shadow-xl z-40 transform -translate-x-full transition-transform duration-200" style="top:48px"></nav>
<div id="mobileOverlay" class="fixed inset-0 bg-black/40 z-30 hidden" onclick="toggleMobileMenu()"></div>
<div class="pm-content">
<!-- 상태 탭 -->
<div class="pm-tabs" id="statusTabs">
<button class="pm-tab active" data-status="" onclick="filterByStatus(this)">전체</button>
<button class="pm-tab" data-status="pending" onclick="filterByStatus(this)">대기</button>
<button class="pm-tab" data-status="grouped" onclick="filterByStatus(this)">구매진행중</button>
<button class="pm-tab" data-status="purchased" onclick="filterByStatus(this)">구매완료</button>
<button class="pm-tab" data-status="received" onclick="filterByStatus(this)">입고완료</button>
</div>
<!-- 카드 리스트 -->
<div class="pm-cards" id="requestCards"></div>
<div class="pm-loading hidden" id="loadingMore">더 불러오는 중...</div>
</div>
<!-- FAB -->
<button class="pm-fab" onclick="openRequestSheet()"><i class="fas fa-plus"></i></button>
<!-- 신청 바텀시트 -->
<div class="pm-overlay" id="requestOverlay" onclick="closeRequestSheet()"></div>
<div class="pm-sheet" id="requestSheet">
<div class="pm-sheet-handle"></div>
<div class="pm-sheet-header">
<span class="pm-sheet-title">소모품 신청</span>
<button class="pm-sheet-close" onclick="closeRequestSheet()"><i class="fas fa-times"></i></button>
</div>
<div class="pm-sheet-body">
<!-- 검색 -->
<div class="pm-search-wrap">
<input type="text" id="searchInput" class="pm-search-input" placeholder="품목 검색 (이름, 초성, 별칭)">
<div class="pm-search-spinner" id="searchSpinner"><i class="fas fa-spinner text-orange-500"></i></div>
</div>
<div class="pm-search-results" id="searchResults"></div>
<!-- 선택된 품목 표시 -->
<div id="selectedItemWrap" class="hidden mb-3">
<div class="flex items-center gap-2 p-3 bg-orange-50 rounded-lg">
<div class="flex-1">
<div id="selectedItemName" class="font-medium text-sm text-gray-800"></div>
<div id="selectedItemMeta" class="text-xs text-gray-500 mt-1"></div>
</div>
<button onclick="clearSelectedItem()" class="text-gray-400 hover:text-red-500"><i class="fas fa-times-circle"></i></button>
</div>
</div>
<input type="hidden" id="selectedItemId">
<input type="hidden" id="selectedCustomName">
<!-- 신규 품목 등록 영역 (Phase 4에서 활성화) -->
<div id="newItemForm" class="hidden">
<div class="p-3 bg-yellow-50 border border-yellow-200 rounded-lg mb-3">
<div class="text-sm font-medium text-yellow-800 mb-2"><i class="fas fa-plus-circle mr-1"></i>새 품목 등록</div>
<div class="pm-field">
<label class="pm-label">품목명 *</label>
<input type="text" id="newItemName" class="pm-input" readonly>
</div>
<div class="grid grid-cols-2 gap-2">
<div class="pm-field">
<label class="pm-label">규격</label>
<input type="text" id="newItemSpec" class="pm-input" placeholder="예: 300mm">
</div>
<div class="pm-field">
<label class="pm-label">제조사</label>
<input type="text" id="newItemMaker" class="pm-input" placeholder="예: 3M">
</div>
</div>
<div class="pm-field">
<label class="pm-label">분류 (선택)</label>
<select id="newItemCategory" class="pm-select">
<option value="">나중에 분류</option>
<option value="consumable">소모품</option>
<option value="safety">안전용품</option>
<option value="repair">수선비</option>
<option value="equipment">설비</option>
</select>
</div>
</div>
</div>
<!-- 수량/메모/사진 -->
<div class="pm-field">
<label class="pm-label">수량 *</label>
<input type="number" id="reqQuantity" class="pm-input" value="1" min="1" inputmode="numeric">
</div>
<div class="pm-field">
<label class="pm-label">메모</label>
<input type="text" id="reqNotes" class="pm-input" placeholder="요청 사항">
</div>
<div class="pm-field">
<label class="pm-label">사진 첨부</label>
<label class="pm-photo-btn">
<i class="fas fa-camera"></i>
<span id="photoLabel">사진 촬영/선택</span>
<input type="file" id="reqPhotoInput" accept="image/*,.heic,.heif" capture="environment" class="hidden" onchange="onMobilePhotoSelected(this)">
</label>
<img id="reqPhotoPreview" class="pm-photo-preview hidden">
</div>
<button id="submitBtn" class="pm-submit" onclick="submitRequest()">신청하기</button>
</div>
</div>
<!-- 상세 바텀시트 -->
<div class="pm-overlay" id="detailOverlay" onclick="closeDetailSheet()"></div>
<div class="pm-sheet" id="detailSheet">
<div class="pm-sheet-handle"></div>
<div class="pm-sheet-header">
<span class="pm-sheet-title">신청 상세</span>
<button class="pm-sheet-close" onclick="closeDetailSheet()"><i class="fas fa-times"></i></button>
</div>
<div class="pm-sheet-body" id="detailContent"></div>
</div>
<script src="/static/js/tkfb-core.js?v=2026040101"></script>
<script src="/static/js/purchase-request-mobile.js?v=2026040101"></script>
<script src="/static/js/shared-bottom-nav.js?v=2026040101"></script>
</body>
</html>