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건씩) // ═══════════════════════════════════════════════════════════════