diff --git a/backend/templates/admin_pos_live.html b/backend/templates/admin_pos_live.html
index 08df6aa..8417698 100644
--- a/backend/templates/admin_pos_live.html
+++ b/backend/templates/admin_pos_live.html
@@ -582,6 +582,61 @@
font-weight: 600;
}
+ /* ── 상세 패널 액션 버튼 ── */
+ .detail-actions {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+ margin-bottom: 20px;
+ }
+ .detail-action-btn {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px 12px;
+ border: none;
+ border-radius: 14px;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+ .detail-action-btn.kiosk {
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
+ color: white;
+ }
+ .detail-action-btn.kiosk:hover {
+ background: linear-gradient(135deg, #4f46e5, #4338ca);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
+ }
+ .detail-action-btn.qr {
+ background: linear-gradient(135deg, #f59e0b, #d97706);
+ color: white;
+ }
+ .detail-action-btn.qr:hover {
+ background: linear-gradient(135deg, #d97706, #b45309);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(245, 158, 11, 0.4);
+ }
+ .detail-action-btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none !important;
+ box-shadow: none !important;
+ }
+ .action-icon {
+ font-size: 28px;
+ margin-bottom: 6px;
+ }
+ .action-text {
+ font-size: 14px;
+ font-weight: 700;
+ }
+ .action-sub {
+ font-size: 12px;
+ opacity: 0.85;
+ margin-top: 2px;
+ }
+
/* ── 반응형 ── */
@media (max-width: 768px) {
.control-section {
@@ -921,8 +976,32 @@
document.getElementById('overlay').classList.add('visible');
document.getElementById('detailPanel').classList.add('open');
+ const expectedPoints = Math.floor(sale.amount * 0.03);
+ const qrStatus = sale.qr_issued
+ ? `✓ QR 발행됨`
+ : `미발행`;
+ const claimedInfo = sale.claimed_name
+ ? `
+ ✓ ${sale.claimed_name}님 적립 완료
+
`
+ : '';
+
// 기본 정보 표시
document.getElementById('detailContent').innerHTML = `
+
+
+
+
+
+
거래번호
@@ -942,13 +1021,14 @@
판매금액
-
₩${Math.floor(sale.amount).toLocaleString()}
+
₩${Math.floor(sale.amount).toLocaleString()}
-
할인
-
₩${Math.floor(sale.discount).toLocaleString()}
+
QR 상태
+
${qrStatus}
+ ${claimedInfo}
📦 품목 목록
@@ -981,6 +1061,81 @@
}
}
+ // ═══════════════════════════════════════════════════════════════
+ // 상세 패널에서 단일 건 처리
+ // ═══════════════════════════════════════════════════════════════
+ async function triggerKioskSingle(orderNo, amount) {
+ const btn = event.target.closest('.detail-action-btn');
+ const originalHtml = btn.innerHTML;
+ btn.disabled = true;
+ btn.innerHTML = '
⏳전송 중...';
+
+ try {
+ const res = await fetch('/api/kiosk/trigger', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ transaction_id: orderNo, amount: amount })
+ });
+ const data = await res.json();
+
+ if (data.success) {
+ btn.innerHTML = `
✅전송 완료!${data.points}P`;
+ showToast(`키오스크 전송 완료! (${data.points}P)`, 'success');
+ setTimeout(() => loadSales(), 1500);
+ } else {
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ showToast(data.message || '전송 실패', 'error');
+ }
+ } catch (err) {
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ showToast(`오류: ${err.message}`, 'error');
+ }
+ }
+
+ async function triggerQrPrintSingle(orderNo, amount, isReprint) {
+ const btn = event.target.closest('.detail-action-btn');
+ const originalHtml = btn.innerHTML;
+ btn.disabled = true;
+ btn.innerHTML = '
⏳출력 중...';
+
+ try {
+ // QR 미발행이면 먼저 생성
+ if (!isReprint) {
+ const genRes = await fetch('/api/admin/qr/generate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ order_no: orderNo, amount: amount, preview: false })
+ });
+ const genData = await genRes.json();
+ if (!genData.success) throw new Error(genData.error);
+ }
+
+ // 프린터 출력
+ const res = await fetch('/api/admin/qr/print', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ order_no: orderNo, printer: 'brother' })
+ });
+ const data = await res.json();
+
+ if (data.success) {
+ btn.innerHTML = '
✅출력 완료!';
+ showToast(data.message, 'success');
+ setTimeout(() => loadSales(), 1500);
+ } else {
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ showToast(data.error || '출력 실패', 'error');
+ }
+ } catch (err) {
+ btn.innerHTML = originalHtml;
+ btn.disabled = false;
+ showToast(`오류: ${err.message}`, 'error');
+ }
+ }
+
// ═══════════════════════════════════════════════════════════════
// 키오스크 전송 (선택된 건 중 첫 번째만 - 키오스크는 1건씩)
// ═══════════════════════════════════════════════════════════════