diff --git a/app/modules/dispatch/router.py b/app/modules/dispatch/router.py index 9aa8148..31eb161 100644 --- a/app/modules/dispatch/router.py +++ b/app/modules/dispatch/router.py @@ -726,7 +726,13 @@ async def api_search( query = (q or "").strip() if len(query) < 2: return JSONResponse({"query": query, "results": []}) - return JSONResponse({"query": query, "results": st.search_parcels(query=query)}) + results = st.search_parcels(query=query) + # 송장 조회 딥링크는 배송사 맵(store)에서 만든다 — 프런트엔 맵이 없다. + for r in results: + r["tracking_url"] = store.courier_tracking_url( + r.get("tracking_id", ""), r.get("shipping_provider", "") + ) + return JSONResponse({"query": query, "results": results}) @router.get("/health") diff --git a/app/modules/dispatch/templates/dispatch/batches.html b/app/modules/dispatch/templates/dispatch/batches.html index 5c220df..2935422 100644 --- a/app/modules/dispatch/templates/dispatch/batches.html +++ b/app/modules/dispatch/templates/dispatch/batches.html @@ -29,16 +29,25 @@ .dsp-q-clear { position:absolute;right:8px;top:50%;transform:translateY(-50%);border:0;background:transparent;color:#94a3b8;font-size:18px;cursor:pointer;line-height:1;padding:0 4px;display:none; } .dsp-search-results { position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:40;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 8px 24px rgba(15,23,42,.12);max-height:64vh;overflow:auto;display:none;padding:6px; } .dsp-search-results.open { display:block; } - .dsp-hit { border-radius:8px;padding:9px 10px;display:flex;justify-content:space-between;gap:12px;align-items:flex-start; } + .dsp-hit { border-radius:8px;padding:11px 12px;display:flex;justify-content:space-between;gap:12px;align-items:flex-start; } .dsp-hit + .dsp-hit { border-top:1px solid #f1f5f9; } .dsp-hit:hover { background:#f8fafc; } - .dsp-hit-name { font-weight:700;font-size:14px;color:#0f172a; } - .dsp-hit-meta { font-size:12.5px;color:#475569;margin-top:2px;line-height:1.5; } - .dsp-hit-meta code { background:#f1f5f9;padding:1px 5px;border-radius:4px;font-size:12px; } + .dsp-hit-body { min-width:0; } + .dsp-hit-name { font-weight:700;font-size:17px;color:#0f172a; } + .dsp-hit-meta { font-size:14px;color:#475569;margin-top:3px;line-height:1.5; } + /* Order / Pkg / 송장 — 한 줄씩 */ + .dsp-id { font-size:14.5px;margin-top:4px;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap; } + .dsp-id-k { color:#94a3b8;font-size:12px;min-width:42px;flex:0 0 auto; } + .dsp-id code { background:#f1f5f9;padding:2px 7px;border-radius:5px;font-size:14px;color:#0f172a; } + .dsp-id-track code { font-weight:700; } + .dsp-track-lnk { text-decoration:none; } + .dsp-track-lnk code { color:#4f46e5;text-decoration:underline;cursor:pointer; } + .dsp-track-lnk:hover code { background:#e0e7ff; } + .dsp-courier { font-size:12.5px;font-weight:600;color:#475569;background:#eef2ff;border:1px solid #c7d2fe;border-radius:999px;padding:1px 9px; } .dsp-hit mark { background:#fde68a;padding:0 1px;border-radius:2px; } - .dsp-hit-batch { font-size:11.5px;color:#64748b;margin-top:3px; } + .dsp-hit-batch { font-size:13px;color:#475569;margin-top:5px; } .dsp-hit-go { white-space:nowrap;flex:0 0 auto;align-self:center; } - .dsp-hit-empty { color:#94a3b8;font-size:13.5px;padding:10px; } + .dsp-hit-empty { color:#94a3b8;font-size:14.5px;padding:11px; } {% endblock %} @@ -294,11 +303,26 @@ } function en() { return (window.dspLang && window.dspLang() === 'en'); } + function idLine(label, value, q) { + if (!value) return ''; + return '
' + label + '' + + '' + hi(value, q) + '
'; + } + function trackLine(r, q) { + if (!r.tracking_id) return ''; + var num = '' + hi(r.tracking_id, q) + ''; + // 송장번호 클릭 → 배송사 조회 페이지(딥링크 있으면). + var inner = r.tracking_url + ? '' + num + '' + : num; + var courier = r.shipping_provider + ? '' + esc(r.shipping_provider) + '' + : ''; + return '
' + (en() ? 'Track' : '송장') + '' + + inner + courier + '
'; + } + function row(r, q) { - var ids = []; - if (r.order_id) ids.push('Order ' + hi(r.order_id, q) + ''); - if (r.package_id) ids.push('Pkg ' + hi(r.package_id, q) + ''); - if (r.tracking_id) ids.push((en() ? 'Track ' : '송장 ') + '' + hi(r.tracking_id, q) + ''); var name = r.recipient_name ? hi(r.recipient_name, q) : esc(en() ? '(no recipient name)' : '(받는 사람 정보 없음)'); @@ -306,14 +330,17 @@ if (r.recipient_phone) contact.push(esc(r.recipient_phone)); if (r.recipient_address) contact.push(esc(r.recipient_address)); var done = r.label_attached && r.handed_to_kagayaku; - var batchLine = esc(r.dispatch_date) + ' · ' + esc(r.platform) + ' · ' + esc(r.batch_name || '') - + ' · ' + (en() ? 'Box #' : '박스 #') + esc(r.seq) - + (done ? ' · ' : ''); + // 날짜·쇼핑몰은 굵게. + var batchLine = '' + esc(r.dispatch_date) + ' · ' + esc(r.platform) + ' · ' + + esc(r.batch_name || '') + ' · ' + (en() ? 'Box #' : '박스 #') + esc(r.seq) + + (done ? ' · ' : ''); return '
' - + '
' + + '
' + '
' + name + '
' + (contact.length ? '
' + contact.join(' · ') + '
' : '') - + '
' + ids.join('   ') + '
' + + idLine('Order', r.order_id, q) + + idLine('Pkg', r.package_id, q) + + trackLine(r, q) + '
' + batchLine + '
' + '
' + ''