diff --git a/backend/pos_printer.py b/backend/pos_printer.py index bd43357..76ba163 100644 --- a/backend/pos_printer.py +++ b/backend/pos_printer.py @@ -98,7 +98,7 @@ def print_text(text: str, cut: bool = True) -> bool: def print_cusetc(customer_name: str, cusetc: str, phone: str = None) -> bool: """ - 특이(참고)사항 영수증 출력 + 특이(참고)사항 영수증 출력 (단순 텍스트 방식) Args: customer_name: 고객 이름 @@ -119,66 +119,27 @@ def print_cusetc(customer_name: str, cusetc: str, phone: str = None) -> bool: else: phone_display = phone - try: - # ESC/POS 명령어 조합 - commands = bytearray() - commands.extend(INIT) - - # 헤더 (중앙 정렬, 크게) - commands.extend(ALIGN_CENTER) - commands.extend(DOUBLE_SIZE) - commands.extend("[ 특이사항 ]\n".encode('euc-kr')) - commands.extend(NORMAL_SIZE) - - # 구분선 - commands.extend("================================\n".encode('euc-kr')) - - # 고객 정보 (왼쪽 정렬) - commands.extend(ALIGN_LEFT) - commands.extend(BOLD_ON) - commands.extend(f"고객: {customer_name}\n".encode('euc-kr')) - commands.extend(BOLD_OFF) - - if phone_display: - commands.extend(f"연락처: {phone_display}\n".encode('euc-kr')) - - commands.extend(f"출력: {now}\n".encode('euc-kr')) - - # 구분선 - commands.extend("--------------------------------\n".encode('euc-kr')) - - # 특이사항 내용 (굵게) - commands.extend(BOLD_ON) - - # 긴 텍스트 줄바꿈 처리 (32자 기준) - lines = [] - for line in cusetc.split('\n'): - while len(line) > 32: - lines.append(line[:32]) - line = line[32:] - lines.append(line) - - for line in lines: - commands.extend(f"{line}\n".encode('euc-kr', errors='replace')) - - commands.extend(BOLD_OFF) - - # 하단 구분선 - commands.extend("================================\n".encode('euc-kr')) - - # 약국명 (중앙 정렬) - commands.extend(ALIGN_CENTER) - commands.extend("청춘약국\n".encode('euc-kr')) - - # 피드 + 커트 - commands.extend(b'\n\n\n') - commands.extend(CUT) - - return print_raw(bytes(commands)) - - except Exception as e: - logging.error(f"[POS Printer] 특이사항 인쇄 실패: {e}") - return False + # 80mm 프린터 = 48자 기준 + LINE = "=" * 48 + THIN = "-" * 48 + + message = f""" +{LINE} + [ 특이사항 ] +{LINE} +고객: {customer_name} +""" + if phone_display: + message += f"연락처: {phone_display}\n" + + message += f"""출력: {now} +{THIN} +{cusetc} +{LINE} + 청춘약국 +""" + + return print_text(message, cut=True) def test_print() -> bool: diff --git a/backend/templates/admin.html b/backend/templates/admin.html index 1a51e7f..faba9da 100644 --- a/backend/templates/admin.html +++ b/backend/templates/admin.html @@ -20,6 +20,41 @@ -webkit-font-smoothing: antialiased; } + /* 토스트 알림 */ + .toast-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 10000; + display: flex; + flex-direction: column; + gap: 10px; + } + .toast { + padding: 14px 20px; + border-radius: 10px; + color: white; + font-weight: 500; + font-size: 14px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + animation: toastIn 0.3s ease, toastOut 0.3s ease 2.7s forwards; + display: flex; + align-items: center; + gap: 10px; + } + .toast.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); } + .toast.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); } + .toast.info { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); } + .toast.printing { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); } + @keyframes toastIn { + from { opacity: 0; transform: translateX(100px); } + to { opacity: 1; transform: translateX(0); } + } + @keyframes toastOut { + from { opacity: 1; transform: translateX(0); } + to { opacity: 0; transform: translateX(100px); } + } + .header { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); padding: 32px 24px; @@ -944,31 +979,53 @@ if (editBtn) editBtn.style.display = 'inline-block'; } - // 특이사항 인쇄 - async function printCusetc(customerName, cusetc, phone) { - if (!confirm(`${customerName}님의 특이사항을 인쇄하시겠습니까?`)) { - return; + // 토스트 알림 함수 + function showToast(message, type = 'info') { + let container = document.querySelector('.toast-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'toast-container'; + document.body.appendChild(container); } + const toast = document.createElement('div'); + toast.className = `toast ${type}`; + + const icons = { success: '✅', error: '❌', info: 'ℹ️', printing: '🖨️' }; + toast.innerHTML = `${icons[type] || ''}${message}`; + + container.appendChild(toast); + + setTimeout(() => toast.remove(), 3000); + } + + // 인쇄용 전역 변수 + let printData = { name: '', cusetc: '', phone: '' }; + + // 특이사항 인쇄 실행 + async function doPrintCusetc() { + // 즉시 피드백 + showToast(`${printData.name}님 특이사항 인쇄 중...`, 'printing'); + try { const res = await fetch('/api/print/cusetc', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - customer_name: customerName, - cusetc: cusetc, - phone: phone + customer_name: printData.name, + cusetc: printData.cusetc, + phone: printData.phone }) }); const data = await res.json(); if (data.success) { - alert('🖨️ ' + data.message); + showToast(data.message, 'success'); } else { - alert('❌ 인쇄 실패: ' + (data.error || '알 수 없는 오류')); + showToast('인쇄 실패: ' + (data.error || '알 수 없는 오류'), 'error'); } } catch (err) { - alert('❌ 오류: ' + err.message); + showToast('오류: ' + err.message, 'error'); } } @@ -1015,7 +1072,7 @@