refactor(cupang): 입고센터 관리 UI 단순화
센터 목록은 이름 등록·조회·관리가 목적이므로 "사용중" 표시를 없애고 활성/비활성/삭제만 남긴다. 좌우 2단(추가 카드 360px + 900px 고정 목록) 대신 상단 추가·검색 바 + 다중 컬럼 그리드로 바꿔 29개 이상 센터를 한 화면에서 훑을 수 있게 했다. - 행마다 폼 하나(formaction/name=active)로 저장·토글·삭제 처리 - 상태는 배지 대신 좌측 점(활성 초록 / 비활성 회색+흐리게) - 클라이언트 검색 필터, 카운트 실시간 갱신 - 라우터에서 center_in_use 조회 제거(화면에서 미사용) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -418,9 +418,6 @@ async def centers_page(request: Request) -> HTMLResponse:
|
|||||||
return guard
|
return guard
|
||||||
store, user = guard
|
store, user = guard
|
||||||
centers = sorted(store.list_centers(include_inactive=True), key=lambda c: c["name"])
|
centers = sorted(store.list_centers(include_inactive=True), key=lambda c: c["name"])
|
||||||
# 사용 중 여부 표시
|
|
||||||
for c in centers:
|
|
||||||
c["in_use"] = store.center_in_use(center_id=c["id"])
|
|
||||||
return render_template(
|
return render_template(
|
||||||
request,
|
request,
|
||||||
"cupang/centers.html",
|
"cupang/centers.html",
|
||||||
@@ -429,7 +426,7 @@ async def centers_page(request: Request) -> HTMLResponse:
|
|||||||
"is_admin": is_admin(user),
|
"is_admin": is_admin(user),
|
||||||
"nav_items": build_erp_nav(user, active="cupang"),
|
"nav_items": build_erp_nav(user, active="cupang"),
|
||||||
"page_title": "쿠팡 밀크런 — 입고센터 관리",
|
"page_title": "쿠팡 밀크런 — 입고센터 관리",
|
||||||
"page_subtitle": "추가 · 수정 · 비활성화. 사용 중 센터는 삭제되지 않고 비활성화됩니다.",
|
"page_subtitle": "센터명 등록 · 수정 · 활성/비활성 · 삭제.",
|
||||||
"centers": centers,
|
"centers": centers,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
@@ -9,58 +9,63 @@
|
|||||||
<a class="erp-btn erp-btn-primary" href="/cupang/">◀◀ 달력</a>
|
<a class="erp-btn erp-btn-primary" href="/cupang/">◀◀ 달력</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cpg-center-layout">
|
<div class="erp-card cpg-form-card cpg-center-card">
|
||||||
|
|
||||||
<!-- 왼쪽: 입고센터 추가 -->
|
<!-- 상단: 추가 + 검색 -->
|
||||||
<div class="erp-card cpg-form-card cpg-center-add">
|
<div class="cpg-center-bar">
|
||||||
<h2 class="cpg-center-add-title">입고센터 추가</h2>
|
<form method="post" action="/cupang/centers" class="cpg-center-addform">
|
||||||
<form method="post" action="/cupang/centers" class="cpg-inline-form">
|
<input class="erp-input cpg-center-addinput" type="text" name="name"
|
||||||
<input class="erp-input" type="text" name="name" placeholder="센터명 (예: 대구3)" required style="flex:1 1 auto;min-width:0" />
|
placeholder="센터명 입력 (예: 대구3)" required autocomplete="off" />
|
||||||
<button type="submit" class="erp-btn erp-btn-primary">추가</button>
|
<button type="submit" class="erp-btn erp-btn-primary">추가</button>
|
||||||
</form>
|
</form>
|
||||||
|
<input class="erp-input cpg-center-search" type="search" id="cpgCenterSearch"
|
||||||
|
placeholder="센터 검색" autocomplete="off" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 오른쪽: 센터 목록 (높이 900 고정, 내부 스크롤) -->
|
<div class="cpg-card-head cpg-center-head">
|
||||||
<div class="erp-card cpg-form-card cpg-center-listcard">
|
<h2>센터 목록 <span class="erp-muted" id="cpgCenterCount">({{ centers|length }})</span></h2>
|
||||||
<div class="cpg-card-head">
|
<span class="erp-muted">이름을 고치면 저장, 쓰지 않는 센터는 비활성 또는 삭제.</span>
|
||||||
<h2>센터 목록 ({{ centers|length }})</h2>
|
|
||||||
<span class="erp-muted">사용 중 센터는 삭제 시 비활성화됩니다.</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cpg-center-list">
|
<!-- 목록: 여러 열로 한 화면에 -->
|
||||||
|
<div class="cpg-center-grid" id="cpgCenterGrid">
|
||||||
{% for c in centers %}
|
{% for c in centers %}
|
||||||
<div class="cpg-center-row {% if not c.active %}is-inactive{% endif %}">
|
<form method="post" action="/cupang/centers/{{ c.id }}/edit"
|
||||||
<form method="post" action="/cupang/centers/{{ c.id }}/edit" class="cpg-center-edit">
|
class="cpg-center-row {% if not c.active %}is-inactive{% endif %}"
|
||||||
|
data-name="{{ c.name|lower }}">
|
||||||
|
<span class="cpg-center-dot" title="{% if c.active %}활성{% else %}비활성{% endif %}"></span>
|
||||||
<input class="erp-input cpg-center-name" type="text" name="name" value="{{ c.name }}" />
|
<input class="erp-input cpg-center-name" type="text" name="name" value="{{ c.name }}" />
|
||||||
<button type="submit" class="erp-btn erp-btn-outline cpg-btn-sm" title="이름 저장">저장</button>
|
<button type="submit" class="erp-btn erp-btn-outline cpg-btn-sm" title="이름 저장">저장</button>
|
||||||
|
<button type="submit" name="active" value="{% if c.active %}0{% else %}1{% endif %}"
|
||||||
|
class="erp-btn erp-btn-outline cpg-btn-sm"
|
||||||
|
title="{% if c.active %}비활성으로 전환{% else %}활성으로 전환{% endif %}">{% if c.active %}비활성{% else %}활성{% endif %}</button>
|
||||||
|
<button type="submit" formaction="/cupang/centers/{{ c.id }}/delete" formnovalidate
|
||||||
|
class="erp-btn erp-btn-danger cpg-btn-sm"
|
||||||
|
onclick="return confirm('{{ c.name }} 센터를 삭제할까요?');">삭제</button>
|
||||||
</form>
|
</form>
|
||||||
<span class="cpg-center-state">
|
|
||||||
{% if c.in_use %}<span class="erp-badge erp-badge-inverse cpg-mini">사용중</span>{% endif %}
|
|
||||||
{% if c.active %}<span class="erp-badge erp-badge-success cpg-mini">활성</span>
|
|
||||||
{% else %}<span class="erp-badge erp-badge-neutral cpg-mini">비활성</span>{% endif %}
|
|
||||||
</span>
|
|
||||||
<span class="cpg-center-act">
|
|
||||||
{% if c.active %}
|
|
||||||
<form method="post" action="/cupang/centers/{{ c.id }}/edit" class="cpg-inline-form">
|
|
||||||
<input type="hidden" name="active" value="0" />
|
|
||||||
<button type="submit" class="erp-btn erp-btn-outline cpg-btn-sm">비활성</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<form method="post" action="/cupang/centers/{{ c.id }}/edit" class="cpg-inline-form">
|
|
||||||
<input type="hidden" name="active" value="1" />
|
|
||||||
<button type="submit" class="erp-btn erp-btn-outline cpg-btn-sm">활성</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
<form method="post" action="/cupang/centers/{{ c.id }}/delete" class="cpg-inline-form"
|
|
||||||
onsubmit="return confirm('{% if c.in_use %}사용 중 → 비활성화됩니다.{% else %}삭제합니다.{% endif %} 계속?');">
|
|
||||||
<button type="submit" class="erp-btn erp-btn-danger cpg-btn-sm">삭제</button>
|
|
||||||
</form>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var box = document.getElementById('cpgCenterSearch');
|
||||||
|
var grid = document.getElementById('cpgCenterGrid');
|
||||||
|
var count = document.getElementById('cpgCenterCount');
|
||||||
|
if (!box || !grid) return;
|
||||||
|
var rows = Array.prototype.slice.call(grid.querySelectorAll('.cpg-center-row'));
|
||||||
|
box.addEventListener('input', function () {
|
||||||
|
var q = box.value.trim().toLowerCase();
|
||||||
|
var n = 0;
|
||||||
|
rows.forEach(function (r) {
|
||||||
|
var hit = !q || (r.dataset.name || '').indexOf(q) !== -1;
|
||||||
|
r.style.display = hit ? '' : 'none';
|
||||||
|
if (hit) n += 1;
|
||||||
|
});
|
||||||
|
if (count) count.textContent = '(' + n + ')';
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829l" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831a" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
+24
-28
@@ -169,45 +169,41 @@
|
|||||||
.cpg-inline-form { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
|
.cpg-inline-form { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
|
||||||
.cpg-row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
|
.cpg-row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||||
|
|
||||||
/* ── 입고센터 관리 (좌: 추가 / 우: 목록) ── */
|
/* ── 입고센터 관리 (상단: 추가·검색 / 아래: 목록 그리드) ── */
|
||||||
.cpg-center-layout {
|
.cpg-center-card { padding: 16px; }
|
||||||
display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start;
|
|
||||||
}
|
|
||||||
.cpg-center-add {
|
|
||||||
width: 360px; flex: 0 0 auto;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
.cpg-center-listcard {
|
|
||||||
flex: 1 1 480px; min-width: 0;
|
|
||||||
height: 900px;
|
|
||||||
display: flex; flex-direction: column; overflow: hidden;
|
|
||||||
}
|
|
||||||
@media (max-width: 820px) {
|
|
||||||
.cpg-center-add, .cpg-center-listcard { width: 100%; flex-basis: 100%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.cpg-center-add-title { font-size: 15px; font-weight: 600; margin: 0 0 8px; }
|
.cpg-center-bar {
|
||||||
.cpg-btn-sm { padding: 3px 8px; font-size: 12px; }
|
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
|
||||||
|
padding-bottom: 12px; border-bottom: 1px solid var(--color-subtle-ash);
|
||||||
|
}
|
||||||
|
.cpg-center-addform { display: flex; gap: 6px; align-items: center; margin: 0; flex: 1 1 320px; min-width: 0; }
|
||||||
|
.cpg-center-addinput { flex: 1 1 auto; min-width: 0; }
|
||||||
|
.cpg-center-search { flex: 0 1 240px; min-width: 0; }
|
||||||
|
|
||||||
/* 목록: 카드 높이 채우고 내부 스크롤 */
|
.cpg-center-head { margin: 12px 0 8px; }
|
||||||
.cpg-center-list {
|
|
||||||
flex: 1 1 auto; min-height: 0; overflow-y: auto;
|
.cpg-center-grid {
|
||||||
display: flex; flex-direction: column; gap: 6px;
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
||||||
|
gap: 6px;
|
||||||
|
max-height: 70vh; overflow-y: auto;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cpg-center-row {
|
.cpg-center-row {
|
||||||
display: flex; align-items: center; gap: 8px;
|
display: flex; align-items: center; gap: 6px;
|
||||||
padding: 5px 8px;
|
margin: 0; padding: 5px 8px;
|
||||||
border: 1px solid var(--color-subtle-ash); border-radius: 10px;
|
border: 1px solid var(--color-subtle-ash); border-radius: 10px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.cpg-center-dot {
|
||||||
|
flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
|
||||||
|
background: var(--color-success-green, #10c22b);
|
||||||
|
}
|
||||||
.cpg-center-row.is-inactive { opacity: .55; }
|
.cpg-center-row.is-inactive { opacity: .55; }
|
||||||
.cpg-center-edit { display: flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; }
|
.cpg-center-row.is-inactive .cpg-center-dot { background: var(--color-subtle-ash); }
|
||||||
.cpg-center-name { flex: 1 1 auto; min-width: 0; }
|
.cpg-center-name { flex: 1 1 auto; min-width: 0; }
|
||||||
.cpg-center-sort { width: 56px; flex: 0 0 auto; }
|
.cpg-btn-sm { padding: 3px 8px; font-size: 12px; flex: 0 0 auto; }
|
||||||
.cpg-center-state { display: inline-flex; gap: 4px; flex: 0 0 auto; }
|
|
||||||
.cpg-center-act { display: inline-flex; gap: 4px; flex: 0 0 auto; }
|
|
||||||
|
|
||||||
/* ── 라인 테이블 ── */
|
/* ── 라인 테이블 ── */
|
||||||
.cpg-lines td { vertical-align: middle; }
|
.cpg-lines td { vertical-align: middle; }
|
||||||
|
|||||||
Reference in New Issue
Block a user