feat(cupang): 센터 중복 등록 알림 + 목록 지역별 그룹화
추가 시 같은 이름이 있으면 조용히 재활성되던 동작(ON CONFLICT DO UPDATE) 때문에 사용자가 중복을 알 수 없었다. 등록 전에 먼저 조회해서 이미 있으면 만들지 않고 알림(활성/비활성 구분)을 띄운다. 목록은 29개가 가로 4열로 흩어져 훑기 어려웠다. 센터명 앞 지역 접두사로 묶고(인천14 → "인천") CSS 멀티컬럼으로 한 열을 위→아래로 읽게 바꿨다. 숫자는 자연 정렬(인천4 < 인천14). 행 액션은 hover/focus 시에만 노출해 이름이 먼저 눈에 들어오게 했다. - 라우터: _center_group / _center_sort_key / _group_centers 추가 - 검색은 그룹 단위로 접히고 결과 0건이면 안내 표시 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
@@ -24,47 +24,78 @@
|
||||
|
||||
<div class="cpg-card-head cpg-center-head">
|
||||
<h2>센터 목록 <span class="erp-muted" id="cpgCenterCount">({{ centers|length }})</span></h2>
|
||||
<span class="erp-muted">쓰지 않는 센터는 비활성 또는 삭제. 이름 변경은 지원하지 않음 — 새로 등록.</span>
|
||||
<span class="erp-muted">지역별로 묶어 표시. 이름 변경은 지원하지 않음 — 삭제 후 새로 등록.</span>
|
||||
</div>
|
||||
|
||||
<!-- 목록: 여러 열로 한 화면에 -->
|
||||
<div class="cpg-center-grid" id="cpgCenterGrid">
|
||||
{% for c in centers %}
|
||||
<div 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>
|
||||
<span class="cpg-center-name">{{ c.name }}</span>
|
||||
<form method="post" action="/cupang/centers/{{ c.id }}/edit" class="cpg-inline-form">
|
||||
<input type="hidden" name="active" value="{% if c.active %}0{% else %}1{% endif %}" />
|
||||
<button type="submit" class="erp-btn erp-btn-outline cpg-btn-sm"
|
||||
title="{% if c.active %}비활성으로 전환{% else %}활성으로 전환{% endif %}">{% if c.active %}비활성{% else %}활성{% endif %}</button>
|
||||
</form>
|
||||
<form method="post" action="/cupang/centers/{{ c.id }}/delete" class="cpg-inline-form"
|
||||
onsubmit="return confirm('{{ c.name }} 센터를 삭제할까요?');">
|
||||
<button type="submit" class="erp-btn erp-btn-danger cpg-btn-sm">삭제</button>
|
||||
</form>
|
||||
<!-- 목록: 지역 그룹 카드, 위→아래로 읽고 열로 흐름 -->
|
||||
<div class="cpg-center-cols" id="cpgCenterGrid">
|
||||
{% for g in center_groups %}
|
||||
<div class="cpg-center-group" data-group="{{ g.name|lower }}">
|
||||
<div class="cpg-center-gtitle">
|
||||
<span class="cpg-center-gname">{{ g.name }}</span>
|
||||
<span class="cpg-center-gcount">{{ g.count }}</span>
|
||||
</div>
|
||||
{% for c in g["items"] %}
|
||||
<div 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>
|
||||
<span class="cpg-center-name">{{ c.name }}</span>
|
||||
<span class="cpg-center-acts">
|
||||
<form method="post" action="/cupang/centers/{{ c.id }}/edit" class="cpg-inline-form">
|
||||
<input type="hidden" name="active" value="{% if c.active %}0{% else %}1{% endif %}" />
|
||||
<button type="submit" class="cpg-icon-btn"
|
||||
title="{% if c.active %}비활성으로 전환{% else %}활성으로 전환{% endif %}">{% if c.active %}비활성{% else %}활성{% endif %}</button>
|
||||
</form>
|
||||
<form method="post" action="/cupang/centers/{{ c.id }}/delete" class="cpg-inline-form"
|
||||
onsubmit="return confirm('{{ c.name }} 센터를 삭제할까요?');">
|
||||
<button type="submit" class="cpg-icon-btn is-danger" title="삭제">삭제</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<p class="cpg-center-empty erp-muted" id="cpgCenterEmpty" hidden>검색 결과 없음</p>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// 추가 결과 알림 (중복 등록 방지)
|
||||
var flash = {{ flash|tojson }};
|
||||
var fname = {{ flash_name|tojson }};
|
||||
if (flash === 'dup') {
|
||||
alert('"' + fname + '" 센터는 이미 등록되어 있습니다.');
|
||||
} else if (flash === 'dup_inactive') {
|
||||
alert('"' + fname + '" 센터는 이미 등록되어 있습니다. (현재 비활성 상태 — 목록에서 활성으로 전환하세요.)');
|
||||
}
|
||||
if (flash) {
|
||||
history.replaceState(null, '', '/cupang/centers');
|
||||
}
|
||||
|
||||
var box = document.getElementById('cpgCenterSearch');
|
||||
var grid = document.getElementById('cpgCenterGrid');
|
||||
var count = document.getElementById('cpgCenterCount');
|
||||
var empty = document.getElementById('cpgCenterEmpty');
|
||||
if (!box || !grid) return;
|
||||
var rows = Array.prototype.slice.call(grid.querySelectorAll('.cpg-center-row'));
|
||||
var groups = Array.prototype.slice.call(grid.querySelectorAll('.cpg-center-group'));
|
||||
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;
|
||||
groups.forEach(function (g) {
|
||||
var shown = 0;
|
||||
Array.prototype.forEach.call(g.querySelectorAll('.cpg-center-row'), function (r) {
|
||||
var hit = !q || (r.dataset.name || '').indexOf(q) !== -1;
|
||||
r.style.display = hit ? '' : 'none';
|
||||
if (hit) shown += 1;
|
||||
});
|
||||
g.style.display = shown ? '' : 'none';
|
||||
n += shown;
|
||||
});
|
||||
if (count) count.textContent = '(' + n + ')';
|
||||
if (empty) empty.hidden = n !== 0;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831b" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260831c" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
Reference in New Issue
Block a user