feat(mgmt-perf): 업로드 영역 하단 배치, 업로드 삭제 API, 앱 내 밝은 배경

- 대시보드 조회를 위·엑셀 업로드를 아래로 재배치
- DELETE /api/mgmt-perf/upload/:id 및 최근 업로드 행 삭제 버튼
- dashboard.css 전역 body 어두운 배경을 body.mgmt-perf-standalone로 한정, 임베드는 투명
- mgmt_perf_embed에 standalone 클래스 유지

Made-with: Cursor
This commit is contained in:
2026-04-13 19:43:57 +09:00
parent 200632f580
commit 90358f05a7
6 changed files with 163 additions and 24 deletions

View File

@@ -20,7 +20,8 @@
border: 1px solid var(--border, #e0e0e0);
border-radius: 10px;
padding: 20px;
background: var(--panel-bg, #fafafa);
background: #ffffff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}
.mgmt-upload-panel h2 {
font-size: 1.05rem;
@@ -86,6 +87,11 @@
border-radius: 10px;
overflow: hidden;
min-height: 400px;
background: #ffffff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}
.mgmt-perf-page .mgmt-perf-embed .container {
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);
}
.mgmt-upload-history {
margin-top: 16px;
@@ -100,11 +106,35 @@
padding: 8px;
border-bottom: 1px solid #eee;
text-align: left;
vertical-align: middle;
}
.mgmt-upload-history th:last-child,
.mgmt-upload-history td.mgmt-upload-delete-cell {
text-align: right;
width: 88px;
white-space: nowrap;
}
.mgmt-upload-history th {
font-weight: 600;
color: #555;
}
.btn-mgmt-upload-delete {
padding: 6px 12px;
font-size: 12px;
border-radius: 6px;
border: 1px solid #fecaca;
background: #fff;
color: #b91c1c;
cursor: pointer;
font-weight: 600;
}
.btn-mgmt-upload-delete:hover {
background: #fef2f2;
}
.btn-mgmt-upload-delete:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
</head>
<body>
@@ -123,10 +153,25 @@
</p>
<div class="mgmt-perf-split">
<section class="mgmt-dash-panel" aria-labelledby="mgmt-dash-heading">
<h2 id="mgmt-dash-heading">대시보드 조회</h2>
<div class="mgmt-dash-inline-wrap">
<div class="mgmt-perf-embed" id="mgmtPerfDashRoot">
<%- include('partials/mgmt_perf_dashboard_container', {
dashboardTitle: typeof dashboardTitle !== 'undefined' ? dashboardTitle : '경영성과 대시보드',
quarterLabel: typeof quarterLabel !== 'undefined' ? quarterLabel : 'Q1',
}) %>
</div>
</div>
<script type="application/json" id="mgmt-perf-payload-json"><%- typeof payloadJson !== 'undefined' ? payloadJson : '{}' %></script>
<script src="/mgmt-perf/chart.umd.min.js"></script>
<script src="/mgmt-perf/dashboard-app.js"></script>
</section>
<section class="mgmt-upload-panel" aria-labelledby="mgmt-upload-heading">
<h2 id="mgmt-upload-heading">엑셀 업로드</h2>
<p class="subtitle" style="margin: 0 0 12px; font-size: 14px">
매출 집계 엑셀(<strong>매출일보</strong> 시트 포함)을 업로드하면 스냅샷이 저장되고, 아래 대시보드에 반영됩니다.
매출 집계 엑셀(<strong>매출일보</strong> 시트 포함)을 업로드하면 스냅샷이 저장되고, <strong>위</strong> 대시보드에 반영됩니다.
</p>
<form id="mgmtPerfUploadForm" class="mgmt-upload-form" enctype="multipart/form-data">
<label>
@@ -152,7 +197,7 @@
</form>
<div id="mgmtPerfUploadMsg" class="mgmt-upload-msg" role="status"></div>
<% if (typeof uploadHistory !== 'undefined' && uploadHistory && uploadHistory.length) { %>
<div class="mgmt-upload-history">
<div class="mgmt-upload-history" id="mgmtUploadHistory">
<strong>최근 업로드</strong>
<table>
<thead>
@@ -160,6 +205,7 @@
<th>일시</th>
<th>파일명</th>
<th>연도·분기</th>
<th></th>
</tr>
</thead>
<tbody>
@@ -168,6 +214,9 @@
<td><%= u.created_at ? new Date(u.created_at).toLocaleString('ko-KR') : '-' %></td>
<td><%= u.original_filename || '-' %></td>
<td><%= u.fiscal_year != null && u.fiscal_year !== '' ? u.fiscal_year + '년 Q' + u.quarter : '-' %></td>
<td class="mgmt-upload-delete-cell">
<button type="button" class="btn-mgmt-upload-delete" data-upload-id="<%= u.id != null ? String(u.id) : '' %>">삭제</button>
</td>
</tr>
<% }); %>
</tbody>
@@ -175,21 +224,6 @@
</div>
<% } %>
</section>
<section class="mgmt-dash-panel" aria-labelledby="mgmt-dash-heading">
<h2 id="mgmt-dash-heading">대시보드 조회</h2>
<div class="mgmt-dash-inline-wrap">
<div class="mgmt-perf-embed" id="mgmtPerfDashRoot">
<%- include('partials/mgmt_perf_dashboard_container', {
dashboardTitle: typeof dashboardTitle !== 'undefined' ? dashboardTitle : '경영성과 대시보드',
quarterLabel: typeof quarterLabel !== 'undefined' ? quarterLabel : 'Q1',
}) %>
</div>
</div>
<script type="application/json" id="mgmt-perf-payload-json"><%- typeof payloadJson !== 'undefined' ? payloadJson : '{}' %></script>
<script src="/mgmt-perf/chart.umd.min.js"></script>
<script src="/mgmt-perf/dashboard-app.js"></script>
</section>
</div>
</main>
</div>
@@ -247,6 +281,53 @@
});
});
})();
(function () {
var root = document.getElementById("mgmtUploadHistory");
if (!root) return;
root.addEventListener("click", function (e) {
var t = e.target;
if (!t || !t.closest) return;
var del = t.closest(".btn-mgmt-upload-delete");
if (!del) return;
var id = del.getAttribute("data-upload-id");
if (!id) return;
if (!window.confirm("이 업로드 기록을 삭제할까요?")) return;
del.disabled = true;
fetch("/api/mgmt-perf/upload/" + encodeURIComponent(id), {
method: "DELETE",
credentials: "same-origin",
})
.then(function (r) {
return r.text().then(function (text) {
var j = {};
if (text) {
try {
j = JSON.parse(text);
} catch (parseErr) {
j = { error: text.slice(0, 200) };
}
}
return { ok: r.ok, body: j, status: r.status };
});
})
.then(function (ref) {
if (ref.ok && ref.body && ref.body.ok === true) {
window.location.reload();
return;
}
var err =
(ref.body && (ref.body.error || ref.body.message)) || "삭제에 실패했습니다.";
window.alert(err);
})
.catch(function () {
window.alert("삭제 요청에 실패했습니다.");
})
.finally(function () {
del.disabled = false;
});
});
})();
</script>
</body>
</html>

View File

@@ -7,7 +7,7 @@
<link rel="stylesheet" href="/mgmt-perf/dashboard.css" />
<script src="/mgmt-perf/chart.umd.min.js"></script>
</head>
<body>
<body class="mgmt-perf-standalone">
<div class="mgmt-perf-embed">
<%- include('partials/mgmt_perf_dashboard_container', { dashboardTitle, quarterLabel }) %>
</div>