ba2478d461
글꼴 글리프는 두께 조절이 안 돼 stroke-width 5.5 SVG 로 대체 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
232 lines
9.5 KiB
HTML
232 lines
9.5 KiB
HTML
{% extends "erp_base.html" %}
|
||
|
||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903k" />{% endblock %}
|
||
|
||
{% block content %}
|
||
<section class="cpg">
|
||
|
||
<!-- 페이지 액션 (달력 컬럼 폭에 맞춤: 신규 좌측 / 설정 달력 오른쪽 끝) -->
|
||
<div class="cpg-actions-grid">
|
||
<div class="cpg-actions-main">
|
||
<a class="erp-btn erp-btn-primary" href="/cupang/box-calc">+ 신규 등록</a>
|
||
<span class="cpg-settings-btns">
|
||
<a class="erp-btn erp-btn-outline" href="/cupang/products">제품명 설정</a>
|
||
<a class="erp-btn erp-btn-outline" href="/cupang/box-rules">상자 입수량 설정</a>
|
||
</span>
|
||
</div>
|
||
<div class="cpg-actions-spacer"></div>
|
||
</div>
|
||
|
||
<div class="cpg-layout">
|
||
<!-- ── 왼쪽: 2개월 월간 달력 ── -->
|
||
<div class="erp-card cpg-cal-card">
|
||
<div class="cpg-mcal-bar">
|
||
<a class="cpg-mcal-navbtn" title="이전 달" aria-label="이전 달"
|
||
href="/cupang/?year={{ prev_y }}&month={{ prev_m }}">
|
||
<svg class="cpg-mcal-arrow" viewBox="0 0 28 24" aria-hidden="true">
|
||
<path d="M23 12 H8" /><path d="M14 5 L7 12 L14 19" />
|
||
</svg>
|
||
</a>
|
||
<h2 class="cpg-mcal-range">{{ months[0].label }} – {{ months[1].label }}</h2>
|
||
<a class="cpg-mcal-navbtn" title="다음 달" aria-label="다음 달"
|
||
href="/cupang/?year={{ next_y }}&month={{ next_m }}">
|
||
<svg class="cpg-mcal-arrow" viewBox="0 0 28 24" aria-hidden="true">
|
||
<path d="M5 12 H20" /><path d="M14 5 L21 12 L14 19" />
|
||
</svg>
|
||
</a>
|
||
</div>
|
||
|
||
<div class="cpg-mcal-months">
|
||
<a class="cpg-mcal-todaybtn" href="/cupang/">오늘</a>
|
||
{% for m in months %}
|
||
<section class="cpg-mcal{% if loop.first %} is-current{% endif %}">
|
||
<header class="cpg-mcal-h">
|
||
<span class="cpg-mcal-mname">{{ m.month }}월</span>
|
||
<span class="cpg-mcal-myear">{{ m.year }}</span>
|
||
{% if m.ship_total %}
|
||
<span class="cpg-mcal-total">출고 {{ m.ship_total }}건</span>
|
||
{% endif %}
|
||
</header>
|
||
|
||
<div class="cpg-mcal-grid">
|
||
{% for wd in weekdays %}
|
||
<div class="cpg-mcal-wd {% if loop.index0 == 0 %}is-sun{% elif loop.index0 == 6 %}is-sat{% endif %}">{{ wd }}</div>
|
||
{% endfor %}
|
||
|
||
{% for week in m.weeks %}
|
||
{% for cell in week %}
|
||
<a class="cpg-mcal-cell
|
||
{% if not cell.in_month %}is-out{% endif %}
|
||
{% if cell.is_today %}is-today{% endif %}
|
||
{% if cell.is_selected %}is-selected{% endif %}
|
||
{% if cell.counts.ship %}has-ship{% endif %}
|
||
{% if cell.is_sunday or cell.is_holiday %}is-red{% elif cell.is_saturday %}is-blue{% endif %}"
|
||
href="/cupang/?year={{ year }}&month={{ month }}&date={{ cell.date }}"
|
||
title="{{ cell.date }}{% if cell.holiday_name %} — {{ cell.holiday_name }}{% endif %}{% if cell.counts.ship %} — 출고 {{ cell.counts.ship }}건 / 센터 {{ cell.counts.centers }}곳{% endif %}">
|
||
<span class="cpg-mcal-day">{{ cell.day }}</span>
|
||
{% if cell.holiday_name %}
|
||
<span class="cpg-mcal-holi">{{ cell.holiday_name }}</span>
|
||
{% endif %}
|
||
{% if cell.counts.ship %}
|
||
<span class="cpg-mcal-tag">{{ cell.counts.ship }}건<em>{{ cell.counts.centers }}곳</em></span>
|
||
{% endif %}
|
||
</a>
|
||
{% endfor %}
|
||
{% endfor %}
|
||
</div>
|
||
</section>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
<div class="cpg-mcal-legend">
|
||
<span><i class="cpg-lg-dot"></i>출고 있는 날</span>
|
||
<span><i class="cpg-lg-today"></i>오늘</span>
|
||
<span><i class="cpg-lg-sel"></i>선택한 날</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── 오른쪽: 선택일 출고 묶음 리스트 ── -->
|
||
<div class="erp-card cpg-list-card">
|
||
<div class="cpg-list-head">
|
||
<h2>{{ selected_date }} 출고</h2>
|
||
<span class="erp-muted">{{ sel_shipments|length }}건</span>
|
||
{% if sel_shipments %}
|
||
<button type="button" class="erp-btn erp-btn-danger cpg-btn-sm cpg-day-del"
|
||
data-day-del="{{ selected_date }}" data-count="{{ sel_shipments|length }}">이 날짜 전체 삭제</button>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if sel_shipments %}
|
||
<ul class="cpg-list">
|
||
{% for s in sel_shipments %}
|
||
{# ③ 센터 분배 카드와 같은 형식 — 혼합 상자는 내용물까지 펼쳐 보여준다 #}
|
||
<li class="cpg-list-item cpg-dist-center has-items">
|
||
<div class="cpg-dist-head">
|
||
<a class="cpg-ship-name" href="/cupang/{{ s.id }}">{{ s.center_name_snapshot or '센터 미지정' }}</a>
|
||
<span class="erp-badge erp-badge-neutral">{{ s.ship_method }}</span>
|
||
<span class="cpg-dist-sum">
|
||
<span class="erp-badge erp-badge-neutral">{{ s.total_boxes }}상자</span>
|
||
<span class="erp-badge erp-badge-neutral">{{ s.total_qty }}개</span>
|
||
<button type="button" class="cpg-icon-btn is-danger" title="이 출고 삭제"
|
||
data-del="{{ s.id }}" data-name="{{ s.center_name_snapshot }}">삭제</button>
|
||
</span>
|
||
</div>
|
||
|
||
<ul class="cpg-dist-items">
|
||
{% if s.box_plan %}
|
||
{% for e in s.box_plan %}
|
||
<li class="cpg-dist-item{% if e.kind == 'mix' %} is-mix{% endif %}">
|
||
<div class="cpg-dist-line">
|
||
<span class="cpg-dist-name">{{ e.name }}</span>
|
||
<span class="cpg-dist-unit">{{ e.count }}상자 · {{ e.quantity }}개</span>
|
||
</div>
|
||
{% if e.kind == 'mix' and e['items'] %}
|
||
<ul class="cpg-dist-tree">
|
||
{% for it in e['items'] %}
|
||
<li><span class="cpg-tree-name">{{ it.product_name }}</span>
|
||
<span class="cpg-tree-qty">{{ it.quantity }}개</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
</li>
|
||
{% endfor %}
|
||
{% else %}
|
||
{# 예전에 만든 출고(상자 구성 미저장) — 제품 합계만 표시 #}
|
||
{% for it in s['items'] %}
|
||
<li class="cpg-dist-item">
|
||
<div class="cpg-dist-line">
|
||
<span class="cpg-dist-name">{{ it.name }}</span>
|
||
<span class="cpg-dist-unit">{{ it.boxes }}상자 · {{ it.qty }}개</span>
|
||
</div>
|
||
</li>
|
||
{% endfor %}
|
||
{% endif %}
|
||
</ul>
|
||
|
||
<div class="cpg-ship-meta erp-muted">출고 {{ s.ship_date }} · 센터입고 {{ s.center_arrival_date }}</div>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="erp-muted cpg-empty">선택한 날짜의 출고 묶음이 없습니다.
|
||
<a href="/cupang/box-calc">상자 계산에서 등록</a></p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 삭제 확인 팝업 -->
|
||
<div class="cpg-modal" id="cpg-del-dlg" hidden>
|
||
<div class="cpg-modal-back" data-del-close></div>
|
||
<div class="cpg-modal-box cpg-del-box" role="dialog" aria-modal="true" aria-labelledby="cpg-del-title">
|
||
<h3 id="cpg-del-title">출고 삭제</h3>
|
||
<p class="cpg-del-msg" id="cpg-del-msg"></p>
|
||
<div class="cpg-del-act">
|
||
<button type="button" class="erp-btn erp-btn-outline" data-del-close>취소</button>
|
||
<button type="button" class="erp-btn erp-btn-danger" id="cpg-del-ok">삭제</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<form method="post" id="cpg-del-form" hidden>
|
||
<input type="hidden" name="date" id="cpg-del-date" />
|
||
<input type="hidden" name="next" id="cpg-del-next" />
|
||
</form>
|
||
|
||
</section>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
(function () {
|
||
var dlg = document.getElementById("cpg-del-dlg");
|
||
if (!dlg) return;
|
||
var msgEl = document.getElementById("cpg-del-msg");
|
||
var okBtn = document.getElementById("cpg-del-ok");
|
||
var form = document.getElementById("cpg-del-form");
|
||
var dateEl = document.getElementById("cpg-del-date");
|
||
var nextEl = document.getElementById("cpg-del-next");
|
||
var pending = null; // {action, date}
|
||
|
||
function close() { dlg.hidden = true; pending = null; }
|
||
|
||
function open(message, action, day) {
|
||
msgEl.textContent = message;
|
||
pending = { action: action, date: day || "" };
|
||
dlg.hidden = false;
|
||
okBtn.focus();
|
||
}
|
||
|
||
Array.prototype.forEach.call(dlg.querySelectorAll("[data-del-close]"), function (el) {
|
||
el.addEventListener("click", close);
|
||
});
|
||
document.addEventListener("keydown", function (e) {
|
||
if (e.key === "Escape" && !dlg.hidden) close();
|
||
});
|
||
|
||
okBtn.addEventListener("click", function () {
|
||
if (!pending) return;
|
||
form.action = pending.action;
|
||
dateEl.value = pending.date;
|
||
nextEl.value = window.location.pathname + window.location.search;
|
||
form.submit();
|
||
});
|
||
|
||
document.addEventListener("click", function (e) {
|
||
var one = e.target.closest("[data-del]");
|
||
if (one) {
|
||
open('"' + (one.dataset.name || "이 센터") + '" 출고를 삭제할까요?',
|
||
"/cupang/" + one.dataset.del + "/delete", "");
|
||
return;
|
||
}
|
||
var day = e.target.closest("[data-day-del]");
|
||
if (day) {
|
||
open(day.dataset.dayDel + " 출고 " + day.dataset.count + "건을 모두 삭제할까요?",
|
||
"/cupang/day-delete", day.dataset.dayDel);
|
||
}
|
||
});
|
||
})();
|
||
</script>
|
||
{% endblock %}
|
||
|
||
|