AyulaPOS/views/transaction/cart-functionality.js

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, ".");
}