71194de2eb
CSS flex 체인만으로 잡히지 않는 환경 대비. render() 이후 다음 프레임에 각 영역의 화면 위치를 재서 max-height 를 !important 로 지정한다. window.CPG_DEBUG = true 후 window.cpgFit() 으로 계산값 확인 가능. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
188 lines
7.5 KiB
HTML
188 lines
7.5 KiB
HTML
{% extends "erp_base.html" %}
|
|
|
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901z" />{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="cpg">
|
|
|
|
<div class="erp-page-actions">
|
|
<a class="erp-btn erp-btn-primary" href="/cupang/">◀◀ 달력</a>
|
|
</div>
|
|
|
|
<div class="erp-card cpg-form-card cpg-center-card">
|
|
|
|
<!-- 상단: 추가 + 검색 -->
|
|
<div class="cpg-center-bar">
|
|
<form method="post" action="/cupang/centers" class="cpg-center-addform">
|
|
<input class="erp-input cpg-center-addinput" type="text" name="name"
|
|
placeholder="센터명 입력 (예: 대구3)" required autocomplete="off" />
|
|
<button type="submit" class="erp-btn erp-btn-primary">추가</button>
|
|
</form>
|
|
<input class="erp-input cpg-center-search" type="search" id="cpgCenterSearch"
|
|
placeholder="센터 검색" autocomplete="off" />
|
|
</div>
|
|
|
|
<div class="cpg-card-head cpg-center-head">
|
|
<h2>센터 목록 <span class="erp-muted" id="cpgCenterCount">({{ centers|length }})</span></h2>
|
|
<span class="erp-muted">지역별로 묶어 표시. 이름 변경은 지원하지 않음 — 삭제 후 새로 등록.</span>
|
|
</div>
|
|
|
|
<!-- 목록: 지역 그룹 패널, 위→아래로 읽고 열로 흐름 -->
|
|
<div class="cpg-center-cols" id="cpgCenterGrid">
|
|
{% for g in center_groups %}
|
|
<section class="cpg-center-group" data-group="{{ g.name|lower }}">
|
|
<button type="button" class="cpg-center-gtitle" aria-expanded="true">
|
|
<span class="cpg-center-caret" aria-hidden="true"></span>
|
|
<span class="cpg-center-gname">{{ g.name }}</span>
|
|
<span class="cpg-center-gcount">{{ g.count }}</span>
|
|
</button>
|
|
<div class="cpg-center-glist">
|
|
{% 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>
|
|
<button type="button" class="cpg-icon-btn is-danger" title="삭제"
|
|
data-del-url="/cupang/centers/{{ c.id }}/delete" data-del-name="{{ c.name }}">삭제</button>
|
|
</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<p class="cpg-center-empty erp-muted" id="cpgCenterEmpty" hidden>검색 결과 없음</p>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<!-- 화면 가운데 알림/확인 팝업 -->
|
|
<div class="cpg-modal" id="cpgCenterDlg" hidden>
|
|
<div class="cpg-modal-back" data-dlg-close></div>
|
|
<div class="cpg-modal-box cpg-center-dlg" role="dialog" aria-modal="true" aria-labelledby="cpgCenterDlgTitle">
|
|
<h3 id="cpgCenterDlgTitle">알림</h3>
|
|
<p class="cpg-center-dlgmsg" id="cpgCenterDlgMsg"></p>
|
|
<div class="cpg-dlg-actions cpg-center-dlgact">
|
|
<button type="button" class="erp-btn erp-btn-outline" id="cpgCenterDlgCancel" data-dlg-close hidden>취소</button>
|
|
<button type="button" class="erp-btn erp-btn-primary" id="cpgCenterDlgOk">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" id="cpgCenterDelForm" hidden></form>
|
|
|
|
<script>
|
|
(function () {
|
|
// ── 가운데 팝업 (alert/confirm 대체) ──
|
|
var dlg = document.getElementById('cpgCenterDlg');
|
|
var dlgTitle = document.getElementById('cpgCenterDlgTitle');
|
|
var dlgMsg = document.getElementById('cpgCenterDlgMsg');
|
|
var dlgOk = document.getElementById('cpgCenterDlgOk');
|
|
var dlgCancel = document.getElementById('cpgCenterDlgCancel');
|
|
var onConfirm = null;
|
|
var lastFocus = null;
|
|
|
|
function closeDlg() {
|
|
dlg.hidden = true;
|
|
onConfirm = null;
|
|
if (lastFocus && lastFocus.focus) lastFocus.focus();
|
|
}
|
|
|
|
function openDlg(opts) {
|
|
lastFocus = document.activeElement;
|
|
dlgTitle.textContent = opts.title || '알림';
|
|
dlgMsg.textContent = opts.message || '';
|
|
onConfirm = opts.onConfirm || null;
|
|
dlgCancel.hidden = !opts.onConfirm;
|
|
dlgOk.textContent = opts.okText || '확인';
|
|
dlgOk.classList.toggle('erp-btn-danger', !!opts.danger);
|
|
dlgOk.classList.toggle('erp-btn-primary', !opts.danger);
|
|
dlg.hidden = false;
|
|
dlgOk.focus();
|
|
}
|
|
|
|
dlgOk.addEventListener('click', function () {
|
|
var fn = onConfirm;
|
|
closeDlg();
|
|
if (fn) fn();
|
|
});
|
|
Array.prototype.forEach.call(dlg.querySelectorAll('[data-dlg-close]'), function (el) {
|
|
el.addEventListener('click', closeDlg);
|
|
});
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape' && !dlg.hidden) closeDlg();
|
|
});
|
|
|
|
// ── 추가 결과 알림 (중복 등록 차단) ──
|
|
var flash = {{ flash|tojson }};
|
|
var fname = {{ flash_name|tojson }};
|
|
if (flash === 'dup') {
|
|
openDlg({ title: '이미 등록된 센터', message: '"' + fname + '" 센터는 이미 목록에 있습니다.' });
|
|
} else if (flash === 'dup_inactive') {
|
|
openDlg({
|
|
title: '이미 등록된 센터',
|
|
message: '"' + fname + '" 센터는 이미 목록에 있습니다. 지금은 비활성 상태이니 목록에서 [활성] 을 눌러 사용하세요.'
|
|
});
|
|
}
|
|
if (flash) history.replaceState(null, '', '/cupang/centers');
|
|
|
|
// ── 삭제 확인 ──
|
|
var delForm = document.getElementById('cpgCenterDelForm');
|
|
document.addEventListener('click', function (e) {
|
|
var btn = e.target.closest('[data-del-url]');
|
|
if (!btn) return;
|
|
openDlg({
|
|
title: '센터 삭제',
|
|
message: '"' + btn.dataset.delName + '" 센터를 삭제할까요?',
|
|
okText: '삭제',
|
|
danger: true,
|
|
onConfirm: function () {
|
|
delForm.action = btn.dataset.delUrl;
|
|
delForm.submit();
|
|
}
|
|
});
|
|
});
|
|
|
|
// ── 그룹 접기 ──
|
|
document.addEventListener('click', function (e) {
|
|
var head = e.target.closest('.cpg-center-gtitle');
|
|
if (!head) return;
|
|
var group = head.closest('.cpg-center-group');
|
|
var collapsed = group.classList.toggle('is-collapsed');
|
|
head.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
});
|
|
|
|
// ── 검색 ──
|
|
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 groups = Array.prototype.slice.call(grid.querySelectorAll('.cpg-center-group'));
|
|
box.addEventListener('input', function () {
|
|
var q = box.value.trim().toLowerCase();
|
|
var n = 0;
|
|
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';
|
|
if (q && shown) g.classList.remove('is-collapsed');
|
|
n += shown;
|
|
});
|
|
if (count) count.textContent = '(' + n + ')';
|
|
if (empty) empty.hidden = n !== 0;
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|