From 46d23a33233e95fed7e328962c601c0ff67ef379 Mon Sep 17 00:00:00 2001 From: king Date: Mon, 22 Jun 2026 15:33:37 +0900 Subject: [PATCH] =?UTF-8?q?feat(dispatch):=20=EC=9D=BC=EA=B4=84=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9D=84=20=ED=86=A0=EA=B8=80=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD(=EC=A0=84=EB=B6=80=20=EC=99=84=EB=A3=8C?= =?UTF-8?q?=EB=A9=B4=20=ED=95=B4=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모두 라벨부착/모두 Kagayaku 전달 버튼이 현재 상태를 보고 토글 · 전 박스 완료면 → 모두 해제, 아니면 → 모두 완료 - 버튼 색으로 현재 상태 표시(전부 완료 시 초록), 개별 토글/로드 시 동기화 - bulk 라우터에 value 파라미터 추가(완료/해제) Co-Authored-By: Claude Opus 4.8 --- app/modules/dispatch/router.py | 8 +++-- .../dispatch/templates/dispatch/detail.html | 32 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/modules/dispatch/router.py b/app/modules/dispatch/router.py index 5a863a6..30ce37c 100644 --- a/app/modules/dispatch/router.py +++ b/app/modules/dispatch/router.py @@ -508,19 +508,21 @@ async def parcels_bulk( request: Request, batch_id: int, field: str = Form(...), + value: str = Form("true"), user: dict[str, Any] = Depends(_require_user), ) -> JSONResponse: - """배치 내 모든 박스의 작업 상태 1개를 완료(TRUE)로 일괄 설정. JSON 반환.""" + """배치 내 모든 박스의 작업 상태 1개를 value(완료/해제)로 일괄 설정. JSON 반환.""" st = _store(request) if st is None: raise HTTPException(status_code=503, detail="dispatch_db 미설정") + target = str(value).strip().lower() in ("true", "1", "on", "yes") try: count = st.bulk_set_status( - batch_id=batch_id, field=field, value=True, worker_name=user.get("email", "") + batch_id=batch_id, field=field, value=target, worker_name=user.get("email", "") ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) - return JSONResponse({"ok": True, "field": field, "count": count}) + return JSONResponse({"ok": True, "field": field, "value": target, "count": count}) @router.get("/api/batches/{batch_id:int}/parcels") diff --git a/app/modules/dispatch/templates/dispatch/detail.html b/app/modules/dispatch/templates/dispatch/detail.html index 6baf7ed..9dc2a90 100644 --- a/app/modules/dispatch/templates/dispatch/detail.html +++ b/app/modules/dispatch/templates/dispatch/detail.html @@ -64,8 +64,8 @@
- - + +
@@ -131,13 +131,34 @@ 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 전달' }; + + function allOn(field) { + var cards = document.querySelectorAll('.dsp-card'); + if (!cards.length) return false; + return Array.prototype.every.call(cards, function (c) { + return c.getAttribute('data-' + field) === 'true'; + }); + } + + // 버튼 상태(초록/외곽선) 동기화. 전부 완료면 on. + function refreshBulkButtons() { + document.querySelectorAll('.dsp-bulk [data-bulk]').forEach(function (btn) { + var on = allOn(btn.getAttribute('data-bulk')); + btn.classList.toggle('erp-btn-primary', on); + btn.classList.toggle('erp-btn-outline', !on); + }); + } + window.dspBulk = function (btn, field) { - if (!confirm('이 배치의 모든 박스를 "' + (BULK_LABELS[field] || field) + '" 완료로 표시할까요?')) return; + var target = !allOn(field); // 전부 완료면 해제, 아니면 완료 + var verb = target ? '완료로 표시' : '완료 해제'; + if (!confirm('이 배치의 모든 박스를 "' + (BULK_LABELS[field] || field) + '" ' + verb + '할까요?')) return; document.querySelectorAll('.dsp-bulk .erp-btn').forEach(function (b) { b.disabled = true; }); var body = new URLSearchParams(); body.set('field', field); + body.set('value', target ? 'true' : 'false'); fetch('/dispatch/batches/' + BATCH_ID + '/bulk', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, @@ -181,6 +202,7 @@ }); card.classList.toggle('is-done', done); updateDoneLabel(); + refreshBulkButtons(); } function updateDoneLabel() { @@ -224,6 +246,8 @@ }); document.getElementById('dsp-search').addEventListener('input', applyFilter); + + refreshBulkButtons(); // 초기 버튼 상태(전부 완료면 초록) })(); {% endblock %}