From 26beab1ae709b25cf4b0cb33fbe80743c11d8f41 Mon Sep 17 00:00:00 2001 From: king Date: Sat, 29 Aug 2026 04:04:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(cupang):=20=EC=A0=84=EC=B2=B4=20=EB=B0=B0?= =?UTF-8?q?=EB=B6=84=20=EC=99=84=EB=A3=8C=20=EC=8B=9C=20=EB=B9=A8=EA=B0=84?= =?UTF-8?q?=20=EB=8F=84=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 모든 박스(제품별 + 혼합)를 센터에 담아 미배분 잔여가 0 이 되면 ② 요약의 "센터 배분" 칸에 빨간 "전체 배분 완료" 도장을 찍는다. 처음 완료될 때만 찍히는 애니메이션이 돌고, 하나라도 빼면 도장이 사라진다. 재계산하면 기록도 초기화된다. Co-Authored-By: Claude Opus 5 --- .../cupang/templates/cupang/box_calc.html | 26 ++++++++++++++----- .../cupang/templates/cupang/box_rules.html | 2 +- .../cupang/templates/cupang/centers.html | 2 +- .../cupang/templates/cupang/detail.html | 2 +- app/modules/cupang/templates/cupang/form.html | 2 +- .../cupang/templates/cupang/index.html | 2 +- .../cupang/templates/cupang/products.html | 2 +- app/static/cupang.css | 24 +++++++++++++++++ tests/js/box_calc_ui.test.js | 19 ++++++++++++++ 9 files changed, 69 insertions(+), 12 deletions(-) diff --git a/app/modules/cupang/templates/cupang/box_calc.html b/app/modules/cupang/templates/cupang/box_calc.html index d263e7d..339a4ed 100644 --- a/app/modules/cupang/templates/cupang/box_calc.html +++ b/app/modules/cupang/templates/cupang/box_calc.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
@@ -249,7 +249,8 @@ }); var had = Object.keys(alloc).some(function (k) { return alloc[k].length; }); alloc = {}; - stamped = {}; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다. + stamped = {}; + allStamped = false; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다. render(); msg.textContent = "계산 완료 (" + calc.results.length + "건)" + (had ? " — 센터 분배 초기화됨" : ""); }) @@ -312,10 +313,13 @@ return '배분 완료'; } - function kpi(label, value, hint) { + var allStamped = false; // 전체 배분 완료 도장이 이미 찍혀 있었는지 + + function kpi(label, value, hint, extra) { return '
' + esc(label) + "" + '' + esc(value) + "" + - '' + esc(hint) + "
"; + '' + esc(hint) + "" + + (extra || "") + ""; } function renderSummary() { @@ -342,9 +346,19 @@ }); var assigned = (totalProd + totalMix) - (remainProd + remainMix); + // 모든 박스를 센터에 배분하면 "센터 배분" 칸에 전체 완료 도장을 찍는다. + var totalBoxes = totalProd + totalMix; + var allDone = totalBoxes > 0 && (remainProd + remainMix) === 0; + var allStamp = ""; + if (allDone) { + allStamp = '전체 배분 완료'; + } + allStamped = allDone; + sumKpis.innerHTML = - kpi("총 박스", (totalProd + totalMix) + "박스", "제품별 " + totalProd + " + 혼합 " + totalMix) + - kpi("센터 배분", assigned + "박스", "③ 에 담긴 수량") + + kpi("총 박스", totalBoxes + "박스", "제품별 " + totalProd + " + 혼합 " + totalMix) + + kpi("센터 배분", assigned + "박스", "③ 에 담긴 수량", allStamp) + kpi("미배분 잔여", (remainProd + remainMix) + "박스", "자투리 " + leftoverUnits + "개 포함"); var ph = ""; diff --git a/app/modules/cupang/templates/cupang/box_rules.html b/app/modules/cupang/templates/cupang/box_rules.html index 1444663..bdc8375 100644 --- a/app/modules/cupang/templates/cupang/box_rules.html +++ b/app/modules/cupang/templates/cupang/box_rules.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/centers.html b/app/modules/cupang/templates/cupang/centers.html index 94b9de5..fc1cf95 100644 --- a/app/modules/cupang/templates/cupang/centers.html +++ b/app/modules/cupang/templates/cupang/centers.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/detail.html b/app/modules/cupang/templates/cupang/detail.html index 985bfbb..4585196 100644 --- a/app/modules/cupang/templates/cupang/detail.html +++ b/app/modules/cupang/templates/cupang/detail.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/form.html b/app/modules/cupang/templates/cupang/form.html index 82b994e..1610fdf 100644 --- a/app/modules/cupang/templates/cupang/form.html +++ b/app/modules/cupang/templates/cupang/form.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/index.html b/app/modules/cupang/templates/cupang/index.html index b6b0e9e..a9980cb 100644 --- a/app/modules/cupang/templates/cupang/index.html +++ b/app/modules/cupang/templates/cupang/index.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 1d5db97..6265fbb 100644 --- a/app/modules/cupang/templates/cupang/products.html +++ b/app/modules/cupang/templates/cupang/products.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/static/cupang.css b/app/static/cupang.css index 08340c9..ecd62dd 100644 --- a/app/static/cupang.css +++ b/app/static/cupang.css @@ -768,3 +768,27 @@ body.cpg-dragging { cursor: grabbing; user-select: none; } .cpg-box-card.is-done .cpg-box-bar { opacity: .4; } /* 도장은 카드가 흐려져도 또렷하게 유지 */ .cpg-sum-prod.is-done .cpg-stamp, .cpg-box-card.is-done .cpg-stamp { opacity: 1; } + +/* 전체 배분 완료 도장 (센터 배분 KPI 칸) */ +.cpg-kpi { position: relative; overflow: hidden; } +.cpg-stamp-all { + --stamp: var(--color-callout-red); + position: absolute; + top: 50%; + right: 10px; + margin-top: -14px; + padding: 4px 10px; + font-size: 13px; + letter-spacing: 2px; + border-width: 3px; + border-radius: 8px; + opacity: .9; + transform: rotate(-12deg); + box-shadow: inset 0 0 0 1px rgba(194, 43, 16, .25); +} +.cpg-stamp-all.is-new { animation: cpg-stamp-all-in .3s cubic-bezier(.2, 1.4, .4, 1); } +@keyframes cpg-stamp-all-in { + 0% { opacity: 0; transform: rotate(-12deg) scale(2.4); } + 60% { opacity: .95; transform: rotate(-12deg) scale(.92); } + 100% { opacity: .9; transform: rotate(-12deg) scale(1); } +} diff --git a/tests/js/box_calc_ui.test.js b/tests/js/box_calc_ui.test.js index 7ffcc9e..77190a3 100644 --- a/tests/js/box_calc_ui.test.js +++ b/tests/js/box_calc_ui.test.js @@ -217,6 +217,25 @@ function dragCardTo(card, target) { $("#cpg-dlg-ok").click(); check("클릭으로 담긴다", $$(".cpg-dist-item").length === 1); + // ── 전체 배분 완료 도장 ─────────────────────────── + check("잔여가 남아 있으면 전체 도장 없음", !$(".cpg-stamp-all")); + while ($(".cpg-sum-prod.is-draggable")) { // 제품 박스 전량 담기 + $(".cpg-sum-prod.is-draggable").click(); + $("#cpg-dlg-all").click(); + } + while ($(".cpg-box-card.is-draggable")) { // 혼합 박스까지 담기 + $(".cpg-box-card.is-draggable").click(); + $("#cpg-dlg-all").click(); + } + check("미배분 잔여 0", $$(".cpg-kpi")[2].querySelector(".cpg-kpi-value").textContent === "0박스", + $$(".cpg-kpi")[2].querySelector(".cpg-kpi-value").textContent); + check("전체 배분 완료 도장 표시", !!$(".cpg-stamp-all")); + check("전체 도장 위치 = 센터 배분 칸", !!$$(".cpg-kpi")[1].querySelector(".cpg-stamp-all")); + check("전체 도장 첫 등장은 애니메이션", $(".cpg-stamp-all").classList.contains("is-new")); + + $(".cpg-dist-del").click(); // 하나 빼면 + check("잔여가 생기면 전체 도장 제거", !$(".cpg-stamp-all")); + console.log("\n" + (fails.length ? "FAILED: " + fails.join(" | ") : "ALL PASS")); process.exit(fails.length ? 1 : 0); })();