feat(dispatch): 송장 클릭 시 클립보드 복사 + J&T 공식 페이지로

- J&T 를 공식 조회 페이지(jtexpress.my/tracking)로 되돌림
- 송장번호 클릭 시 클립보드에 자동 복사 후 새 탭 열기(공식 페이지에 붙여넣기)
  복사 완료 토스트 표시. Ninja Van 등 자동입력 되는 곳도 복사는 동작.
- 송장 링크 굵기 800 + 밑줄 2px 로 강조

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 19:17:43 +09:00
parent fe8a4f146e
commit 6141f0e38a
2 changed files with 34 additions and 4 deletions
+3 -2
View File
@@ -264,8 +264,9 @@ def courier_logo_url(provider: Any) -> str:
# 매핑 없는 배송사는 링크 없이 텍스트로 표시(courier_tracking_url 가 "" 반환).
_COURIER_TRACK_URL: dict[str, str] = {
"ninjavan": "https://www.ninjavan.co/en-my/tracking?id={t}",
# J&T MY 공식 딥링크는 송장 자동입력이 안 돼 17TRACK 으로(범용, #nums 자동조회).
"jnt": "https://t.17track.net/en#nums={t}",
# J&T MY 공식 페이지는 송장 자동입력이 안 된다 → 클릭 시 송장번호를 클립보드에
# 복사하고 공식 조회 페이지를 연다(직원이 붙여넣기). 복사는 템플릿 JS 가 처리.
"jnt": "https://www.jtexpress.my/tracking?billcode={t}",
"flash": "https://www.flashexpress.my/fle/tracking?se={t}",
"citylink": "https://www.citylinkexpress.com/tracking-result/?track_no={t}",
"poslaju": "https://track.pos.com.my/postal-services/quick-access/?track-trace&trackingNo03={t}",
@@ -16,8 +16,13 @@
.dsp-order { font-size:15px;line-height:1.3;color:#334155;word-break:break-all; }
.dsp-order b { display:block;font-size:14px;font-weight:700;color:#475569;letter-spacing:.5px; }
.dsp-order span { font-size:27px;font-weight:800;color:#0f172a;letter-spacing:.2px; }
.dsp-track-link { color:#2563eb;font-weight:700;text-decoration:underline;
.dsp-track-link { color:#2563eb;font-weight:800;text-decoration:underline;
text-decoration-thickness:2px;text-underline-offset:3px;cursor:pointer; }
.dsp-toast { position:fixed;left:50%;bottom:24px;transform:translateX(-50%);
background:#0f172a;color:#fff;padding:10px 18px;border-radius:9px;
font-size:14px;font-weight:600;opacity:0;pointer-events:none;
transition:opacity .18s;z-index:9999;box-shadow:0 4px 16px rgba(0,0,0,.25); }
.dsp-toast.show { opacity:.96; }
.dsp-items { list-style:none;margin:0;padding:8px 0;border-top:1px dashed #e2e8f0;border-bottom:1px dashed #e2e8f0; }
.dsp-items li { font-size:16px;font-weight:600;color:#0f172a;padding:2px 0; }
.dsp-items .qty { color:#2563eb; }
@@ -75,7 +80,7 @@
<div><b>Package</b> {{ p.package_id or '—' }}</div>
<div><b>Tracking</b>
{% set turl = p.tracking_id | courier_track(p.shipping_provider) %}
{% if turl %}<a class="dsp-track-link" href="{{ turl }}" target="_blank" rel="noopener noreferrer">{{ p.tracking_id }}</a>
{% if turl %}<a class="dsp-track-link" href="{{ turl }}" target="_blank" rel="noopener noreferrer" data-copy="{{ p.tracking_id }}">{{ p.tracking_id }}</a>
{% else %}{{ p.tracking_id or '—' }}{% endif %}
</div>
</div>
@@ -104,6 +109,7 @@
{% endif %}
</div>
</div>
<div class="dsp-toast" id="dsp-toast" role="status" aria-live="polite"></div>
</section>
{% endblock %}
@@ -185,6 +191,29 @@
});
document.getElementById('dsp-search').addEventListener('input', applyFilter);
// ── 송장번호 클릭 시 클립보드 복사(공식 페이지에 붙여넣기용) ──
var toast = document.getElementById('dsp-toast');
var toastTimer = null;
function showToast(msg) {
if (!toast) return;
toast.textContent = msg;
toast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toast.classList.remove('show'); }, 1600);
}
document.addEventListener('click', function (e) {
var a = e.target.closest('.dsp-track-link');
if (!a) return;
var num = (a.getAttribute('data-copy') || a.textContent || '').trim();
if (!num) return;
// 링크는 그대로 새 탭으로 열리고(preventDefault 안 함), 송장번호만 복사한다.
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(num)
.then(function () { showToast('송장번호 복사됨: ' + num); })
.catch(function () {});
}
});
})();
</script>
{% endblock %}