diff --git a/backend/templates/admin_pos_live.html b/backend/templates/admin_pos_live.html
index dd9b2e2..ddc27db 100644
--- a/backend/templates/admin_pos_live.html
+++ b/backend/templates/admin_pos_live.html
@@ -125,6 +125,16 @@
color: #94a3b8;
cursor: not-allowed;
}
+ .btn-kiosk {
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
+ color: #fff;
+ }
+ .btn-kiosk:hover { background: linear-gradient(135deg, #4f46e5, #4338ca); }
+ .btn-kiosk:disabled {
+ background: #e2e8f0;
+ color: #94a3b8;
+ cursor: not-allowed;
+ }
.auto-refresh {
display: flex;
@@ -598,7 +608,8 @@
@@ -838,15 +836,15 @@
});
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- // QR λ°ν κΈ°λ₯
+ // ν€μ€μ€ν¬ & λΌλ²¨ μΆλ ₯ κΈ°λ₯
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
let selectedSale = null;
- let selectedPrinter = 'brother';
- let qrGenerated = false;
+ let selectedIdx = -1;
- // ν
μ΄λΈ ν μ ν μ QR λ²νΌ νμ±ν
+ // ν
μ΄λΈ ν μ ν μ λ²νΌ νμ±ν
function showDetail(orderNo, idx) {
+ selectedIdx = idx;
// μ ν νμ
document.querySelectorAll('#salesTable tr').forEach(tr => tr.classList.remove('selected'));
document.querySelector(`#salesTable tr[data-idx="${idx}"]`)?.classList.add('selected');
@@ -854,15 +852,13 @@
const sale = salesData[idx];
selectedSale = sale;
- // QR λ²νΌ νμ±ν (QR λ―Έλ°ν 건λ§)
+ // λ²νΌ νμ±ν
+ document.getElementById('kioskBtn').disabled = false;
+ document.getElementById('qrBtn').disabled = false;
+
+ // QR λ°ν μ¬λΆμ λ°λΌ λ²νΌ ν
μ€νΈ λ³κ²½
const qrBtn = document.getElementById('qrBtn');
- if (!sale.qr_issued) {
- qrBtn.disabled = false;
- qrBtn.textContent = 'π·οΈ QR λ°ν';
- } else {
- qrBtn.disabled = false;
- qrBtn.textContent = 'π μ¬μΆλ ₯';
- }
+ qrBtn.textContent = sale.qr_issued ? 'π μ¬μΆλ ₯' : 'π·οΈ λΌλ²¨μΆλ ₯';
// ν¨λ μ΄κΈ°
document.getElementById('overlay').classList.add('visible');
@@ -928,166 +924,174 @@
}
}
- function openQrModal() {
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // ν€μ€μ€ν¬ μ μ‘
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ async function triggerKiosk() {
if (!selectedSale) {
alert('λ¨Όμ νλ§€ 건μ μ νν΄μ£ΌμΈμ.');
return;
}
- qrGenerated = false;
- const sale = selectedSale;
- const isReprint = sale.qr_issued;
-
- document.getElementById('qrModalBody').innerHTML = `
-
-
-
κ±°λλ²νΈ
-
${sale.order_no}
-
-
-
μκ°
-
${sale.time}
-
-
-
νλ§€κΈμ‘
-
β©${Math.floor(sale.amount).toLocaleString()}
-
-
-
μμ μ 립
-
${Math.floor(sale.amount * 0.03).toLocaleString()}P
-
-
-
-
- ${isReprint
- ? '
μ΄λ―Έ λ°νλ QRμ
λλ€.
μ¬μΆλ ₯νλ €λ©΄ νλ¦°ν°λ₯Ό μ ν ν "μΆλ ₯" λ²νΌμ λλ₯΄μΈμ.
'
- : '
QR μμ± λ²νΌμ λλ¬μ£ΌμΈμ
'
- }
-
-
-
-
-
- ${isReprint
- ? ''
- : ''
- }
-
- `;
-
- document.getElementById('qrModal').classList.add('visible');
- }
-
- function closeQrModal() {
- document.getElementById('qrModal').classList.remove('visible');
- }
-
- function selectPrinter(type) {
- selectedPrinter = type;
- document.querySelectorAll('.printer-option').forEach(el => el.classList.remove('selected'));
- document.querySelector(`.printer-option:nth-child(${type === 'brother' ? 1 : 2})`).classList.add('selected');
- }
-
- async function generateQr() {
- if (!selectedSale) return;
-
- const container = document.getElementById('qrPreviewContainer');
- container.innerHTML = '
';
+ const btn = document.getElementById('kioskBtn');
+ const originalText = btn.textContent;
+ btn.disabled = true;
+ btn.textContent = 'μ μ‘ μ€...';
try {
- const res = await fetch('/api/admin/qr/generate', {
+ const res = await fetch('/api/kiosk/trigger', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
- order_no: selectedSale.order_no,
- amount: selectedSale.amount,
- preview: true
+ transaction_id: selectedSale.order_no,
+ amount: selectedSale.amount
})
});
const data = await res.json();
if (data.success) {
- qrGenerated = true;
- container.innerHTML = `
-

-
- β ${data.claimable_points}P μ 립 κ°λ₯
-
- `;
+ btn.textContent = `β ${data.points}P`;
+ btn.style.background = '#10b981';
- // μΆλ ₯ λ²νΌ νμ±ν
- const printBtn = document.getElementById('printBtn');
- if (printBtn) printBtn.disabled = false;
+ // ν μ€νΈ λ©μμ§
+ showToast(`ν€μ€μ€ν¬ μ μ‘ μλ£! (${data.points}P)`, 'success');
- // ν
μ΄λΈ κ°±μ
- loadSales();
+ // ν
μ΄λΈ μλ‘κ³ μΉ¨
+ setTimeout(() => {
+ loadSales();
+ btn.textContent = originalText;
+ btn.style.background = '';
+ btn.disabled = false;
+ }, 2000);
} else {
- container.innerHTML = `
β ${data.error}
`;
+ showToast(data.message || 'μ μ‘ μ€ν¨', 'error');
+ btn.textContent = originalText;
+ btn.disabled = false;
}
} catch (err) {
- container.innerHTML = `
μ€λ₯: ${err.message}
`;
+ showToast(`μ€λ₯: ${err.message}`, 'error');
+ btn.textContent = originalText;
+ btn.disabled = false;
}
}
- async function printQrLabel() {
- if (!selectedSale) return;
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // λΌλ²¨ μΆλ ₯ (Brother QL-810W)
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ async function triggerQrPrint() {
+ if (!selectedSale) {
+ alert('λ¨Όμ νλ§€ 건μ μ νν΄μ£ΌμΈμ.');
+ return;
+ }
- const container = document.getElementById('qrPreviewContainer');
- const originalContent = container.innerHTML;
- container.innerHTML = '
νλ¦°ν°λ‘ μ μ‘ μ€...
';
+ const btn = document.getElementById('qrBtn');
+ const originalText = btn.textContent;
+ btn.disabled = true;
+ btn.textContent = 'μΆλ ₯ μ€...';
try {
+ // 1. QR λ―Έλ°νμ΄λ©΄ λ¨Όμ μμ±
+ if (!selectedSale.qr_issued) {
+ const genRes = await fetch('/api/admin/qr/generate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ order_no: selectedSale.order_no,
+ amount: selectedSale.amount,
+ preview: false
+ })
+ });
+ const genData = await genRes.json();
+ if (!genData.success) {
+ throw new Error(genData.error);
+ }
+ }
+
+ // 2. νλ¦°ν° μΆλ ₯
const res = await fetch('/api/admin/qr/print', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_no: selectedSale.order_no,
- printer: selectedPrinter
+ printer: 'brother'
})
});
const data = await res.json();
if (data.success) {
- container.innerHTML = `
- ${originalContent.includes('qr-preview-img') ? originalContent : ''}
-
- β ${data.message}
-
- `;
+ btn.textContent = 'β μΆλ ₯μλ£';
+ btn.style.background = '#10b981';
+ showToast(data.message, 'success');
- // μ μ ν λͺ¨λ¬ λ«κΈ°
setTimeout(() => {
- closeQrModal();
loadSales();
+ btn.textContent = originalText;
+ btn.style.background = '';
+ btn.disabled = false;
}, 2000);
} else {
- container.innerHTML = `
- ${originalContent}
-
- β ${data.error}
-
- `;
+ showToast(data.error || 'μΆλ ₯ μ€ν¨', 'error');
+ btn.textContent = originalText;
+ btn.disabled = false;
}
} catch (err) {
- container.innerHTML = `
- ${originalContent}
-
- μ€λ₯: ${err.message}
-
- `;
+ showToast(`μ€λ₯: ${err.message}`, 'error');
+ btn.textContent = originalText;
+ btn.disabled = false;
}
}
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // ν μ€νΈ λ©μμ§
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ function showToast(message, type = 'info') {
+ // κΈ°μ‘΄ ν μ€νΈ μ κ±°
+ const existing = document.querySelector('.toast');
+ if (existing) existing.remove();
+
+ const toast = document.createElement('div');
+ toast.className = 'toast';
+ toast.style.cssText = `
+ position: fixed;
+ bottom: 24px;
+ left: 50%;
+ transform: translateX(-50%);
+ padding: 14px 28px;
+ border-radius: 12px;
+ font-weight: 600;
+ font-size: 15px;
+ z-index: 9999;
+ animation: toastIn 0.3s ease;
+ ${type === 'success'
+ ? 'background: #10b981; color: white;'
+ : type === 'error'
+ ? 'background: #ef4444; color: white;'
+ : 'background: #1e293b; color: white;'}
+ `;
+ toast.textContent = message;
+ document.body.appendChild(toast);
+
+ setTimeout(() => {
+ toast.style.animation = 'toastOut 0.3s ease';
+ setTimeout(() => toast.remove(), 300);
+ }, 3000);
+ }
+
+ // ν μ€νΈ μ λλ©μ΄μ
μΆκ°
+ const toastStyle = document.createElement('style');
+ toastStyle.textContent = `
+ @keyframes toastIn {
+ from { opacity: 0; transform: translateX(-50%) translateY(20px); }
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
+ }
+ @keyframes toastOut {
+ from { opacity: 1; transform: translateX(-50%) translateY(0); }
+ to { opacity: 0; transform: translateX(-50%) translateY(20px); }
+ }
+ `;
+ document.head.appendChild(toastStyle);