BAKMIE MERKAH

Loading sistem kasir...

Bakmie Merkah brand logo BAKMIE MERKAH

Pilih menu untuk ditambahkan ke pesanan

Online

Kasir Bertugas:

-
`); printWindow.document.close(); }; const startSession = (name) => { if (!name || name.trim() === '') { document.getElementById('cashier-error').textContent = 'Nama kasir tidak boleh kosong'; return; } currentCashier = name.trim(); currentCashierDisplay.textContent = currentCashier; cashierSelectionModal.classList.add('hidden'); mainAppContainer.classList.remove('disabled'); showNotification(`Selamat datang, ${currentCashier}!`, 'success'); }; // --- EVENT LISTENERS --- // Search and Filter productSearchInput.addEventListener('input', (e) => { searchQuery = e.target.value; renderProducts(); }); document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.filter-btn').forEach(b => { b.classList.remove('active', 'bg-red-500', 'text-white'); b.classList.add('bg-slate-700', 'hover:bg-slate-600', 'text-slate-300'); }); e.target.classList.remove('bg-slate-700', 'hover:bg-slate-600', 'text-slate-300'); e.target.classList.add('active', 'bg-red-500', 'text-white'); currentFilter = e.target.dataset.filter; renderProducts(); }); }); // Cart interactions cartItemsEl.addEventListener('click', (e) => { const target = e.target.closest('button'); if (!target) return; const productId = parseInt(target.dataset.id); if (target.classList.contains('quantity-change')) { updateQuantity(productId, parseInt(target.dataset.change)); } else if (target.classList.contains('remove-item')) { removeFromCart(productId); } }); // Payment modal interactions payButton.addEventListener('click', showPaymentModal); resetButton.addEventListener('click', resetCart); cancelPaymentButton.addEventListener('click', () => paymentModal.classList.add('hidden')); cashPaidInput.addEventListener('input', calculateChange); cashPaidInput.addEventListener('keyup', (e) => { if (e.key === 'Enter' && !confirmPaymentButton.disabled) { confirmPaymentButton.click(); } }); // Quick amount buttons document.querySelectorAll('.quick-amount').forEach(btn => { btn.addEventListener('click', () => { cashPaidInput.value = btn.dataset.amount; calculateChange(); }); }); document.getElementById('exact-amount').addEventListener('click', () => { const total = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0); cashPaidInput.value = total; calculateChange(); }); confirmPaymentButton.addEventListener('click', processAndShowPreview); // Print preview interactions closePreviewButton.addEventListener('click', () => { printPreviewModal.classList.add('hidden'); }); confirmPrintButton.addEventListener('click', () => { const receiptHTML = previewReceiptContentEl.innerHTML; if (activePrinter) { printViaEthernet(receiptHTML, activePrinter); } else { showNotification('Tidak ada printer aktif yang dipilih. Silakan pilih printer terlebih dahulu.', 'error'); renderAvailablePrinters(); selectPrinterModal.classList.remove('hidden'); } printPreviewModal.classList.add('hidden'); }); // History modal interactions historyButton.addEventListener('click', () => { renderHistory(); historyModal.classList.remove('hidden'); }); closeHistoryButton.addEventListener('click', () => historyModal.classList.add('hidden')); // History filters document.getElementById('history-date-filter').addEventListener('change', renderHistory); document.getElementById('history-cashier-filter').addEventListener('change', renderHistory); document.getElementById('clear-history-filter').addEventListener('click', () => { document.getElementById('history-date-filter').value = ''; document.getElementById('history-cashier-filter').value = ''; renderHistory(); }); // Export history document.getElementById('export-history').addEventListener('click', () => { const csvContent = [ ['ID Transaksi', 'Tanggal', 'Kasir', 'Pelanggan', 'Items', 'Total', 'Tunai', 'Kembali'].join(','), ...salesHistory.map(tx => [ tx.id, new Date(tx.date).toLocaleString('id-ID'), tx.cashier || 'N/A', tx.customerName || 'N/A', tx.items.map(item => `${item.name} (${item.quantity})`).join('; '), tx.total, tx.cashPaid || 0, tx.change || 0 ].join(',')) ].join('\n'); const blob = new Blob([csvContent], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `bakmie-merkah-history-${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); showNotification('Data riwayat berhasil diekspor', 'success'); }); historyContentEl.addEventListener('click', (e) => { const deleteButton = e.target.closest('.delete-transaction-button'); if (deleteButton) { adminAction = 'deleteOne'; transactionIdToDelete = deleteButton.dataset.id; adminModalPromptEl.textContent = `Masukkan kata sandi untuk menghapus transaksi #${transactionIdToDelete}.`; adminPasswordInput.value = ''; passwordErrorEl.textContent = ''; adminModal.classList.remove('hidden'); adminPasswordInput.focus(); } }); clearHistoryTriggerButton.addEventListener('click', () => { adminAction = 'deleteAll'; transactionIdToDelete = null; adminModalPromptEl.textContent = 'Masukkan kata sandi untuk menghapus seluruh riwayat penjualan.'; adminPasswordInput.value = ''; passwordErrorEl.textContent = ''; adminModal.classList.remove('hidden'); adminPasswordInput.focus(); }); // Admin modal interactions adminDashboardTrigger.addEventListener('click', () => { adminAction = 'openDashboard'; adminModalPromptEl.textContent = 'Masukkan kata sandi untuk mengakses dasbor admin.'; adminPasswordInput.value = ''; passwordErrorEl.textContent = ''; adminModal.classList.remove('hidden'); adminPasswordInput.focus(); }); cancelAdminActionButton.addEventListener('click', () => { adminModal.classList.add('hidden'); adminAction = null; transactionIdToDelete = null; }); confirmAdminActionButton.addEventListener('click', () => { if (adminPasswordInput.value === ADMIN_PASSWORD) { switch(adminAction) { case 'deleteOne': deleteTransaction(transactionIdToDelete); break; case 'deleteAll': salesHistory = []; saveHistoryToLocalStorage(); showNotification('Semua riwayat penjualan berhasil dihapus', 'success'); renderHistory(); break; case 'openDashboard': renderAdminDashboard(); adminDashboardModal.classList.remove('hidden'); break; } adminModal.classList.add('hidden'); adminAction = null; transactionIdToDelete = null; } else { passwordErrorEl.textContent = 'Kata sandi salah!'; adminPasswordInput.value = ''; adminPasswordInput.focus(); } }); adminPasswordInput.addEventListener('keyup', (e) => { passwordErrorEl.textContent = ''; if (e.key === 'Enter') confirmAdminActionButton.click(); }); closeAdminDashboardButton.addEventListener('click', () => adminDashboardModal.classList.add('hidden')); // Cashier session management cashierNameInput.addEventListener('input', (e) => { const name = e.target.value.trim(); startSessionButton.disabled = name === ''; document.getElementById('cashier-error').textContent = ''; }); cashierNameInput.addEventListener('keyup', (e) => { if (e.key === 'Enter' && !startSessionButton.disabled) { startSessionButton.click(); } }); startSessionButton.addEventListener('click', () => { const name = cashierNameInput.value.trim(); if (name) startSession(name); }); changeCashierButton.addEventListener('click', () => { mainAppContainer.classList.add('disabled'); cashierSelectionModal.classList.remove('hidden'); cashierNameInput.value = ''; cashierNameInput.focus(); }); // Printer management addPrinterButton.addEventListener('click', () => { printerNameInput.value = ''; printerIpInput.value = ''; printerPortInput.value = '9100'; printerDescriptionInput.value = ''; addPrinterModal.classList.remove('hidden'); printerNameInput.focus(); }); cancelAddPrinterButton.addEventListener('click', () => { addPrinterModal.classList.add('hidden'); }); savePrinterConfigButton.addEventListener('click', () => { const name = printerNameInput.value.trim(); const ip = printerIpInput.value.trim(); const port = parseInt(printerPortInput.value); const description = printerDescriptionInput.value.trim(); if (!name || !ip || isNaN(port) || port <= 0) { showNotification('Harap isi semua kolom dengan benar.', 'error'); return; } if (!validateIP(ip)) { showNotification('Format alamat IP tidak valid. Contoh: 192.168.1.100', 'error'); return; } if (port < 1 || port > 65535) { showNotification('Port harus antara 1 dan 65535.', 'error'); return; } // Check for duplicate name or IP const duplicate = registeredPrinters.find(p => p.name === name || (p.ip === ip && p.port === port)); if (duplicate) { showNotification('Printer dengan nama atau alamat IP:Port yang sama sudah ada.', 'error'); return; } const newPrinter = { id: Date.now(), name, ip, port, description, dateAdded: new Date().toISOString() }; registeredPrinters.push(newPrinter); savePrintersToLocalStorage(); renderRegisteredPrinters(); addPrinterModal.classList.add('hidden'); showNotification(`Printer "${name}" berhasil ditambahkan!`, 'success'); }); registeredPrintersListEl.addEventListener('click', (e) => { const deleteButton = e.target.closest('.delete-printer-button'); const setActiveButton = e.target.closest('.set-active-printer-button'); const testButton = e.target.closest('.test-printer-button'); if (deleteButton) { const printerId = parseInt(deleteButton.dataset.id); const printer = registeredPrinters.find(p => p.id === printerId); if (confirm(`Apakah Anda yakin ingin menghapus printer "${printer.name}"?`)) { registeredPrinters = registeredPrinters.filter(p => p.id !== printerId); if (activePrinter && activePrinter.id === printerId) { activePrinter = null; } savePrintersToLocalStorage(); renderRegisteredPrinters(); showNotification(`Printer "${printer.name}" berhasil dihapus.`, 'success'); } } else if (setActiveButton) { const printerId = parseInt(setActiveButton.dataset.id); activePrinter = registeredPrinters.find(p => p.id === printerId); savePrintersToLocalStorage(); renderRegisteredPrinters(); showNotification(`Printer "${activePrinter.name}" sekarang aktif.`, 'success'); } else if (testButton) { const printerId = parseInt(testButton.dataset.id); const printer = registeredPrinters.find(p => p.id === printerId); // Simulate test (in real implementation, you'd ping the printer) showNotification(`Test koneksi ke ${printer.name} (${printer.ip}:${printer.port}) - Simulasi berhasil`, 'success'); } }); cancelSelectPrinterButton.addEventListener('click', () => { selectPrinterModal.classList.add('hidden'); paymentModal.classList.remove('hidden'); }); // Keyboard shortcuts document.addEventListener('keydown', (e) => { if (e.ctrlKey || e.metaKey) { switch(e.key) { case 'f': e.preventDefault(); productSearchInput.focus(); break; case 'n': e.preventDefault(); if (cart.length > 0) payButton.click(); break; case 'r': e.preventDefault(); if (cart.length > 0) resetButton.click(); break; } } if (e.key === 'Escape') { // Close any open modal document.querySelectorAll('[id$="-modal"]').forEach(modal => { if (!modal.classList.contains('hidden')) { modal.classList.add('hidden'); } }); } }); // Connection status simulation const updateConnectionStatus = () => { const statusIndicator = document.getElementById('status-indicator'); const statusText = document.getElementById('status-text'); const isOnline = navigator.onLine; if (isOnline) { statusIndicator.className = 'w-3 h-3 bg-green-500 rounded-full'; statusText.textContent = 'Online'; } else { statusIndicator.className = 'w-3 h-3 bg-red-500 rounded-full'; statusText.textContent = 'Offline'; } }; window.addEventListener('online', updateConnectionStatus); window.addEventListener('offline', updateConnectionStatus); // --- INITIALIZATION --- const init = () => { mainAppContainer.classList.add('disabled'); loadHistoryFromLocalStorage(); loadPrintersFromLocalStorage(); renderProducts(); renderCart(); updateConnectionStatus(); // Show version in console console.log(`%cBakmie Merkah POS System v${APP_VERSION}`, 'color: #ef4444; font-size: 16px; font-weight: bold;'); console.log('Sistem kasir digital dengan fitur printer Ethernet dan pelaporan real-time'); }; init(); });