diff --git a/backend/app.py b/backend/app.py
index a27c563..d00dfb4 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1791,7 +1791,8 @@ def admin():
ml.balance_after,
ml.reason,
ml.description,
- ml.created_at
+ ml.created_at,
+ ml.transaction_id
FROM mileage_ledger ml
JOIN users u ON ml.user_id = u.id
ORDER BY ml.created_at DESC
@@ -1912,12 +1913,35 @@ def api_kiosk_trigger():
claimable_points = token_info['claimable_points']
qr_url = token_info['qr_url']
+ # MSSQL에서 구매 품목 조회
+ sale_items = []
+ try:
+ mssql_session = db_manager.get_session('PM_PRES')
+ sale_sub_query = text("""
+ SELECT
+ ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name,
+ S.SL_NM_item AS quantity,
+ S.SL_TOTAL_PRICE AS total
+ FROM SALE_SUB S
+ LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
+ WHERE S.SL_NO_order = :transaction_id
+ ORDER BY S.DrugCode
+ """)
+ rows = mssql_session.execute(sale_sub_query, {'transaction_id': transaction_id}).fetchall()
+ sale_items = [
+ {'name': r.goods_name, 'qty': int(r.quantity or 0), 'total': int(r.total or 0)}
+ for r in rows
+ ]
+ except Exception as e:
+ logging.warning(f"키오스크 품목 조회 실패 (transaction_id={transaction_id}): {e}")
+
# 키오스크 세션 저장
kiosk_current_session = {
'transaction_id': transaction_id,
'amount': int(amount),
'points': claimable_points,
'qr_url': qr_url,
+ 'items': sale_items,
'created_at': datetime.now(KST).isoformat()
}
@@ -1954,7 +1978,8 @@ def api_kiosk_current():
'transaction_id': kiosk_current_session['transaction_id'],
'amount': kiosk_current_session['amount'],
'points': kiosk_current_session['points'],
- 'qr_url': kiosk_current_session.get('qr_url')
+ 'qr_url': kiosk_current_session.get('qr_url'),
+ 'items': kiosk_current_session.get('items', [])
})
diff --git a/backend/templates/admin.html b/backend/templates/admin.html
index 942daf3..95c77ea 100644
--- a/backend/templates/admin.html
+++ b/backend/templates/admin.html
@@ -202,6 +202,11 @@
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
+ .section table tbody tr[onclick]:hover {
+ background: #eef2ff;
+ cursor: pointer;
+ }
+
/* 사이드바 레이아웃 */
.layout-wrapper {
display: flex;
@@ -495,12 +500,12 @@
{% for tx in recent_transactions %}
-
+
| {{ tx.nickname }} |
{{ tx.phone[:3] }}-{{ tx.phone[3:7] }}-{{ tx.phone[7:] if tx.phone|length > 7 else '' }} |
{{ "{:,}".format(tx.points) }}P |
{{ "{:,}".format(tx.balance_after) }}P |
- {{ tx.description or tx.reason }} |
+ {{ tx.description or tx.reason }}{% if tx.transaction_id %} 🔍{% endif %} |
{{ tx.created_at[:16].replace('T', ' ') }} |
{% endfor %}
diff --git a/backend/templates/kiosk.html b/backend/templates/kiosk.html
index 75ac8d1..3064670 100644
--- a/backend/templates/kiosk.html
+++ b/backend/templates/kiosk.html
@@ -38,8 +38,9 @@
align-items: center;
padding: 24px;
position: relative;
+ overflow-y: auto;
}
- .screen { display: none; width: 100%; height: 100%; }
+ .screen { display: none; width: 100%; }
.screen.active { display: flex; }
/* ══════════════════════════════════════
@@ -207,6 +208,39 @@
.claim-amount-label { font-size: 15px; color: #6b7280; margin-bottom: 4px; }
.claim-amount { font-size: 36px; font-weight: 900; color: #1e1b4b; letter-spacing: -1px; }
.claim-points { font-size: 20px; color: #6366f1; font-weight: 700; margin-top: 8px; }
+ /* 품목 카드 */
+ .items-card {
+ background: #fff;
+ border-radius: 16px;
+ padding: 16px 20px;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.06);
+ width: 100%;
+ max-height: 200px;
+ overflow-y: auto;
+ }
+ .items-title {
+ font-size: 13px;
+ font-weight: 700;
+ color: #6b7280;
+ margin-bottom: 8px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+ .items-list { display: flex; flex-direction: column; gap: 4px; }
+ .item-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ color: #374151;
+ padding: 4px 0;
+ border-bottom: 1px solid #f3f4f6;
+ }
+ .item-row:last-child { border-bottom: none; }
+ .item-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
+ .item-qty { color: #9ca3af; font-size: 13px; flex-shrink: 0; }
+ .item-total { font-weight: 600; color: #6366f1; flex-shrink: 0; min-width: 60px; text-align: right; }
+
.qr-container {
background: #fff;
border-radius: 20px;
@@ -246,6 +280,7 @@
padding: 14px 20px;
display: flex;
align-items: center;
+ justify-content: center;
transition: border-color 0.2s;
min-height: 64px;
}
@@ -256,7 +291,6 @@
font-weight: 700;
color: #9ca3af;
letter-spacing: 1px;
- margin-right: 4px;
flex-shrink: 0;
}
.phone-number {
@@ -264,8 +298,7 @@
font-weight: 700;
color: #1e1b4b;
letter-spacing: 2px;
- flex: 1;
- text-align: center;
+ margin-left: 2px;
}
.phone-number.placeholder { color: #d1d5db; }
@@ -348,9 +381,52 @@
/* ── 적립/성공 화면 배경 밝게 ── */
.claim-screen, .success-screen { background: #f5f7fa; border-radius: 24px; padding: 32px; }
- /* ── 반응형 ── */
+ /* ── 반응형: 세로 모니터 (portrait, 폭 700px 이상) ── */
+ @media (orientation: portrait) and (min-width: 700px) {
+ .main { padding: 32px; }
+
+ /* 적립 화면: 세로 스택, 공간 활용 */
+ .claim-screen {
+ flex-direction: column;
+ gap: 24px;
+ padding: 32px 48px;
+ max-width: 640px;
+ align-items: center;
+ justify-content: center;
+ }
+ .claim-left {
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 16px;
+ width: 100%;
+ align-items: flex-start;
+ }
+ .claim-info-card { flex: 1; min-width: 200px; }
+ .qr-container { flex-shrink: 0; }
+ .items-card { width: 100%; max-height: 160px; }
+ .qr-container img { width: 140px; height: 140px; }
+
+ .divider { flex-direction: row; }
+ .divider-line { width: 60px; height: 2px; }
+
+ .claim-right { width: 100%; align-items: center; }
+ .phone-display-wrap { max-width: 440px; }
+ .phone-prefix { font-size: 30px; }
+ .phone-number { font-size: 30px; white-space: nowrap; }
+ .numpad { max-width: 440px; }
+ .submit-btn { max-width: 440px; }
+
+ /* 슬라이드 더 크게 */
+ .slides-wrapper { height: 420px; }
+ .slide-icon { width: 110px; height: 110px; font-size: 56px; }
+ .slide-title { font-size: 34px; }
+ .slide-desc { font-size: 19px; }
+ .slide-highlight { font-size: 16px; padding: 12px 32px; }
+ }
+
+ /* ── 반응형: 좁은 화면 (모바일) ── */
@media (max-width: 700px) {
- .claim-screen { flex-direction: column; gap: 20px; padding: 20px; }
+ .claim-screen { flex-direction: column; gap: 24px; padding: 20px; }
.divider { flex-direction: row; }
.divider-line { width: 60px; height: 2px; }
.claim-amount { font-size: 28px; }
@@ -438,6 +514,10 @@
0원
적립 0P
+
휴대폰으로 QR을 스캔하여
적립할 수도 있습니다
@@ -453,7 +533,7 @@
전화번호로 적립하기
- 010 -
+ 010-
0000-0000
@@ -677,6 +757,22 @@ async function pollKioskSession() {
document.getElementById('claimAmount').textContent = data.amount.toLocaleString() + '원';
document.getElementById('claimPoints').textContent = data.points.toLocaleString();
+ // 품목 목록 표시
+ const itemsCard = document.getElementById('itemsCard');
+ const itemsList = document.getElementById('itemsList');
+ if (data.items && data.items.length > 0) {
+ itemsList.innerHTML = data.items.map(item =>
+ `
+ ${item.name}
+ ${item.qty}개
+ ${item.total.toLocaleString()}원
+
`
+ ).join('');
+ itemsCard.style.display = '';
+ } else {
+ itemsCard.style.display = 'none';
+ }
+
if (data.qr_url) {
document.getElementById('qrImage').src =
'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' +