375 lines
12 KiB
JavaScript
375 lines
12 KiB
JavaScript
/**
|
|
* Cart Functionality
|
|
* Handle cart interactions in the POS system
|
|
*/
|
|
|
|
$(document).ready(function() {
|
|
// Setup quantity controls
|
|
setupQuantityControls();
|
|
|
|
// Setup cart item deletion confirmation
|
|
setupCartDeletionConfirmation();
|
|
|
|
// Setup cash input handling
|
|
setupCashInput();
|
|
|
|
// Setup quick cash buttons
|
|
setupQuickCashButtons();
|
|
|
|
// Setup checkout form
|
|
setupCheckoutForm();
|
|
|
|
// Initialize cart scrolling
|
|
initializeCartScrolling();
|
|
|
|
// Handle multi-select functionality
|
|
setupMultiSelectMode();
|
|
|
|
// Handle window resize to adjust cart height
|
|
$(window).on('resize', function() {
|
|
initializeCartScrolling();
|
|
});
|
|
|
|
// Handle form submission with enter key
|
|
setupQuantityFormSubmission();
|
|
});
|
|
|
|
/**
|
|
* Setup quantity controls for increment and decrement
|
|
* This function handles the +/- buttons for item quantities
|
|
*/
|
|
function setupQuantityControls() {
|
|
// Remove any existing event handlers to prevent duplicates
|
|
$('.quantity-btn.increment, .quantity-btn.decrement').off('click');
|
|
|
|
// Setup increment button
|
|
$('.quantity-btn.increment').on('click', function() {
|
|
const quantityField = $(this).siblings('.quantity-field');
|
|
const currentValue = parseInt(quantityField.val()) || 0;
|
|
const maxStock = parseInt(quantityField.data('max-stock')) || 99;
|
|
|
|
// Only increment if below max stock
|
|
if (currentValue < maxStock) {
|
|
quantityField.val(currentValue + 1);
|
|
}
|
|
});
|
|
|
|
// Setup decrement button
|
|
$('.quantity-btn.decrement').on('click', function() {
|
|
const quantityField = $(this).siblings('.quantity-field');
|
|
const currentValue = parseInt(quantityField.val()) || 0;
|
|
|
|
// Only decrement if above 1
|
|
if (currentValue > 1) {
|
|
quantityField.val(currentValue - 1);
|
|
}
|
|
});
|
|
|
|
// Handle manual input validation
|
|
$('.quantity-field').on('change', function() {
|
|
let value = parseInt($(this).val()) || 0;
|
|
const maxStock = parseInt($(this).data('max-stock')) || 99;
|
|
|
|
// Ensure value is between 1 and max stock
|
|
if (value < 1) value = 1;
|
|
if (value > maxStock) value = maxStock;
|
|
|
|
$(this).val(value);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup cart item deletion confirmation
|
|
* This function handles the delete button clicks and confirmation modal
|
|
*/
|
|
function setupCartDeletionConfirmation() {
|
|
// Remove any existing event handlers to prevent duplicates
|
|
$('.delete-cart-item').off('click');
|
|
|
|
// Setup delete button click event
|
|
$('.delete-cart-item').on('click', function(e) {
|
|
e.preventDefault();
|
|
|
|
const itemIndex = $(this).data('index');
|
|
|
|
// Set the confirmation button's href
|
|
$('#confirm-delete-btn').attr('href', 'index.php?remove_item=' + itemIndex);
|
|
|
|
// Show the confirmation modal
|
|
if (typeof bootstrap !== 'undefined' && $('#deleteConfirmModal').length) {
|
|
const deleteModal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
|
|
deleteModal.show();
|
|
} else {
|
|
// Fallback if Bootstrap modal isn't available
|
|
if (confirm('Apakah Anda yakin ingin menghapus item ini dari keranjang Anda?')) {
|
|
window.location.href = 'index.php?remove_item=' + itemIndex;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Setup confirm delete button
|
|
$('#confirm-delete-btn').on('click', function() {
|
|
// Button href is already set above
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup quantity form submission on enter key
|
|
*/
|
|
function setupQuantityFormSubmission() {
|
|
$('.quantity-field').on('keydown', function(e) {
|
|
if (e.keyCode === 13) {
|
|
e.preventDefault();
|
|
$(this).closest('form').submit();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup cash input handling with number formatting
|
|
*/
|
|
function setupCashInput() {
|
|
const cashInput = $('#cash-amount');
|
|
const hiddenCashAmount = $('#hidden-cash-amount');
|
|
|
|
// Initialize change amount display
|
|
updateChangeDisplay();
|
|
|
|
// Handle cash input change
|
|
cashInput.on('input', function() {
|
|
// Remove non-numeric characters except decimal point
|
|
let value = $(this).val().replace(/[^\d]/g, '');
|
|
|
|
// Parse to integer
|
|
const numericValue = parseInt(value) || 0;
|
|
|
|
// Format with thousand separator
|
|
$(this).val(formatNumber(numericValue));
|
|
|
|
// Update hidden field with numeric value
|
|
hiddenCashAmount.val(numericValue);
|
|
|
|
// Update change display
|
|
updateChangeDisplay();
|
|
});
|
|
|
|
// Make sure initial value is formatted
|
|
const initialValue = parseInt(cashInput.val().replace(/[^\d]/g, '')) || 0;
|
|
cashInput.val(formatNumber(initialValue));
|
|
hiddenCashAmount.val(initialValue);
|
|
}
|
|
|
|
/**
|
|
* Update change display based on current cash amount and total
|
|
*/
|
|
function updateChangeDisplay() {
|
|
const cashInput = $('#cash-amount');
|
|
const changeDisplay = $('#change-amount');
|
|
const changeContainer = $('#change-container');
|
|
const hiddenCashAmount = $('#hidden-cash-amount');
|
|
const hiddenChangeAmount = $('#hidden-change-amount');
|
|
|
|
// Get total from the displayed value or use 0 if not available
|
|
const totalText = $('.total-value h6').text();
|
|
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
|
|
|
|
// Get cash value from hidden field or parse from display
|
|
let cashValue = parseFloat(hiddenCashAmount.val()) || 0;
|
|
if (cashValue === 0) {
|
|
// Try to parse from display as fallback
|
|
cashValue = parseFloat(cashInput.val().replace(/[^\d.,]/g, '').replace(/\./g, '').replace(',', '.')) || 0;
|
|
hiddenCashAmount.val(cashValue);
|
|
}
|
|
|
|
// Calculate change
|
|
const change = cashValue - totalAmount;
|
|
|
|
// Update hidden field for change amount
|
|
hiddenChangeAmount.val(Math.max(0, change));
|
|
|
|
// Format and display
|
|
if (change >= 0 && totalAmount > 0) {
|
|
changeDisplay.text('Rp. ' + formatNumber(change));
|
|
changeContainer.show();
|
|
} else {
|
|
changeContainer.hide();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup quick cash buttons
|
|
*/
|
|
function setupQuickCashButtons() {
|
|
$('.quick-cash').on('click', function() {
|
|
const value = $(this).data('value');
|
|
$('#cash-amount').val(formatNumber(value));
|
|
$('#hidden-cash-amount').val(value);
|
|
updateChangeDisplay();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup checkout form validation
|
|
*/
|
|
function setupCheckoutForm() {
|
|
$('#checkout-form').on('submit', function(e) {
|
|
const cashAmount = parseFloat($('#hidden-cash-amount').val()) || 0;
|
|
const totalText = $('.total-value h6').text();
|
|
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
|
|
|
|
if (cashAmount < totalAmount) {
|
|
e.preventDefault();
|
|
alert('Jumlah tunai harus sama dengan atau lebih besar dari jumlah total.');
|
|
return false;
|
|
}
|
|
|
|
// Disable button to prevent double submission
|
|
$(this).find('button[type="submit"]').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Memproses...');
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize scrolling behavior for cart
|
|
*/
|
|
function initializeCartScrolling() {
|
|
// Make sure product table has correct max height
|
|
const windowHeight = $(window).height();
|
|
const headerHeight = $('.order-list').outerHeight() || 0;
|
|
const cardHeaderHeight = $('.card-order .card-body:first-of-type').outerHeight() || 0;
|
|
const cardFooterHeight = $('.card-order .card-body:last-of-type').outerHeight() || 0;
|
|
const availableHeight = windowHeight - headerHeight - cardHeaderHeight - cardFooterHeight - 120; // 120px buffer
|
|
|
|
// Set max height for scrollable area
|
|
$('.product-table').css('max-height', availableHeight + 'px');
|
|
}
|
|
|
|
/**
|
|
* Setup multi-select mode for products
|
|
*/
|
|
function setupMultiSelectMode() {
|
|
let isMultiSelectMode = false;
|
|
let selectedProducts = [];
|
|
|
|
// Toggle multi-select mode
|
|
$('.productset').on('long-press', function() {
|
|
if (!isMultiSelectMode) {
|
|
enterMultiSelectMode();
|
|
// Select the long-pressed product
|
|
$(this).addClass('selected');
|
|
const productId = $(this).data('product-id');
|
|
selectedProducts.push(productId);
|
|
updateSelectedCount();
|
|
}
|
|
});
|
|
|
|
// Simulate long press on desktop
|
|
let pressTimer;
|
|
$('.productset').on('mousedown', function() {
|
|
const $this = $(this);
|
|
pressTimer = setTimeout(function() {
|
|
if (!isMultiSelectMode) {
|
|
enterMultiSelectMode();
|
|
// Select the long-pressed product
|
|
$this.addClass('selected');
|
|
const productId = $this.data('product-id');
|
|
selectedProducts.push(productId);
|
|
updateSelectedCount();
|
|
}
|
|
}, 800); // Long press threshold of 800ms
|
|
}).on('mouseup mouseleave', function() {
|
|
clearTimeout(pressTimer);
|
|
});
|
|
|
|
// Handle product click in multi-select mode
|
|
$('.productset').on('click', function(e) {
|
|
if (isMultiSelectMode) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
const $this = $(this);
|
|
const productId = $this.data('product-id');
|
|
|
|
if ($this.hasClass('selected')) {
|
|
// Deselect
|
|
$this.removeClass('selected');
|
|
selectedProducts = selectedProducts.filter(id => id !== productId);
|
|
} else {
|
|
// Select
|
|
$this.addClass('selected');
|
|
selectedProducts.push(productId);
|
|
}
|
|
|
|
updateSelectedCount();
|
|
}
|
|
});
|
|
|
|
// Cancel selection
|
|
$('#cancel-selection').on('click', function() {
|
|
exitMultiSelectMode();
|
|
});
|
|
|
|
// Add selected products to cart
|
|
$('#add-selected-to-cart').on('click', function() {
|
|
if (selectedProducts.length > 0) {
|
|
// Show loading state
|
|
$(this).prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Menambahkan...');
|
|
|
|
// Submit selected products
|
|
$.ajax({
|
|
url: 'index.php',
|
|
type: 'POST',
|
|
data: {
|
|
product_ids: selectedProducts
|
|
},
|
|
success: function() {
|
|
window.location.reload();
|
|
},
|
|
error: function() {
|
|
alert('Gagal menambahkan produk ke keranjang. Silakan coba lagi.');
|
|
$('#add-selected-to-cart').prop('disabled', false).text('Tambahkan ke keranjang');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Functions to enter/exit multi-select mode
|
|
function enterMultiSelectMode() {
|
|
isMultiSelectMode = true;
|
|
selectedProducts = [];
|
|
$('.multi-select-toolbar').addClass('active');
|
|
$('.selected-count').text('0 produk terseleksi');
|
|
|
|
// Disable normal click actions on products
|
|
$('.add-single-form button[type="submit"]').prop('disabled', true);
|
|
}
|
|
|
|
function exitMultiSelectMode() {
|
|
isMultiSelectMode = false;
|
|
selectedProducts = [];
|
|
$('.multi-select-toolbar').removeClass('active');
|
|
$('.productset').removeClass('selected');
|
|
|
|
// Re-enable normal click actions
|
|
$('.add-single-form button[type="submit"]').prop('disabled', false);
|
|
}
|
|
|
|
function updateSelectedCount() {
|
|
$('.selected-count').text(selectedProducts.length + ' produk terseleksi');
|
|
|
|
// Only enable add button if products are selected
|
|
if (selectedProducts.length > 0) {
|
|
$('#add-selected-to-cart').prop('disabled', false);
|
|
} else {
|
|
$('#add-selected-to-cart').prop('disabled', true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format number with thousand separator
|
|
*/
|
|
function formatNumber(number) {
|
|
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
|
} |