feat(dispatch): 일괄 완료 버튼(모두 라벨부착/모두 Kagayaku 전달) + 검색바 축소

- 출고 작업 툴바 우측에 일괄 버튼 2개. 배치 내 전 박스 상태를 완료로 일괄 설정
- db.bulk_set_status: 변경된 박스만 로그(set 기반 INSERT), 화이트리스트 검증
- 라우터 POST /batches/{id}/bulk
- 검색 입력칸 폭 축소(flex 0 1 240px)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 15:29:55 +09:00
parent ef4658fb1d
commit a1dd9bed4a
3 changed files with 84 additions and 1 deletions
@@ -4,7 +4,9 @@
<style>
.dsp-toolbar { display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0; }
.dsp-filter { display:flex;gap:6px;flex-wrap:wrap; }
.dsp-search { flex:1 1 220px;min-width:180px; }
.dsp-search { flex:0 1 240px;min-width:150px; }
.dsp-bulk { margin-left:auto;display:flex;gap:8px;flex-wrap:wrap; }
.dsp-bulk .erp-btn:disabled { opacity:.6;cursor:wait; }
.dsp-cards { display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px; }
.dsp-card { border:1px solid #e2e8f0;border-radius:12px;padding:14px;background:#fff;display:flex;flex-direction:column;gap:10px; }
.dsp-card { position:relative; }
@@ -61,6 +63,10 @@
</div>
<input class="erp-input dsp-search" id="dsp-search" type="search"
placeholder="검색: Order ID / Tracking ID / 받는 사람 / SKU" />
<div class="dsp-bulk">
<button class="erp-btn erp-btn-outline" type="button" onclick="dspBulk(this,'label_attached')">모두 라벨 부착</button>
<button class="erp-btn erp-btn-outline" type="button" onclick="dspBulk(this,'handed_to_kagayaku')">모두 Kagayaku 전달</button>
</div>
</div>
<div class="dsp-cards" id="dsp-cards">
@@ -123,6 +129,27 @@
<script>
(function () {
var STATUS_FIELDS = {{ status_fields|list|tojson }};
var BATCH_ID = document.querySelector('.dsp').getAttribute('data-batch');
// ── 일괄 완료 처리 ──
var BULK_LABELS = { label_attached: '라벨 부착', handed_to_kagayaku: 'Kagayaku 전달' };
window.dspBulk = function (btn, field) {
if (!confirm('이 배치의 모든 박스를 "' + (BULK_LABELS[field] || field) + '" 완료로 표시할까요?')) return;
document.querySelectorAll('.dsp-bulk .erp-btn').forEach(function (b) { b.disabled = true; });
var body = new URLSearchParams();
body.set('field', field);
fetch('/dispatch/batches/' + BATCH_ID + '/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
})
.then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
.then(function () { location.reload(); })
.catch(function () {
alert('일괄 처리에 실패했습니다. 다시 시도하세요.');
document.querySelectorAll('.dsp-bulk .erp-btn').forEach(function (b) { b.disabled = false; });
});
};
// ── 상태 토글 (즉시 DB 저장) ──
window.dspToggle = function (btn) {